mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
Merge branch 'master' into topic/gregor/smb-nfs
Conflicts: policy/bro.init policy/nfs.bro policy/portmapper.bro src/Analyzer.cc src/AnalyzerTags.h src/Gnutella.cc src/NFS.cc src/NFS.h src/Portmap.cc src/Portmap.h src/RPC.cc src/RPC.h src/SSLProxy.h src/TCP_Reassembler.cc src/XDR.cc src/bro.bif src/const.bif src/event.bif src/scan.l src/types.bif
This commit is contained in:
commit
9f7d4980ea
200 changed files with 3527 additions and 10279 deletions
72
Makefile
72
Makefile
|
@ -5,15 +5,12 @@
|
|||
# to offer. For more, execute that one directly.
|
||||
#
|
||||
|
||||
BUILD=build
|
||||
BROCCOLI=aux/broccoli
|
||||
BROCTL=aux/broctl
|
||||
|
||||
# CMake/CPack versions before 2.8.2 have bugs that can create bad packages
|
||||
CMAKE_PACK_REQ=2.8.2
|
||||
CMAKE_VER=`cmake -version`
|
||||
|
||||
OSX_VER_CMD=sw_vers | sed -n 's/ProductVersion://p' | cut -d . -f 2
|
||||
SOURCE=$(PWD)
|
||||
BUILD=$(SOURCE)/build
|
||||
TMP=/tmp/bro-dist.$(UID)
|
||||
BRO_V=`cat $(SOURCE)/VERSION`
|
||||
BROCCOLI_V=`cat $(SOURCE)/aux/broccoli/VERSION`
|
||||
BROCTL_V=`cat $(SOURCE)/aux/broctl/VERSION`
|
||||
|
||||
all: configured
|
||||
( cd $(BUILD) && make )
|
||||
|
@ -31,39 +28,25 @@ doc: configured
|
|||
docclean: configured
|
||||
( cd $(BUILD) && make docclean && make restclean )
|
||||
|
||||
dist: cmake_version
|
||||
# Minimum Bro source package
|
||||
( \
|
||||
./configure --ignore-dirs='aux/broctl;aux/broccoli' --pkg-name-prefix=Bro && \
|
||||
cd $(BUILD) && \
|
||||
make package_source \
|
||||
)
|
||||
# Full Bro source package
|
||||
( \
|
||||
./configure --pkg-name-prefix=Bro-all && \
|
||||
cd $(BUILD) && \
|
||||
make package_source \
|
||||
)
|
||||
# Broccoli source package
|
||||
( \
|
||||
cd $(BROCCOLI) && \
|
||||
./configure && \
|
||||
cd $(BUILD) && \
|
||||
make package_source && \
|
||||
mv Broccoli*.tar.gz ../../../$(BUILD)/ && \
|
||||
cd .. && \
|
||||
rm -r $(BUILD) \
|
||||
)
|
||||
# Broctl source package
|
||||
( \
|
||||
cd $(BROCTL) && \
|
||||
./configure && \
|
||||
cd $(BUILD) && \
|
||||
make package_source && \
|
||||
mv Broctl*.tar.gz ../../../$(BUILD)/ && \
|
||||
cd .. && \
|
||||
rm -r $(BUILD) \
|
||||
)
|
||||
dist:
|
||||
@( mkdir -p $(BUILD) && rm -rf $(TMP) && mkdir $(TMP) )
|
||||
@cp -R $(SOURCE) $(TMP)/Bro-$(BRO_V)
|
||||
@( cd $(TMP) && find . -name .git\* | xargs rm -rf )
|
||||
@( cd $(TMP) && find . -name \*.swp | xargs rm -rf )
|
||||
@( cd $(TMP) && find . -type d -name build | xargs rm -rf )
|
||||
@( cd $(TMP) && tar -czf $(BUILD)/Bro-all-$(BRO_V).tar.gz Bro-$(BRO_V) )
|
||||
@( cd $(TMP)/Bro-$(BRO_V)/aux && mv broccoli Broccoli-$(BROCCOLI_V) && \
|
||||
tar -czf $(BUILD)/Broccoli-$(BROCCOLI_V).tar.gz Broccoli-$(BROCCOLI_V) )
|
||||
@( cd $(TMP)/Bro-$(BRO_V)/aux && mv broctl Broctl-$(BROCTL_V) && \
|
||||
tar -czf $(BUILD)/Broctl-$(BROCTL_V).tar.gz Broctl-$(BROCTL_V) )
|
||||
@( cd $(TMP)/Bro-$(BRO_V)/aux && rm -rf Broctl* Broccoli* )
|
||||
@( cd $(TMP) && tar -czf $(BUILD)/Bro-$(BRO_V).tar.gz Bro-$(BRO_V) )
|
||||
@rm -rf $(TMP)
|
||||
@echo "Distribution source tarballs have been compiled in $(BUILD)"
|
||||
|
||||
bindist:
|
||||
@( cd pkg && ( ./make-deb-packages || ./make-mac-packages || \
|
||||
./make-rpm-packages ) )
|
||||
|
||||
distclean:
|
||||
rm -rf $(BUILD)
|
||||
|
@ -72,7 +55,4 @@ configured:
|
|||
@test -d $(BUILD) || ( echo "Error: No build/ directory found. Did you run configure?" && exit 1 )
|
||||
@test -e $(BUILD)/Makefile || ( echo "Error: No build/Makefile found. Did you run configure?" && exit 1 )
|
||||
|
||||
cmake_version:
|
||||
@test "$(CMAKE_VER)" \> "cmake version $(CMAKE_PACK_REQ)" || ( echo "Error: please use a CMake version greater than $(CMAKE_PACK_REQ)" && exit 1 )
|
||||
|
||||
.PHONY : all install clean distclean configured cmake_version
|
||||
.PHONY : all install clean doc docclean dist bindist distclean configured
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 14a7cfe4ea2ff6c7f5301dcb81a869adcd6e9834
|
||||
Subproject commit 1a610bced1c83644a5bfaeb6e98cf75380fe61a1
|
|
@ -1 +1 @@
|
|||
Subproject commit 7c20b1a41016471eb03ee7a0fbfe1fb34bdc084a
|
||||
Subproject commit 8843da57dc8aee433550727dcbd1199824ca9da4
|
|
@ -1 +1 @@
|
|||
Subproject commit c4eaf7c7471ab04ae8af0f2913cb8350d9ae0b3a
|
||||
Subproject commit ad9528f6795f104db8ec2f1425fc0b69d77ab92d
|
|
@ -1 +1 @@
|
|||
Subproject commit d0154a7e88cd1b6bccc11c042e451fcb9b5459bf
|
||||
Subproject commit a9aeb2e1a8434c583c75f5941b58dc69a7517444
|
|
@ -63,10 +63,7 @@ endmacro(SetPackageVersion)
|
|||
#
|
||||
# Darwin - PackageMaker
|
||||
# Linux - RPM if the platform has rpmbuild installed
|
||||
# DEB is ommitted because CPack does not give enough
|
||||
# control over how the package is created and lacks support
|
||||
# for automatic dependency detection.
|
||||
#
|
||||
# DEB if the platform has dpkg-shlibdeps installed
|
||||
#
|
||||
# CPACK_GENERATOR is set by this macro
|
||||
# CPACK_SOURCE_GENERATOR is set by this macro
|
||||
|
@ -77,9 +74,14 @@ macro(SetPackageGenerators)
|
|||
list(APPEND CPACK_GENERATOR PackageMaker)
|
||||
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
find_program(RPMBUILD_EXE rpmbuild)
|
||||
find_program(DPKGSHLIB_EXE dpkg-shlibdeps)
|
||||
if (RPMBUILD_EXE)
|
||||
set(CPACK_GENERATOR ${CPACK_GENERATOR} RPM)
|
||||
endif ()
|
||||
if (DPKGSHLIB_EXE)
|
||||
set(CPACK_GENERATOR ${CPACK_GENERATOR} DEB)
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS true)
|
||||
endif ()
|
||||
endif ()
|
||||
endmacro(SetPackageGenerators)
|
||||
|
||||
|
@ -159,11 +161,27 @@ macro(SetPackageInstallScripts VERSION)
|
|||
endif ()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
# DEB packages can automatically handle configuration files
|
||||
# if provided in a "conffiles" file in the packaging
|
||||
set(conffiles_file ${CMAKE_CURRENT_BINARY_DIR}/conffiles)
|
||||
if (INSTALLED_CONFIG_FILES)
|
||||
string(REPLACE " " ";" conffiles ${INSTALLED_CONFIG_FILES})
|
||||
endif ()
|
||||
file(WRITE ${conffiles_file} "")
|
||||
foreach (_file ${conffiles})
|
||||
file(APPEND ${conffiles_file} "${_file}\n")
|
||||
endforeach ()
|
||||
|
||||
list(APPEND CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
|
||||
${CMAKE_CURRENT_BINARY_DIR}/conffiles)
|
||||
|
||||
# RPMs don't need any explicit direction regarding config files.
|
||||
|
||||
# Leaving the set of installed config files empty will just
|
||||
# bypass the logic in the pre/post install scripts and let
|
||||
# the RPM do their own thing (regarding backups, etc.)
|
||||
# bypass the logic in the default pre/post install scripts and let
|
||||
# the RPMs/DEBs do their own thing (regarding backups, etc.)
|
||||
# when upgrading packages.
|
||||
set (INSTALLED_CONFIG_FILES "")
|
||||
set(INSTALLED_CONFIG_FILES "")
|
||||
endif ()
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cmake/package_preinstall.sh.in)
|
||||
|
@ -171,10 +189,16 @@ macro(SetPackageInstallScripts VERSION)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/package_preinstall.sh.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/package_preinstall.sh
|
||||
@ONLY)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/package_preinstall.sh.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/preinst
|
||||
@ONLY)
|
||||
set(CPACK_PREFLIGHT_SCRIPT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/package_preinstall.sh)
|
||||
set(CPACK_RPM_PRE_INSTALL_SCRIPT_FILE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/package_preinstall.sh)
|
||||
list(APPEND CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
|
||||
${CMAKE_CURRENT_BINARY_DIR}/preinst)
|
||||
endif ()
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/cmake/package_postupgrade.sh.in)
|
||||
|
@ -182,10 +206,16 @@ macro(SetPackageInstallScripts VERSION)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/package_postupgrade.sh.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/package_postupgrade.sh
|
||||
@ONLY)
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/package_postupgrade.sh.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/postinst
|
||||
@ONLY)
|
||||
set(CPACK_POSTUPGRADE_SCRIPT
|
||||
${CMAKE_CURRENT_BINARY_DIR}/package_postupgrade.sh)
|
||||
set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/package_postupgrade.sh)
|
||||
list(APPEND CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA
|
||||
${CMAKE_CURRENT_BINARY_DIR}/postinst)
|
||||
endif ()
|
||||
endmacro(SetPackageInstallScripts)
|
||||
|
||||
|
|
|
@ -48,21 +48,13 @@ if [ -n "${sampleFiles}" ]; then
|
|||
EOF
|
||||
fi
|
||||
|
||||
# make sure that world-writeable dirs have the sticky bit set
|
||||
# so that unprivileged can't rename/remove files within
|
||||
|
||||
if [ -d /var/opt/bro/spool ]; then
|
||||
chmod +t /var/opt/bro/spool
|
||||
fi
|
||||
|
||||
if [ -d /var/opt/bro/spool/tmp ]; then
|
||||
chmod +t /var/opt/bro/spool/tmp
|
||||
fi
|
||||
|
||||
if [ -d /var/opt/bro/spool/policy ]; then
|
||||
chmod +t /var/opt/bro/spool/policy
|
||||
fi
|
||||
|
||||
if [ -d /var/opt/bro/logs ]; then
|
||||
chmod +t /var/opt/bro/logs
|
||||
# Set up world writeable spool and logs directory for broctl, making sure
|
||||
# to set the sticky bit so that unprivileged users can't rename/remove files.
|
||||
# (CMake/CPack is supposed to install them, but has problems with empty dirs)
|
||||
if [ -n "@EMPTY_WORLD_DIRS@" ]; then
|
||||
for dir in "@EMPTY_WORLD_DIRS@"; do
|
||||
mkdir -p ${dir}
|
||||
chmod 777 ${dir}
|
||||
chmod +t ${dir}
|
||||
done
|
||||
fi
|
||||
|
|
14
pkg/check-cmake
Executable file
14
pkg/check-cmake
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
# CMake/CPack versions before 2.8.3 have bugs that can create bad packages
|
||||
# Since packages will be built on several different systems, a single
|
||||
# version of CMake is required to obtain consistency, but can be increased
|
||||
# as new versions of CMake come out that also produce working packages.
|
||||
|
||||
CMAKE_PACK_REQ="cmake version 2.8.4"
|
||||
CMAKE_VER=`cmake -version`
|
||||
|
||||
if [ "${CMAKE_VER}" != "${CMAKE_PACK_REQ}" ]; then
|
||||
echo "Package creation requires ${CMAKE_PACK_REQ}" >&2
|
||||
exit 1
|
||||
fi
|
47
pkg/make-deb-packages
Executable file
47
pkg/make-deb-packages
Executable file
|
@ -0,0 +1,47 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script generates binary DEB packages.
|
||||
# They can be found in ../build/ after running.
|
||||
|
||||
./check-cmake || { exit 1; }
|
||||
|
||||
# The DEB CPack generator depends on `dpkg-shlibdeps` to automatically
|
||||
# determine what dependencies to set for the packages
|
||||
type dpkg-shlibdeps > /dev/null 2>&1 || {
|
||||
echo "\
|
||||
Creating DEB packages requires the "dpkg-shlibs" command, usually provided by
|
||||
the 'dpkg-dev' package, please install it first.
|
||||
" >&2;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
prefix=/opt/bro
|
||||
|
||||
# During the packaging process, `dpkg-shlibs` will fail if used on a library
|
||||
# that links to other internal/project libraries unless an RPATH is used or
|
||||
# we set LD_LIBRARY_PATH such that it can find the internal/project library
|
||||
# in the temporary packaging tree.
|
||||
export LD_LIBRARY_PATH=./${prefix}/lib
|
||||
|
||||
cd ..
|
||||
|
||||
# Minimum Bro
|
||||
./configure --prefix=${prefix} --disable-broccoli --disable-broctl \
|
||||
--pkg-name-prefix=Bro --binary-package
|
||||
( cd build && make package )
|
||||
|
||||
# Full Bro package
|
||||
./configure --prefix=${prefix} --pkg-name-prefix=Bro-all --binary-package
|
||||
( cd build && make package )
|
||||
|
||||
# Broccoli
|
||||
cd aux/broccoli
|
||||
./configure --prefix=${prefix} --binary-package
|
||||
( cd build && make package && mv Broccoli*.deb ../../../build/ )
|
||||
cd ../..
|
||||
|
||||
# Broctl
|
||||
cd aux/broctl
|
||||
./configure --prefix=${prefix} --binary-package
|
||||
( cd build && make package && mv Broctl*.deb ../../../build/ )
|
||||
cd ../..
|
|
@ -1,18 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script creates binary packages for Mac OS X.
|
||||
# They can be found in build/ after running.
|
||||
# They can be found in ../build/ after running.
|
||||
|
||||
prefix=/opt/bro
|
||||
|
||||
# CMake/CPack versions before 2.8.2 have bugs that can create bad packages
|
||||
CMAKE_PACK_REQ=2.8.3
|
||||
CMAKE_VER=`cmake -version`
|
||||
|
||||
if [ "${CMAKE_VER}" \< "${CMAKE_PACK_REQ}" ]; then
|
||||
echo "Package creation requires CMake > 2.8.2" >&2
|
||||
exit 1
|
||||
fi
|
||||
./check-cmake || { exit 1; }
|
||||
|
||||
type sw_vers > /dev/null 2>&1 || {
|
||||
echo "Unable to get Mac OS X version" >&2;
|
||||
|
@ -38,6 +29,10 @@ else
|
|||
arch=x86_64
|
||||
fi
|
||||
|
||||
prefix=/opt/bro
|
||||
|
||||
cd ..
|
||||
|
||||
# Minimum Bro
|
||||
CMAKE_OSX_ARCHITECTURES=${arch} ./configure --prefix=${prefix} \
|
||||
--disable-broccoli --disable-broctl --pkg-name-prefix=Bro \
|
|
@ -1,18 +1,22 @@
|
|||
#!/bin/sh
|
||||
|
||||
# This script generates binary RPM packages.
|
||||
# They can be found in build/ after running.
|
||||
# They can be found in ../build/ after running.
|
||||
|
||||
./check-cmake || { exit 1; }
|
||||
|
||||
# The RPM CPack generator depends on `rpmbuild` to create packages
|
||||
type rpmbuild > /dev/null 2>&1 || {
|
||||
echo "\
|
||||
Creating RPM packages requires the "rpmbuild" command, usually provided by
|
||||
the 'rpm-build' package, please install it first.
|
||||
" >&2;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
prefix=/opt/bro
|
||||
|
||||
# CMake/CPack versions before 2.8.2 have bugs that can create bad packages
|
||||
CMAKE_PACK_REQ=2.8.2
|
||||
CMAKE_VER=`cmake -version`
|
||||
|
||||
if [ "${CMAKE_VER}" \< "${CMAKE_PACK_REQ}" ]; then
|
||||
echo "Package creation requires CMake > 2.8.2" >&2
|
||||
exit 1
|
||||
fi
|
||||
cd ..
|
||||
|
||||
# Minimum Bro
|
||||
./configure --prefix=${prefix} --disable-broccoli --disable-broctl \
|
181
policy/bro.init
181
policy/bro.init
|
@ -11,9 +11,12 @@ global no_handler: event(name: string, val: any);
|
|||
# Type declarations
|
||||
type string_array: table[count] of string;
|
||||
type string_set: set[string];
|
||||
type count_set: set[count];
|
||||
type index_vec: vector of count;
|
||||
type string_vec: vector of string;
|
||||
|
||||
type table_string_of_string: table[string] of string;
|
||||
|
||||
type transport_proto: enum { unknown_transport, tcp, udp, icmp };
|
||||
|
||||
type conn_id: record {
|
||||
|
@ -718,47 +721,43 @@ const RPC_status = {
|
|||
};
|
||||
|
||||
module NFS3;
|
||||
|
||||
export {
|
||||
# Should the read and write events return the file data that
|
||||
# has been read/written?
|
||||
# Should the read and write events return the file data that has been
|
||||
# read/written?
|
||||
const return_data = F &redef;
|
||||
|
||||
# If nfs_return_data==T: how much data should be returned at most
|
||||
# If nfs_return_data is true, how much data should be returned at most.
|
||||
const return_data_max = 512 &redef;
|
||||
|
||||
# If nfs_return_data==T: whether to *only* return data if the read or
|
||||
# write offset is 0, i.e., only return data for the beginning of the
|
||||
# file
|
||||
const return_data_first_only = T &redef;
|
||||
# This record summarizes the general results and status
|
||||
# of NFSv3 request/reply pairs. It's part of every NFSv3
|
||||
# event
|
||||
|
||||
# If nfs_return_data is true, whether to *only* return data if the read or write
|
||||
# offset is 0, i.e., only return data for the beginning of the file.
|
||||
const return_data_first_only = T &redef;
|
||||
|
||||
# This record summarizes the general results and status of NFSv3 request/reply
|
||||
# pairs. It's part of every NFSv3 event.
|
||||
type info_t: record {
|
||||
rpc_stat: rpc_status; # if this indicates not successful, the reply record in the
|
||||
# events will be a empty and contain uninitialized fields, so
|
||||
# don't use it!
|
||||
rpc_stat: rpc_status; # If this indicates not successful, the reply record in the
|
||||
# events will be empty and contain uninitialized fields, so
|
||||
# don't use it.
|
||||
nfs_stat: status_t;
|
||||
|
||||
# the start time, duration, and length in bytes of the
|
||||
# request (call).
|
||||
# NOTE that the start and end time might not be accurate. For TCP we
|
||||
# record the time when a chunk of data is delivered to the analyzer. Depending
|
||||
# on the Reassembler, this might be well after the first packet of the request
|
||||
# The start time, duration, and length in bytes of the request (call). Note that
|
||||
# the start and end time might not be accurate. For TCP, we record the
|
||||
# time when a chunk of data is delivered to the analyzer. Depending on the
|
||||
# Reassembler, this might be well after the first packet of the request
|
||||
# was received.
|
||||
req_start: time;
|
||||
req_dur: interval;
|
||||
req_len: count;
|
||||
|
||||
# Same for the reply
|
||||
# Same for the reply.
|
||||
rep_start: time;
|
||||
rep_dur: interval;
|
||||
rep_len: count;
|
||||
};
|
||||
|
||||
|
||||
# NFSv3 types
|
||||
# type names are based on RFC 1813
|
||||
# NFSv3 types. Type names are based on RFC 1813.
|
||||
type fattr_t: record {
|
||||
ftype: file_type_t;
|
||||
mode: count;
|
||||
|
@ -777,15 +776,15 @@ export {
|
|||
};
|
||||
|
||||
type diropargs_t : record {
|
||||
dirfh: string; # the file handle of the directory
|
||||
fname: string; # the name of the file we are interested in
|
||||
dirfh: string; # the file handle of the directory
|
||||
fname: string; # the name of the file we are interested in
|
||||
};
|
||||
|
||||
# Note, we don't need a post_op_attr type. We can always use
|
||||
# an fattr_t &optional
|
||||
# Note, we don't need a "post_op_attr" type. We use an "fattr_t &optional"
|
||||
# instead.
|
||||
|
||||
type lookup_reply_t: record {
|
||||
# If the lookup failed, dir_attr may be set.
|
||||
# If the lookup failed, dir_attr may be set.
|
||||
# If the lookup succeeded, fh is always set and obj_attr and dir_attr may be set.
|
||||
fh: string &optional; # file handle of object looked up
|
||||
obj_attr: fattr_t &optional; # optional attributes associated w/ file
|
||||
|
@ -793,33 +792,33 @@ export {
|
|||
};
|
||||
|
||||
type readargs_t: record {
|
||||
fh: string; # file handle to read from
|
||||
offset: count; # Offset in file. Should really go to 64 bit int here
|
||||
size: count; # number of bytes to read
|
||||
fh: string; # file handle to read from
|
||||
offset: count; # offset in file
|
||||
size: count; # number of bytes to read
|
||||
};
|
||||
|
||||
type read_reply_t: record {
|
||||
# if the lookup fails attr may be set.
|
||||
# if the lookup succeeds, attr may be set and all other fields are set
|
||||
attr: fattr_t &optional; # attributes
|
||||
size: count &optional; # number of bytes read
|
||||
eof: bool &optional; # did the read end at EOF
|
||||
data: string &optional; # the actual data. NOT IMPLEMENTED YET.
|
||||
# If the lookup fails, attr may be set. If the lookup succeeds, attr may be set
|
||||
# and all other fields are set.
|
||||
attr: fattr_t &optional; # attributes
|
||||
size: count &optional; # number of bytes read
|
||||
eof: bool &optional; # did the read end at EOF
|
||||
data: string &optional; # the actual data; not yet implemented.
|
||||
};
|
||||
|
||||
type readlink_reply_t: record {
|
||||
# if the request fails attr may be set.
|
||||
# if the request succeeds, attr may be set and all other fields are set
|
||||
attr: fattr_t &optional; # attributes
|
||||
nfspath: string &optional; # the contents of the symlink. In general a pathname as text
|
||||
# If the request fails, attr may be set. If the request succeeds, attr may be
|
||||
# set and all other fields are set.
|
||||
attr: fattr_t &optional; # attributes
|
||||
nfspath: string &optional; # the contents of the symlink; in general a pathname as text
|
||||
};
|
||||
|
||||
type writeargs_t: record {
|
||||
fh: string; # file handle to write to
|
||||
offset: count; # Offset in file. Should really go to 64 bit int here
|
||||
size: count; # number of bytes to write
|
||||
stable: stable_how_t; # how and when data is commited
|
||||
data: string &optional; # the actual data. NOT IMPLEMENTED YET.
|
||||
fh: string; # file handle to write to
|
||||
offset: count; # offset in file
|
||||
size: count; # number of bytes to write
|
||||
stable: stable_how_t; # how and when data is commited
|
||||
data: string &optional; # the actual data; not implemented yet
|
||||
};
|
||||
|
||||
type wcc_attr_t: record {
|
||||
|
@ -829,65 +828,64 @@ export {
|
|||
};
|
||||
|
||||
type write_reply_t: record {
|
||||
# if the request fails pre|post attr may be set.
|
||||
# if the request succeeds, pre|post attr may be set and all other fields are set
|
||||
preattr: wcc_attr_t &optional; # pre operation attributes
|
||||
postattr: fattr_t &optional; # post operation attributes
|
||||
# If the request fails, pre|post attr may be set. If the request succeeds,
|
||||
# pre|post attr may be set and all other fields are set.
|
||||
preattr: wcc_attr_t &optional; # pre operation attributes
|
||||
postattr: fattr_t &optional; # post operation attributes
|
||||
size: count &optional;
|
||||
commited: stable_how_t &optional;
|
||||
commited: stable_how_t &optional;
|
||||
verf: count &optional; # write verifier cookue
|
||||
};
|
||||
|
||||
# reply for create, mkdir, symlink
|
||||
type newobj_reply_t: record {
|
||||
# If the proc failed, dir_*_attr may be set.
|
||||
# If the proc succeeded, fh and the attr's may be set.
|
||||
# Note no guarantee that fh is set after success
|
||||
fh: string &optional; # file handle of object created.
|
||||
obj_attr: fattr_t &optional; # optional attributes associated w/ new object
|
||||
dir_pre_attr: wcc_attr_t &optional; # optional attributes associated w/ dir.
|
||||
dir_post_attr: fattr_t &optional; # optional attributes associated w/ dir.
|
||||
# If the proc failed, dir_*_attr may be set. If the proc succeeded, fh and
|
||||
# the attr's may be set. Note: no guarantee that fh is set after
|
||||
# success.
|
||||
fh: string &optional; # file handle of object created
|
||||
obj_attr: fattr_t &optional; # optional attributes associated w/ new object
|
||||
dir_pre_attr: wcc_attr_t &optional; # optional attributes associated w/ dir
|
||||
dir_post_attr: fattr_t &optional; # optional attributes associated w/ dir
|
||||
};
|
||||
|
||||
# reply for remove, rmdir
|
||||
# == wcc_data
|
||||
# Corresponds to "wcc_data" in the spec.
|
||||
type delobj_reply_t: record {
|
||||
dir_pre_attr: wcc_attr_t &optional; # optional attributes associated w/ dir.
|
||||
dir_post_attr: fattr_t &optional; # optional attributes associated w/ dir.
|
||||
dir_pre_attr: wcc_attr_t &optional; # optional attributes associated w/ dir
|
||||
dir_post_attr: fattr_t &optional; # optional attributes associated w/ dir
|
||||
};
|
||||
|
||||
# This record is used for both readdir and readdirplus
|
||||
# This record is used for both readdir and readdirplus.
|
||||
type readdirargs_t: record {
|
||||
isplus: bool; # is this a readdirplus request?
|
||||
dirfh: string; # the directory filehandle
|
||||
cookie: count; # cookie / pos in dir. 0 for first call
|
||||
cookieverf: count; # the cookie verifier
|
||||
dircount: count; # "count" field for readdir, maxcount otherwise. in bytes
|
||||
maxcount: count &optional; # only used for readdirplus. in bytes
|
||||
isplus: bool; # is this a readdirplus request?
|
||||
dirfh: string; # the directory filehandle
|
||||
cookie: count; # cookie / pos in dir; 0 for first call
|
||||
cookieverf: count; # the cookie verifier
|
||||
dircount: count; # "count" field for readdir; maxcount otherwise (in bytes)
|
||||
maxcount: count &optional; # only used for readdirplus. in bytes
|
||||
};
|
||||
|
||||
type direntry_t: record {
|
||||
# fh and attr are used for readdirplus. However, even for readdirplus they need not
|
||||
# be filled out.
|
||||
fileid: count; # e.g., inode number
|
||||
fname: string; # filename
|
||||
cookie: count;
|
||||
attr: fattr_t &optional; # readdirplus: the FH attributes for the entry
|
||||
fh: string &optional; # readdirplus: the FH for the entry
|
||||
# fh and attr are used for readdirplus. However, even for readdirplus they may
|
||||
# not be filled out.
|
||||
fileid: count; # e.g., inode number
|
||||
fname: string; # filename
|
||||
cookie: count;
|
||||
attr: fattr_t &optional; # readdirplus: the FH attributes for the entry
|
||||
fh: string &optional; # readdirplus: the FH for the entry
|
||||
};
|
||||
|
||||
type direntry_vec_t: vector of direntry_t;
|
||||
|
||||
# used for readdir and readdirplus
|
||||
# Used for readdir and readdirplus.
|
||||
type readdir_reply_t: record {
|
||||
isplus: bool; # is the reply for a readdirplus request
|
||||
# if error: dir_attr might be set
|
||||
# if success: dir_attr may be set, all others must be set
|
||||
dir_attr: fattr_t &optional;
|
||||
# If error: dir_attr might be set. If success: dir_attr may be set, all others
|
||||
# must be set.
|
||||
isplus: bool; # is the reply for a readdirplus request
|
||||
dir_attr: fattr_t &optional;
|
||||
cookieverf: count &optional;
|
||||
entries: direntry_vec_t &optional;
|
||||
eof: bool; # if T: no more entries in dir.
|
||||
|
||||
eof: bool; # if true, no more entries in dir.
|
||||
};
|
||||
|
||||
type fsstat_t: record {
|
||||
|
@ -903,6 +901,7 @@ export {
|
|||
} # end export
|
||||
|
||||
module GLOBAL;
|
||||
|
||||
type ntp_msg: record {
|
||||
id: count;
|
||||
code: count;
|
||||
|
@ -1069,16 +1068,20 @@ global dns_max_queries = 5;
|
|||
# has bigger cipherspecs, we won't do a comparisons of cipherspecs.
|
||||
const ssl_max_cipherspec_size = 68 &redef;
|
||||
|
||||
# SSL and X.509 types.
|
||||
type cipher_suites_list: set[count];
|
||||
type SSL_sessionID: table[count] of count;
|
||||
type X509_extension: table[count] of string;
|
||||
type X509_extensions: table[count] of string;
|
||||
|
||||
type X509: record {
|
||||
issuer: string;
|
||||
version: count;
|
||||
serial: string;
|
||||
subject: string;
|
||||
orig_addr: addr;
|
||||
issuer: string;
|
||||
not_valid_before: time;
|
||||
not_valid_after: time;
|
||||
};
|
||||
|
||||
# This is indexed with the CA's name and yields a DER (binary) encoded certificate.
|
||||
const root_ca_certs: table[string] of string = {} &redef;
|
||||
|
||||
type http_stats_rec: record {
|
||||
num_requests: count;
|
||||
num_replies: count;
|
||||
|
@ -1383,7 +1386,7 @@ const gap_report_freq = 1.0 sec &redef;
|
|||
|
||||
# Whether we want content_gap and drop reports for partial connections
|
||||
# (a connection is partial if it is missing a full handshake). Note that
|
||||
# gap reports for partial connections might not be reliable.
|
||||
# gap reports for partial connections might not be reliable.
|
||||
const report_gaps_for_partial = F &redef;
|
||||
|
||||
# Globals associated with entire-run statistics on gaps (useful
|
||||
|
|
|
@ -18,6 +18,10 @@ global have_SMTP = F; # if true, we've loaded smtp.bro
|
|||
# TODO: Do we have a nicer way of defining this prototype?
|
||||
export { global FTP::is_ftp_data_conn: function(c: connection): bool; }
|
||||
|
||||
# Whether to include connection state history in the logs generated
|
||||
# by record_connection.
|
||||
const record_state_history = F &redef;
|
||||
|
||||
# Whether to add 4 more columns to conn.log with
|
||||
# orig_packet orig_ip_bytes resp_packets resp_ip_bytes
|
||||
# Requires use_conn_size_analyzer=T
|
||||
|
@ -311,6 +315,10 @@ function record_connection(f: file, c: connection)
|
|||
conn_size(c$orig, trans), conn_size(c$resp, trans),
|
||||
conn_state(c, trans), flags);
|
||||
|
||||
if ( record_state_history )
|
||||
log_msg = fmt("%s %s", log_msg,
|
||||
c$history == "" ? "X" : c$history);
|
||||
|
||||
if ( use_conn_size_analyzer && report_conn_size_analyzer )
|
||||
log_msg = fmt("%s %s %s", log_msg,
|
||||
conn_size_from_analyzer(c$orig), conn_size_from_analyzer(c$resp));
|
||||
|
|
|
@ -10,17 +10,16 @@ export {
|
|||
global names_log_file = open_log_file("nfs-files") &redef;
|
||||
global readdir_log = open_log_file("nfs-readdir") &redef;
|
||||
|
||||
# we want to estiamte how long it takes to lookup a chain of FH
|
||||
# (directories) until we reach a FH that is used in a read or write
|
||||
# operation. Whenever we get a new FH, we check how long ago we
|
||||
# got the FH's parent. If this is less than fh_chain_maxtime, we
|
||||
# assume that they belong to a lookup chain and set the dt value for
|
||||
# the FH accordingly.
|
||||
# We want to estimate how long it takes to lookup a chain of FH (directories)
|
||||
# until we reach a FH that is used in a read or write operation. Whenever we
|
||||
# get a new FH, we check how long ago we got the FH's parent. If this is less
|
||||
# than fh_chain_maxtime, we assume that they belong to a lookup chain and set
|
||||
# the dt value for the FH accordingly.
|
||||
global fh_chain_maxtime = 100 msec;
|
||||
}
|
||||
|
||||
|
||||
redef capture_filters += {
|
||||
redef capture_filters += {
|
||||
["nfs"] = "port 2049",
|
||||
# NFS UDP packets are often fragmented.
|
||||
["nfs-frag"] = "(ip[6:2] & 0x3fff != 0) and udp",
|
||||
|
@ -30,13 +29,13 @@ global nfs_ports = { 2049/tcp, 2049/udp } &redef;
|
|||
redef dpd_config += { [ANALYZER_NFS] = [$ports = nfs_ports] };
|
||||
|
||||
# Information about a filehandle
|
||||
type fh_info : record {
|
||||
type fh_info : record {
|
||||
id: count; # A unique ID (counter) for more readable representation of the FH
|
||||
pathname: string &default="@"; # the path leading to this FH
|
||||
basename: string &default=""; # the name of this FHs file or directory
|
||||
mimetype: string &default="";
|
||||
t0: time &default=double_to_time(0); # time when we first saw this FH
|
||||
dt: interval &default=0 sec; # time it took to get this FH (assuming a chain of
|
||||
dt: interval &default=0 sec; # time it took to get this FH (assuming a chain of
|
||||
# procedures that ultimately yield the FH for the file
|
||||
# a client is interested in
|
||||
chainlen: count &default=0;
|
||||
|
@ -66,7 +65,7 @@ function get_fh_info(c: connection, fh: string): fh_info
|
|||
return fh_map[c$id$resp_h, fh];
|
||||
}
|
||||
|
||||
function log_filename(proc: string, info: fh_info)
|
||||
function log_filename(proc: string, info: fh_info)
|
||||
{
|
||||
print names_log_file, fmt("%.6f %s path FH%d %s/%s", network_time(), proc,
|
||||
info$id, info$pathname, info$basename);
|
||||
|
@ -75,7 +74,7 @@ function log_filename(proc: string, info: fh_info)
|
|||
|
||||
function fmt_attr(a: fattr_t): string
|
||||
{
|
||||
local s = fmt("%s %s %d %d %d %d %d %d %d %d %d %.2f %.2f %.2f",
|
||||
local s = fmt("%s %s %d %d %d %d %d %d %d %d %d %.2f %.2f %.2f",
|
||||
a$ftype, mode2string(a$mode), a$nlink, a$uid, a$gid, a$size, a$used, a$rdev1, a$rdev2,
|
||||
a$fsid, a$fileid, a$atime, a$mtime, a$ctime);
|
||||
return s;
|
||||
|
@ -85,11 +84,11 @@ function log_attributes(c: connection, proc: string, fh: string, attr: fattr_t)
|
|||
{
|
||||
local info = get_fh_info(c,fh);
|
||||
local did_change = F;
|
||||
# check whether the attributes have changes
|
||||
if (info?$attr)
|
||||
# check whether the attributes have changes
|
||||
if (info?$attr)
|
||||
{
|
||||
# We can't compare records for equality :-(. So we use a hack.
|
||||
# We add the two instance we want to compare to a set. If there
|
||||
# We can't compare records for equality :-(. So we use a hack.
|
||||
# We add the two instance we want to compare to a set. If there
|
||||
# are two elements in the set, the records are not equal...
|
||||
local dummy: set[fattr_t];
|
||||
add dummy[info$attr];
|
||||
|
@ -102,13 +101,13 @@ function log_attributes(c: connection, proc: string, fh: string, attr: fattr_t)
|
|||
if (did_change)
|
||||
{
|
||||
info$attr = attr;
|
||||
print names_log_file, fmt("%.6f %s attr FH%d %s", network_time(), proc,
|
||||
print names_log_file, fmt("%.6f %s attr FH%d %s", network_time(), proc,
|
||||
info$id, fmt_attr(attr));
|
||||
}
|
||||
}
|
||||
|
||||
# Update (or add) a filehandle mapping.
|
||||
# parentfh ... parent (directory)
|
||||
# parentfh ... parent (directory)
|
||||
# name ....... the name for this FH
|
||||
# fh ......... the new FH
|
||||
function add_update_fh(c: connection, proc: string, parentfh: string, name: string, fh: string)
|
||||
|
@ -120,11 +119,11 @@ function add_update_fh(c: connection, proc: string, parentfh: string, name: stri
|
|||
if (name == ".")
|
||||
return;
|
||||
info$basename = name;
|
||||
if (parentfh != "")
|
||||
if (parentfh != "")
|
||||
{
|
||||
local parentinfo = get_fh_info(c, parentfh);
|
||||
info$pathname = cat(parentinfo$pathname, "/", parentinfo$basename);
|
||||
if ( (network_time() - parentinfo$t0) < fh_chain_maxtime
|
||||
if ( (network_time() - parentinfo$t0) < fh_chain_maxtime
|
||||
&& info$dt < 0 sec )
|
||||
{
|
||||
# The FH is part of lookup chain and it doesn't yet have a dt value
|
||||
|
@ -151,15 +150,15 @@ function set_fh_mimetype(c: connection, fh: string, proc:string, data: string)
|
|||
}
|
||||
}
|
||||
|
||||
# Get the total time of the lookup chain for this FH to the
|
||||
# current network time. Returns a negative interal if no
|
||||
# Get the total time of the lookup chain for this FH to the
|
||||
# current network time. Returns a negative interal if no
|
||||
# lookup chain was found
|
||||
function get_fh_chaintime_str(c:connection, fh:string): string
|
||||
{
|
||||
local info = get_fh_info(c, fh);
|
||||
if ((network_time() - info$t0) < fh_chain_maxtime)
|
||||
return fmt("%d %.6f", info$chainlen, info$dt + (network_time() - info$t0));
|
||||
else
|
||||
else
|
||||
return fmt("%d %.6f", 0, 0.0);
|
||||
}
|
||||
|
||||
|
@ -187,7 +186,7 @@ function print_attr(attr: fattr_t): string
|
|||
return fmt("%s", attr);
|
||||
}
|
||||
|
||||
function map_conn(cid: conn_id): count
|
||||
function map_conn(cid: conn_id): count
|
||||
{
|
||||
if (cid !in nfs_conns)
|
||||
nfs_conns[cid] = ++num_nfs_conns;
|
||||
|
@ -208,16 +207,16 @@ function is_rpc_success(info: info_t): bool
|
|||
function nfs_get_log_prefix(c: connection, info: info_t, proc: string): string
|
||||
{
|
||||
local nfs_stat_str = (info$rpc_stat == RPC_SUCCESS) ? fmt("%s", info$nfs_stat) : "X";
|
||||
return fmt("%.06f %.06f %d %.06f %.06f %d %s %s %d %s %s %s",
|
||||
return fmt("%.06f %.06f %d %.06f %.06f %d %s %s %d %s %s %s",
|
||||
info$req_start, info$req_dur, info$req_len,
|
||||
info$rep_start, info$rep_dur, info$rep_len,
|
||||
id_string(c$id), get_port_transport_proto(c$id$orig_p),
|
||||
map_conn(c$id),
|
||||
map_conn(c$id),
|
||||
proc, info$rpc_stat, nfs_stat_str);
|
||||
}
|
||||
|
||||
|
||||
event nfs_proc_not_implemented(c: connection, info: info_t, proc: proc_t)
|
||||
event nfs_proc_not_implemented(c: connection, info: info_t, proc: proc_t)
|
||||
{
|
||||
local prefix = nfs_get_log_prefix(c, info, fmt("%s", proc));
|
||||
|
||||
|
@ -231,13 +230,13 @@ event nfs_proc_null(c: connection, info: info_t)
|
|||
print log_file, prefix;
|
||||
}
|
||||
|
||||
event nfs_proc_getattr (c: connection, info: info_t, fh: string, attrs: fattr_t)
|
||||
event nfs_proc_getattr (c: connection, info: info_t, fh: string, attrs: fattr_t)
|
||||
{
|
||||
local prefix = nfs_get_log_prefix(c, info, "getattr");
|
||||
|
||||
if (is_success(info))
|
||||
log_attributes(c, "getattr", fh, attrs);
|
||||
|
||||
|
||||
print log_file, fmt("%s %s", prefix, get_fh_id(c,fh));
|
||||
}
|
||||
|
||||
|
@ -257,7 +256,7 @@ event nfs_proc_lookup(c: connection, info: info_t, req: diropargs_t, rep: lookup
|
|||
log_attributes(c, "lookup", rep$fh, rep$obj_attr);
|
||||
add_update_fh(c, "lookup", req$dirfh, req$fname, rep$fh);
|
||||
print log_file, fmt("%s %s + %s => %s", prefix, get_fh_id(c, req$dirfh), req$fname, get_fh_id(c, rep$fh));
|
||||
|
||||
|
||||
}
|
||||
|
||||
event nfs_proc_read(c: connection, info: info_t, req: readargs_t, rep: read_reply_t)
|
||||
|
@ -267,7 +266,7 @@ event nfs_proc_read(c: connection, info: info_t, req: readargs_t, rep: read_repl
|
|||
msg = fmt("%s %s @%d: %d", msg, get_fh_id(c, req$fh), req$offset, req$size);
|
||||
if (is_success(info))
|
||||
{
|
||||
msg = fmt("%s got %d bytes %s %s", msg, rep$size, (rep$eof) ? "<eof>" : "x",
|
||||
msg = fmt("%s got %d bytes %s %s", msg, rep$size, (rep$eof) ? "<eof>" : "x",
|
||||
get_fh_chaintime_str(c, req$fh));
|
||||
if (rep?$data && req$offset==0 && rep$size>0)
|
||||
set_fh_mimetype(c, req$fh, "read", rep$data);
|
||||
|
@ -278,7 +277,7 @@ event nfs_proc_read(c: connection, info: info_t, req: readargs_t, rep: read_repl
|
|||
print log_file, msg;
|
||||
}
|
||||
|
||||
event nfs_proc_readlink(c: connection, info: info_t, fh: string, rep: readlink_reply_t)
|
||||
event nfs_proc_readlink(c: connection, info: info_t, fh: string, rep: readlink_reply_t)
|
||||
{
|
||||
local msg = nfs_get_log_prefix(c, info, "readlink");
|
||||
|
||||
|
@ -300,7 +299,7 @@ event nfs_proc_write(c: connection, info: info_t, req: writeargs_t, rep: write_r
|
|||
msg = fmt("%s %s @%d: %d %s", msg, get_fh_id(c, req$fh), req$offset, req$size, req$stable);
|
||||
if (is_success(info))
|
||||
{
|
||||
msg = fmt("%s wrote %d bytes %s %s", msg, rep$size, rep$commited,
|
||||
msg = fmt("%s wrote %d bytes %s %s", msg, rep$size, rep$commited,
|
||||
get_fh_chaintime_str(c, req$fh));
|
||||
if (req?$data && req$offset==0 && rep$size>0)
|
||||
set_fh_mimetype(c, req$fh, "write", req$data);
|
||||
|
@ -370,7 +369,7 @@ function fmt_direntry(c: connection, e: direntry_t): string
|
|||
if (e?$fh)
|
||||
rv = fmt("%s %s", rv, get_fh_id(c, e$fh));
|
||||
return rv;
|
||||
|
||||
|
||||
}
|
||||
|
||||
event nfs_proc_readdir(c: connection, info: info_t, req: readdirargs_t, rep: readdir_reply_t)
|
||||
|
|
|
@ -58,8 +58,13 @@ function build_default_pcap_filter(): string
|
|||
return cmd_line_bpf_filter;
|
||||
|
||||
if ( all_packets )
|
||||
{
|
||||
# Return an "always true" filter.
|
||||
return "ip or not ip";
|
||||
if ( bro_has_ipv6() )
|
||||
return "ip or not ip";
|
||||
else
|
||||
return "not ip6";
|
||||
}
|
||||
|
||||
## Build filter dynamically.
|
||||
|
||||
|
@ -76,6 +81,10 @@ function build_default_pcap_filter(): string
|
|||
# Finally, join them.
|
||||
local filter = join_filters(cfilter, rfilter);
|
||||
|
||||
# Exclude IPv6 if we don't support it.
|
||||
if ( ! bro_has_ipv6() )
|
||||
filter = fmt("(not ip6) and (%s)", filter);
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
|
|
@ -118,6 +118,8 @@ export {
|
|||
|
||||
# Indexed by the portmapper request and a boolean that's T if
|
||||
# the request was answered, F it was attempted but not answered.
|
||||
# If there's an entry in the set, then the access won't lead to a
|
||||
# NOTICE (unless the connection is hot for some other reason).
|
||||
# If there's an entry in the set, then the access won't lead to a
|
||||
# NOTICE (unless the connection is hot for some other reason).
|
||||
const RPC_do_not_complain: set[string, bool] = {
|
||||
|
@ -140,7 +142,7 @@ export {
|
|||
# Logs all portmapper mappings that we observe (i.e., getport and
|
||||
# dump replies. Format:
|
||||
# timestamp orig_h orig_p resp_h resp_p proto localInit PortmapProcedure RPCprogram version port proto
|
||||
# the mapping is then: <resp_h> accepts <RPCprogram> with <version>
|
||||
# the mapping is then: <resp_h> accepts <RPCprogram> with <version>
|
||||
# calls on <port> <proto>. We learned this mapping via <PortmapProcedure>
|
||||
const mapping_log_file = open_log_file("portmapper-maps") &redef;
|
||||
}
|
||||
|
@ -151,7 +153,7 @@ const portmapper_ports = { 111/tcp, 111/udp } &redef;
|
|||
redef dpd_config += { [ANALYZER_PORTMAPPER] = [$ports = portmapper_ports] };
|
||||
|
||||
# Indexed by source and destination addresses, plus the portmapper service.
|
||||
# If the tuple is in the set, then we already created a NOTICE for it and
|
||||
# If the tuple is in the set, then we already created a NOTICE for it and
|
||||
# shouldn't do so again.
|
||||
global did_pm_notice: set[addr, addr, string];
|
||||
|
||||
|
@ -185,7 +187,7 @@ function rpc_prog(p: count): string
|
|||
|
||||
function pm_get_conn_string(cid: conn_id) : string
|
||||
{
|
||||
return fmt("%s %d %s %d %s %s",
|
||||
return fmt("%s %d %s %d %s %s",
|
||||
cid$orig_h, cid$orig_p,
|
||||
cid$resp_h, cid$resp_p,
|
||||
get_port_transport_proto(cid$resp_p),
|
||||
|
@ -194,9 +196,9 @@ function pm_get_conn_string(cid: conn_id) : string
|
|||
}
|
||||
|
||||
# Log a pm_request or pm_attempt to the log file
|
||||
function pm_log(r: connection, proc: string, msg: string, success: bool)
|
||||
function pm_log(r: connection, proc: string, msg: string, success: bool)
|
||||
{
|
||||
print log_file, fmt("%f %s %s %s %s", network_time(),
|
||||
print log_file, fmt("%f %s %s %s %s", network_time(),
|
||||
pm_get_conn_string(r$id),
|
||||
proc, success, msg);
|
||||
}
|
||||
|
@ -218,9 +220,9 @@ function pm_log_mapping_dump(r: connection, m: pm_mappings)
|
|||
}
|
||||
|
||||
# Log portmapper mappings received from a getport procedure
|
||||
# Unfortunately, pm_request_getport doesn't return pm_mapping,
|
||||
# Unfortunately, pm_request_getport doesn't return pm_mapping,
|
||||
# but returns the parameters separately ....
|
||||
function pm_log_mapping_getport(r: connection, pr: pm_port_request, p: port)
|
||||
function pm_log_mapping_getport(r: connection, pr: pm_port_request, p: port)
|
||||
{
|
||||
local prog = rpc_prog(pr$program);
|
||||
local ver = pr$version;
|
||||
|
@ -229,7 +231,7 @@ function pm_log_mapping_getport(r: connection, pr: pm_port_request, p: port)
|
|||
pm_get_conn_string(r$id),
|
||||
prog, ver, p, get_port_transport_proto(p));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function pm_check_getport(r: connection, prog: string): bool
|
||||
|
@ -377,7 +379,7 @@ event pm_request_dump(r: connection, m: pm_mappings)
|
|||
{
|
||||
local do_notice = [r$id$orig_h, r$id$resp_h] !in RPC_dump_okay;
|
||||
# pm_mapping_to_text has the side-effect of updating RPC_server_map
|
||||
pm_request(r, "pm_dump",
|
||||
pm_request(r, "pm_dump",
|
||||
length(m) == 0 ? "(nil)" : pm_mapping_to_text(r$id$resp_h, m),
|
||||
do_notice);
|
||||
pm_log_mapping_dump(r, m);
|
||||
|
|
131
policy/ssl-mozilla-CAs.bro
Normal file
131
policy/ssl-mozilla-CAs.bro
Normal file
File diff suppressed because one or more lines are too long
979
policy/ssl.bro
979
policy/ssl.bro
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,7 @@
|
|||
// $Id: Analyzer.cc,v 1.1.4.28 2006/06/01 17:18:10 sommer Exp $
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Analyzer.h"
|
||||
#include "PIA.h"
|
||||
#include "Event.h"
|
||||
|
@ -34,8 +36,8 @@
|
|||
#include "Portmap.h"
|
||||
#include "POP3.h"
|
||||
#include "SSH.h"
|
||||
#include "SSLProxy.h"
|
||||
#include "SSL-binpac.h"
|
||||
#include "Syslog-binpac.h"
|
||||
#include "ConnSizeAnalyzer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -60,6 +62,9 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
|
|||
{ AnalyzerTag::ICMP_Echo, "ICMP_ECHO",
|
||||
ICMP_Echo_Analyzer::InstantiateAnalyzer,
|
||||
ICMP_Echo_Analyzer::Available, 0, false },
|
||||
{ AnalyzerTag::ICMP_Redir, "ICMP_REDIR",
|
||||
ICMP_Redir_Analyzer::InstantiateAnalyzer,
|
||||
ICMP_Redir_Analyzer::Available, 0, false },
|
||||
|
||||
{ AnalyzerTag::TCP, "TCP", TCP_Analyzer::InstantiateAnalyzer,
|
||||
TCP_Analyzer::Available, 0, false },
|
||||
|
@ -116,8 +121,6 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
|
|||
SMTP_Analyzer::Available, 0, false },
|
||||
{ AnalyzerTag::SSH, "SSH", SSH_Analyzer::InstantiateAnalyzer,
|
||||
SSH_Analyzer::Available, 0, false },
|
||||
{ AnalyzerTag::SSL, "SSL", SSLProxy_Analyzer::InstantiateAnalyzer,
|
||||
SSLProxy_Analyzer::Available, 0, false },
|
||||
{ AnalyzerTag::Telnet, "TELNET", Telnet_Analyzer::InstantiateAnalyzer,
|
||||
Telnet_Analyzer::Available, 0, false },
|
||||
|
||||
|
@ -133,9 +136,12 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
|
|||
{ AnalyzerTag::HTTP_BINPAC, "HTTP_BINPAC",
|
||||
HTTP_Analyzer_binpac::InstantiateAnalyzer,
|
||||
HTTP_Analyzer_binpac::Available, 0, false },
|
||||
{ AnalyzerTag::SSL_BINPAC, "SSL_BINPAC",
|
||||
{ AnalyzerTag::SSL, "SSL",
|
||||
SSL_Analyzer_binpac::InstantiateAnalyzer,
|
||||
SSL_Analyzer_binpac::Available, 0, false },
|
||||
{ AnalyzerTag::SYSLOG_BINPAC, "SYSLOG_BINPAC",
|
||||
Syslog_Analyzer_binpac::InstantiateAnalyzer,
|
||||
Syslog_Analyzer_binpac::Available, 0, false },
|
||||
|
||||
{ AnalyzerTag::File, "FILE", File_Analyzer::InstantiateAnalyzer,
|
||||
File_Analyzer::Available, 0, false },
|
||||
|
@ -168,7 +174,6 @@ const Analyzer::Config Analyzer::analyzer_configs[] = {
|
|||
{ AnalyzerTag::Contents_SMB, "CONTENTS_SMB", 0, 0, 0, false },
|
||||
{ AnalyzerTag::Contents_RPC, "CONTENTS_RPC", 0, 0, 0, false },
|
||||
{ AnalyzerTag::Contents_NFS, "CONTENTS_NFS", 0, 0, 0, false },
|
||||
{ AnalyzerTag::Contents_SSL, "CONTENTS_SSL", 0, 0, 0, false },
|
||||
};
|
||||
|
||||
AnalyzerTimer::~AnalyzerTimer()
|
||||
|
|
|
@ -22,19 +22,20 @@ namespace AnalyzerTag {
|
|||
PIA_TCP, PIA_UDP,
|
||||
|
||||
// Transport-layer analyzers.
|
||||
ICMP, ICMP_TimeExceeded, ICMP_Unreachable, ICMP_Echo, TCP, UDP,
|
||||
ICMP,
|
||||
ICMP_TimeExceeded, ICMP_Unreachable, ICMP_Echo, ICMP_Redir,
|
||||
TCP, UDP,
|
||||
|
||||
// Application-layer analyzers (hand-written).
|
||||
BitTorrent, BitTorrentTracker,
|
||||
DCE_RPC, DNS, Finger, FTP, Gnutella, HTTP, Ident, IRC,
|
||||
Login, NCP, NetbiosSSN, NFS, NTP, POP3, Portmapper, Rlogin,
|
||||
RPC, Rsh, SMB, SMTP, SSH,
|
||||
SSL,
|
||||
Telnet,
|
||||
|
||||
// Application-layer analyzers, binpac-generated.
|
||||
DHCP_BINPAC, DNS_TCP_BINPAC, DNS_UDP_BINPAC,
|
||||
HTTP_BINPAC, SSL_BINPAC,
|
||||
HTTP_BINPAC, SSL, SYSLOG_BINPAC,
|
||||
|
||||
// Other
|
||||
File, Backdoor, InterConn, SteppingStone, TCPStats,
|
||||
|
@ -45,7 +46,6 @@ namespace AnalyzerTag {
|
|||
Contents, ContentLine, NVT, Zip, Contents_DNS, Contents_NCP,
|
||||
Contents_NetbiosSSN, Contents_Rlogin, Contents_Rsh,
|
||||
Contents_DCE_RPC, Contents_SMB, Contents_RPC, Contents_NFS,
|
||||
Contents_SSL,
|
||||
// End-marker.
|
||||
LastAnalyzer
|
||||
};
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <algorithm>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BroString.h"
|
||||
#include "Var.h"
|
||||
|
||||
|
|
|
@ -197,8 +197,8 @@ binpac_target(smb.pac
|
|||
smb-protocol.pac smb-pipe.pac smb-mailslot.pac)
|
||||
binpac_target(ssl.pac
|
||||
ssl-defs.pac ssl-protocol.pac ssl-analyzer.pac)
|
||||
binpac_target(ssl-record-layer.pac
|
||||
ssl-defs.pac ssl.pac)
|
||||
binpac_target(syslog.pac
|
||||
syslog-protocol.pac syslog-analyzer.pac)
|
||||
|
||||
########################################################################
|
||||
## bro target
|
||||
|
@ -239,16 +239,6 @@ set(dns_SRCS nb_dns.c)
|
|||
set_source_files_properties(nb_dns.c PROPERTIES COMPILE_FLAGS
|
||||
-fno-strict-aliasing)
|
||||
|
||||
set(openssl_SRCS
|
||||
X509.cc
|
||||
SSLCiphers.cc
|
||||
SSLInterpreter.cc
|
||||
SSLProxy.cc
|
||||
SSLv2.cc
|
||||
SSLv3.cc
|
||||
SSLv3Automaton.cc
|
||||
)
|
||||
|
||||
if (USE_NMALLOC)
|
||||
set(malloc_SRCS malloc.c)
|
||||
endif ()
|
||||
|
@ -389,6 +379,7 @@ set(bro_SRCS
|
|||
Stats.cc
|
||||
SteppingStone.cc
|
||||
Stmt.cc
|
||||
Syslog-binpac.cc
|
||||
TCP.cc
|
||||
TCP_Endpoint.cc
|
||||
TCP_Reassembler.cc
|
||||
|
|
52
src/Conn.cc
52
src/Conn.cc
|
@ -216,56 +216,6 @@ Connection::~Connection()
|
|||
--external_connections;
|
||||
}
|
||||
|
||||
uint64 Connection::uid_counter = 0;
|
||||
uint64 Connection::uid_instance = 0;
|
||||
|
||||
uint64 Connection::CalculateNextUID()
|
||||
{
|
||||
if ( uid_instance == 0 )
|
||||
{
|
||||
// This is the first time we need a UID.
|
||||
|
||||
if ( ! have_random_seed() )
|
||||
{
|
||||
// If we don't need deterministic output (as
|
||||
// indicated by a set seed), we calculate the
|
||||
// instance ID by hashing something likely to be
|
||||
// globally unique.
|
||||
struct {
|
||||
char hostname[128];
|
||||
struct timeval time;
|
||||
pid_t pid;
|
||||
int rnd;
|
||||
} unique;
|
||||
|
||||
gethostname(unique.hostname, 128);
|
||||
unique.hostname[sizeof(unique.hostname)-1] = '\0';
|
||||
gettimeofday(&unique.time, 0);
|
||||
unique.pid = getpid();
|
||||
unique.rnd = bro_random();
|
||||
|
||||
uid_instance = HashKey::HashBytes(&unique, sizeof(unique));
|
||||
++uid_instance; // Now it's larger than zero.
|
||||
}
|
||||
|
||||
else
|
||||
// Generate determistic UIDs.
|
||||
uid_instance = 1;
|
||||
}
|
||||
|
||||
// Now calculate the unique ID for this connection.
|
||||
struct {
|
||||
uint64 counter;
|
||||
hash_t instance;
|
||||
} key;
|
||||
|
||||
key.counter = ++uid_counter;
|
||||
key.instance = uid_instance;
|
||||
|
||||
uint64_t h = HashKey::HashBytes(&key, sizeof(key));
|
||||
return h;
|
||||
}
|
||||
|
||||
void Connection::Done()
|
||||
{
|
||||
finished = 1;
|
||||
|
@ -417,7 +367,7 @@ RecordVal* Connection::BuildConnVal()
|
|||
conn_val->Assign(8, new StringVal("")); // history
|
||||
|
||||
if ( ! uid )
|
||||
uid = CalculateNextUID();
|
||||
uid = calculate_unique_id();
|
||||
|
||||
char tmp[20];
|
||||
conn_val->Assign(9, new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62)));
|
||||
|
|
|
@ -303,8 +303,6 @@ public:
|
|||
|
||||
void SetUID(uint64 arg_uid) { uid = arg_uid; }
|
||||
|
||||
static uint64 CalculateNextUID();
|
||||
|
||||
protected:
|
||||
|
||||
Connection() { persistent = 0; }
|
||||
|
@ -363,9 +361,6 @@ protected:
|
|||
PIA* primary_PIA;
|
||||
|
||||
uint64 uid; // Globally unique connection ID.
|
||||
|
||||
static uint64 uid_counter; // Counter for uids.
|
||||
static uint64 uid_instance; // Instance ID, computed once.
|
||||
};
|
||||
|
||||
class ConnectionTimer : public Timer {
|
||||
|
|
|
@ -620,7 +620,7 @@ void ConnCompressor::PktHdrToPendingConn(double time, const HashKey* key,
|
|||
c->FIN = (tp->th_flags & TH_FIN) != 0;
|
||||
c->RST = (tp->th_flags & TH_RST) != 0;
|
||||
c->ACK = (tp->th_flags & TH_ACK) != 0;
|
||||
c->uid = Connection::CalculateNextUID();
|
||||
c->uid = calculate_unique_id();
|
||||
c->num_bytes_ip = ip->TotalLen();
|
||||
c->num_pkts = 1;
|
||||
c->invalid = 0;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// $Id: ContentLine.cc,v 1.1.2.8 2006/06/01 01:55:42 sommer Exp $
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "ContentLine.h"
|
||||
#include "TCP.h"
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "DNS_Mgr.h"
|
||||
#include "Event.h"
|
||||
#include "Net.h"
|
||||
|
|
|
@ -229,6 +229,14 @@ bool DPM::BuildInitialAnalyzerTree(TransportProto proto, Connection* conn,
|
|||
}
|
||||
break;
|
||||
|
||||
case ICMP_REDIRECT:
|
||||
if ( ICMP_Redir_Analyzer::Available() )
|
||||
{
|
||||
root = new ICMP_Redir_Analyzer(conn);
|
||||
DBG_DPD(conn, "activated ICMP Redir analyzer");
|
||||
}
|
||||
break;
|
||||
|
||||
case ICMP_UNREACH:
|
||||
if ( ICMP_Unreachable_Analyzer::Available() )
|
||||
{
|
||||
|
|
|
@ -343,7 +343,7 @@ vector<ParseLocationRec> parse_location_string(const string& s)
|
|||
plr.type = plrUnknown;
|
||||
|
||||
FILE* throwaway = search_for_file(filename.c_str(), "bro",
|
||||
&full_filename);
|
||||
&full_filename, true);
|
||||
if ( ! throwaway )
|
||||
{
|
||||
debug_msg("No such policy file: %s.\n", filename.c_str());
|
||||
|
|
56
src/Desc.cc
56
src/Desc.cc
|
@ -207,41 +207,71 @@ void ODesc::Indent()
|
|||
}
|
||||
}
|
||||
|
||||
static const char hex_chars[] = "0123456789ABCDEF";
|
||||
static const char hex_chars[] = "0123456789abcdef";
|
||||
|
||||
static const char* find_first_unprintable(ODesc* d, const char* bytes, unsigned int n)
|
||||
{
|
||||
if ( d->IsBinary() )
|
||||
return 0;
|
||||
|
||||
while ( n-- )
|
||||
{
|
||||
if ( ! isprint(*bytes) )
|
||||
return bytes;
|
||||
++bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ODesc::AddBytes(const void* bytes, unsigned int n)
|
||||
{
|
||||
if ( ! escape )
|
||||
return AddBytesRaw(bytes, n);
|
||||
|
||||
const char* s = (const char*) bytes;
|
||||
const char* e = (const char*) bytes + n;
|
||||
|
||||
while ( s < e )
|
||||
{
|
||||
const char* t = (const char*) memchr(s, escape[0], e - s);
|
||||
const char* t1 = escape ? (const char*) memchr(s, escape[0], e - s) : e;
|
||||
const char* t2 = find_first_unprintable(this, s, t1 ? e - t1 : e - s);
|
||||
|
||||
if ( ! t )
|
||||
if ( t2 && (t2 < t1 || ! t1) )
|
||||
{
|
||||
AddBytesRaw(s, t2 - s);
|
||||
|
||||
char hex[6] = "\\x00";
|
||||
hex[2] = hex_chars[((*t2) & 0xf0) >> 4];
|
||||
hex[3] = hex_chars[(*t2) & 0x0f];
|
||||
AddBytesRaw(hex, sizeof(hex));
|
||||
|
||||
s = t2 + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! escape )
|
||||
break;
|
||||
|
||||
if ( memcmp(t, escape, escape_len) != 0 )
|
||||
if ( ! t1 )
|
||||
break;
|
||||
|
||||
AddBytesRaw(s, t - s);
|
||||
if ( memcmp(t1, escape, escape_len) != 0 )
|
||||
break;
|
||||
|
||||
AddBytesRaw(s, t1 - s);
|
||||
|
||||
for ( int i = 0; i < escape_len; ++i )
|
||||
{
|
||||
char hex[5] = "\\x00";
|
||||
hex[2] = hex_chars[(*t) >> 4];
|
||||
hex[3] = hex_chars[(*t) & 0x0f];
|
||||
hex[2] = hex_chars[((*t1) & 0xf0) >> 4];
|
||||
hex[3] = hex_chars[(*t1) & 0x0f];
|
||||
AddBytesRaw(hex, sizeof(hex));
|
||||
++t;
|
||||
++t1;
|
||||
}
|
||||
|
||||
s = t;
|
||||
s = t1;
|
||||
}
|
||||
|
||||
AddBytesRaw(s, e - s);
|
||||
if ( s < e )
|
||||
AddBytesRaw(s, e - s);
|
||||
}
|
||||
|
||||
void ODesc::AddBytesRaw(const void* bytes, unsigned int n)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "Net.h"
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "File.h"
|
||||
#include "Type.h"
|
||||
#include "Timer.h"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// $Id: FileAnalyzer.cc,v 1.1.4.2 2006/06/01 17:18:10 sommer Exp $
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "FileAnalyzer.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "NetVar.h"
|
||||
#include "HTTP.h"
|
||||
#include "Gnutella.h"
|
||||
#include "Event.h"
|
||||
#include "PIA.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
GnutellaMsgState::GnutellaMsgState()
|
||||
{
|
||||
buffer = "";
|
||||
|
|
20
src/ICMP.cc
20
src/ICMP.cc
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "Net.h"
|
||||
|
@ -323,6 +325,24 @@ void ICMP_Echo_Analyzer::NextICMP(double t, const struct icmp* icmpp, int len,
|
|||
ConnectionEvent(f, vl);
|
||||
}
|
||||
|
||||
ICMP_Redir_Analyzer::ICMP_Redir_Analyzer(Connection* c)
|
||||
: ICMP_Analyzer(AnalyzerTag::ICMP_Redir, c)
|
||||
{
|
||||
}
|
||||
|
||||
void ICMP_Redir_Analyzer::NextICMP(double t, const struct icmp* icmpp, int len,
|
||||
int caplen, const u_char*& data)
|
||||
{
|
||||
uint32 addr = ntohl(icmpp->icmp_hun.ih_void);
|
||||
|
||||
val_list* vl = new val_list;
|
||||
vl->append(BuildConnVal());
|
||||
vl->append(BuildICMPVal());
|
||||
vl->append(new AddrVal(htonl(addr)));
|
||||
|
||||
ConnectionEvent(icmp_redirect, vl);
|
||||
}
|
||||
|
||||
|
||||
void ICMP_Context_Analyzer::NextICMP(double t, const struct icmp* icmpp,
|
||||
int len, int caplen, const u_char*& data)
|
||||
|
|
16
src/ICMP.h
16
src/ICMP.h
|
@ -74,6 +74,22 @@ protected:
|
|||
int len, int caplen, const u_char*& data);
|
||||
};
|
||||
|
||||
class ICMP_Redir_Analyzer : public ICMP_Analyzer {
|
||||
public:
|
||||
ICMP_Redir_Analyzer(Connection* conn);
|
||||
|
||||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new ICMP_Redir_Analyzer(conn); }
|
||||
|
||||
static bool Available() { return icmp_redirect; }
|
||||
|
||||
protected:
|
||||
ICMP_Redir_Analyzer() { }
|
||||
|
||||
virtual void NextICMP(double t, const struct icmp* icmpp,
|
||||
int len, int caplen, const u_char*& data);
|
||||
};
|
||||
|
||||
class ICMP_Context_Analyzer : public ICMP_Analyzer {
|
||||
public:
|
||||
ICMP_Context_Analyzer(AnalyzerTag::Tag tag, Connection* conn)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "util.h"
|
||||
#include "IOSource.h"
|
||||
|
||||
|
|
|
@ -1089,7 +1089,7 @@ LogVal** LogMgr::RecordToFilterVals(Stream* stream, Filter* filter,
|
|||
if ( ! val )
|
||||
{
|
||||
// Value, or any of its parents, is not set.
|
||||
vals[i] = new LogVal(type, false);
|
||||
vals[i] = new LogVal(filter->fields[i]->type, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ bool LogWriterAscii::DoWrite(int num_fields, const LogField* const * fields,
|
|||
return false;
|
||||
}
|
||||
|
||||
desc.Add("\n");
|
||||
desc.AddRaw("\n", 1);
|
||||
|
||||
if ( fwrite(desc.Bytes(), desc.Len(), 1, file) != 1 )
|
||||
{
|
||||
|
|
209
src/NFS.cc
209
src/NFS.cc
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "NetVar.h"
|
||||
|
@ -9,16 +11,13 @@
|
|||
#include "NFS.h"
|
||||
#include "Event.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
int NFS_Interp::RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n)
|
||||
{
|
||||
if ( c->Program() != 100003 )
|
||||
Weird(fmt("bad_RPC_program (%d)", c->Program()));
|
||||
|
||||
uint32 proc = c->Proc();
|
||||
// the call arguments, depends on the call type obviously...
|
||||
// The call arguments, depends on the call type obviously ...
|
||||
Val *callarg = 0;
|
||||
|
||||
switch ( proc ) {
|
||||
|
@ -47,15 +46,15 @@ int NFS_Interp::RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n)
|
|||
|
||||
case BifEnum::NFS3::PROC_CREATE:
|
||||
callarg = nfs3_diropargs(buf, n);
|
||||
// TODO: implement create attributes.
|
||||
// For now we just skip over them
|
||||
// TODO: implement create attributes. For now we just skip
|
||||
// over them.
|
||||
n = 0;
|
||||
break;
|
||||
|
||||
case BifEnum::NFS3::PROC_MKDIR:
|
||||
callarg = nfs3_diropargs(buf, n);
|
||||
// TODO: implement mkdir attributes.
|
||||
// For now we just skip over them
|
||||
// TODO: implement mkdir attributes. For now we just skip
|
||||
// over them.
|
||||
n = 0;
|
||||
break;
|
||||
|
||||
|
@ -78,43 +77,47 @@ int NFS_Interp::RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n)
|
|||
default:
|
||||
callarg = 0;
|
||||
if ( proc < BifEnum::NFS3::PROC_END_OF_PROCS )
|
||||
{ // We know the procedure but haven't implemented it
|
||||
n = 0; // otherwise DeliverRPC complains about excess_RPC
|
||||
{
|
||||
// We know the procedure but haven't implemented it.
|
||||
// Otherwise DeliverRPC would complain about
|
||||
// excess_RPC.
|
||||
n = 0;
|
||||
}
|
||||
else
|
||||
else
|
||||
Weird(fmt("unknown_NFS_request(%u)", proc));
|
||||
|
||||
// Return 1 so that replies to unprocessed calls will still
|
||||
// be processed, and the return status extracted
|
||||
// be processed, and the return status extracted.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( !buf )
|
||||
if ( ! buf )
|
||||
{
|
||||
// There was a parse error while trying to extract the call
|
||||
// arguments. However, we don't know where exactly it happened
|
||||
// and whether Vals where already allocated (e.g., a RecordVal
|
||||
// was allocated but we failed to fill it).
|
||||
// So we Unref() the call arguments, and we are fine.
|
||||
// There was a parse error while trying to extract the call
|
||||
// arguments. However, we don't know where exactly it
|
||||
// happened and whether Vals where already allocated (e.g., a
|
||||
// RecordVal was allocated but we failed to fill it). So we
|
||||
// Unref() the call arguments, and we are fine.
|
||||
Unref(callarg);
|
||||
callarg = 0;
|
||||
return 0;
|
||||
}
|
||||
c->AddVal(callarg); // it's save to AddVal(0).
|
||||
|
||||
c->AddVal(callarg); // It's save to AddVal(0).
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int NFS_Interp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status rpc_status,
|
||||
const u_char*& buf, int& n, double start_time, double last_time,
|
||||
int reply_len)
|
||||
const u_char*& buf, int& n, double start_time,
|
||||
double last_time, int reply_len)
|
||||
{
|
||||
EventHandlerPtr event = 0;
|
||||
Val *reply = 0;
|
||||
BifEnum::NFS3::status_t nfs_status = BifEnum::NFS3::NFS3ERR_OK;
|
||||
bool rpc_success = ( rpc_status == BifEnum::RPC_SUCCESS );
|
||||
|
||||
// reply always starts with the NFS status
|
||||
// Reply always starts with the NFS status.
|
||||
if ( rpc_success )
|
||||
{
|
||||
if ( n >= 4 )
|
||||
|
@ -126,17 +129,19 @@ int NFS_Interp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status rpc_status,
|
|||
if ( nfs_reply_status )
|
||||
{
|
||||
val_list* vl = event_common_vl(c, rpc_status, nfs_status,
|
||||
start_time, last_time, reply_len);
|
||||
start_time, last_time, reply_len);
|
||||
analyzer->ConnectionEvent(nfs_reply_status, vl);
|
||||
}
|
||||
|
||||
if (!rpc_success)
|
||||
if ( ! rpc_success )
|
||||
{
|
||||
// We set the buffer to NULL, the function that extract the reply
|
||||
// from the data stream will then return empty records.
|
||||
// We set the buffer to NULL, the function that extract the
|
||||
// reply from the data stream will then return empty records.
|
||||
//
|
||||
buf = NULL;
|
||||
n = 0;
|
||||
}
|
||||
|
||||
switch ( c->Proc() ) {
|
||||
case BifEnum::NFS3::PROC_NULL:
|
||||
event = nfs_proc_null;
|
||||
|
@ -153,11 +158,9 @@ int NFS_Interp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status rpc_status,
|
|||
break;
|
||||
|
||||
case BifEnum::NFS3::PROC_READ:
|
||||
{
|
||||
bro_uint_t offset;
|
||||
offset = c->RequestVal()->AsRecordVal()->Lookup(1)->AsCount();
|
||||
reply = nfs3_read_reply(buf, n, nfs_status, offset);
|
||||
}
|
||||
event = nfs_proc_read;
|
||||
break;
|
||||
|
||||
|
@ -205,8 +208,11 @@ int NFS_Interp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status rpc_status,
|
|||
|
||||
default:
|
||||
if ( c->Proc() < BifEnum::NFS3::PROC_END_OF_PROCS )
|
||||
{ // We know the procedure but haven't implemented it
|
||||
n = 0; // otherwise DeliverRPC complains about excess_RPC
|
||||
{
|
||||
// We know the procedure but haven't implemented it.
|
||||
// Otherwise DeliverRPC would complain about
|
||||
// excess_RPC.
|
||||
n = 0;
|
||||
reply = new EnumVal(c->Proc(), BifType::Enum::NFS3::proc_t);
|
||||
event = nfs_proc_not_implemented;
|
||||
}
|
||||
|
@ -214,31 +220,37 @@ int NFS_Interp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status rpc_status,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (rpc_success && !buf)
|
||||
if ( rpc_success && ! buf )
|
||||
{
|
||||
// There was a parse error. We have to unref the reply.
|
||||
// (see also comments in RPC_BuildCall
|
||||
// There was a parse error. We have to unref the reply. (see
|
||||
// also comments in RPC_BuildCall.
|
||||
Unref(reply);
|
||||
reply = 0;
|
||||
return 0;
|
||||
}
|
||||
// Note: if reply == 0, it won't be added to the val_list for the event.
|
||||
// While we can check for that on the policy layer it's kinda ugly, because
|
||||
// it's contrary to the event prototype. But having this optional argument to
|
||||
// the event is really helpful.... Otherwise I have to let reply
|
||||
// point to a RecordVal where all fields are optional and all are set
|
||||
// to 0...
|
||||
if (event)
|
||||
|
||||
// Note: if reply == 0, it won't be added to the val_list for the
|
||||
// event. While we can check for that on the policy layer it's kinda
|
||||
// ugly, because it's contrary to the event prototype. But having
|
||||
// this optional argument to the event is really helpful. Otherwise I
|
||||
// have to let reply point to a RecordVal where all fields are
|
||||
// optional and all are set to 0 ...
|
||||
if ( event )
|
||||
{
|
||||
val_list* vl = event_common_vl(c, rpc_status, nfs_status,
|
||||
start_time, last_time, reply_len);
|
||||
|
||||
Val *request = c->TakeRequestVal();
|
||||
|
||||
if ( request )
|
||||
vl->append(request);
|
||||
|
||||
if ( reply )
|
||||
vl->append(reply);
|
||||
|
||||
analyzer->ConnectionEvent(event, vl);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -247,33 +259,38 @@ StringVal* NFS_Interp::nfs3_file_data(const u_char*& buf, int& n, uint64_t offse
|
|||
int data_n;
|
||||
|
||||
// extract the data, move buf and n
|
||||
const u_char *data = extract_XDR_opaque(buf, n, data_n, 1<<30, true);
|
||||
const u_char *data = extract_XDR_opaque(buf, n, data_n, 1 << 30, true);
|
||||
|
||||
// check whether we have to deliver data to the event
|
||||
if (!BifConst::NFS3::return_data)
|
||||
// check whether we have to deliver data to the event
|
||||
if ( ! BifConst::NFS3::return_data )
|
||||
return 0;
|
||||
if (BifConst::NFS3::return_data_first_only && offset!=0)
|
||||
|
||||
if ( BifConst::NFS3::return_data_first_only && offset != 0 )
|
||||
return 0;
|
||||
|
||||
// Ok, so we want to return some data
|
||||
data_n = min(data_n, size);
|
||||
data_n = min(data_n, int(BifConst::NFS3::return_data_max));
|
||||
if (data_n>0)
|
||||
|
||||
if ( data_n > 0 )
|
||||
return new StringVal(new BroString(data, data_n, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
val_list* NFS_Interp::event_common_vl(RPC_CallInfo *c, BifEnum::rpc_status rpc_status,
|
||||
BifEnum::NFS3::status_t nfs_status, double rep_start_time, double rep_last_time,
|
||||
int reply_len)
|
||||
val_list* NFS_Interp::event_common_vl(RPC_CallInfo *c, BifEnum::rpc_status rpc_status,
|
||||
BifEnum::NFS3::status_t nfs_status,
|
||||
double rep_start_time,
|
||||
double rep_last_time, int reply_len)
|
||||
{
|
||||
// returns a new val_list that already has a conn_val, and nfs3_info
|
||||
// These are the first parameters for each nfs_* event...
|
||||
// Returns a new val_list that already has a conn_val, and nfs3_info.
|
||||
// These are the first parameters for each nfs_* event ...
|
||||
val_list *vl = new val_list;
|
||||
vl->append(analyzer->BuildConnVal());
|
||||
|
||||
RecordVal *info = new RecordVal(BifType::Record::NFS3::info_t);
|
||||
info->Assign(0, new EnumVal(rpc_status, BifType::Enum::rpc_status));
|
||||
info->Assign(1, new EnumVal(nfs_status, BifType::Enum::NFS3::status_t));
|
||||
info->Assign(0, new EnumVal(rpc_status, BifType::Enum::rpc_status));
|
||||
info->Assign(1, new EnumVal(nfs_status, BifType::Enum::NFS3::status_t));
|
||||
info->Assign(2, new Val(c->StartTime(), TYPE_TIME));
|
||||
info->Assign(3, new Val(c->LastTime()-c->StartTime(), TYPE_INTERVAL));
|
||||
info->Assign(4, new Val(c->RPCLen(), TYPE_COUNT));
|
||||
|
@ -299,6 +316,7 @@ StringVal* NFS_Interp::nfs3_fh(const u_char*& buf, int& n)
|
|||
RecordVal* NFS_Interp::nfs3_fattr(const u_char*& buf, int& n)
|
||||
{
|
||||
RecordVal* attrs = new RecordVal(BifType::Record::NFS3::fattr_t);
|
||||
|
||||
attrs->Assign(0, nfs3_ftype(buf, n)); // file type
|
||||
attrs->Assign(1, ExtractUint32(buf, n)); // mode
|
||||
attrs->Assign(2, ExtractUint32(buf, n)); // nlink
|
||||
|
@ -317,7 +335,7 @@ RecordVal* NFS_Interp::nfs3_fattr(const u_char*& buf, int& n)
|
|||
return attrs;
|
||||
}
|
||||
|
||||
EnumVal* NFS_Interp::nfs3_ftype(const u_char*& buf, int& n)
|
||||
EnumVal* NFS_Interp::nfs3_ftype(const u_char*& buf, int& n)
|
||||
{
|
||||
BifEnum::NFS3::file_type_t t = (BifEnum::NFS3::file_type_t)extract_XDR_uint32(buf, n);
|
||||
return new EnumVal(t, BifType::Enum::NFS3::file_type_t);
|
||||
|
@ -326,6 +344,7 @@ EnumVal* NFS_Interp::nfs3_ftype(const u_char*& buf, int& n)
|
|||
RecordVal* NFS_Interp::nfs3_wcc_attr(const u_char*& buf, int& n)
|
||||
{
|
||||
RecordVal* attrs = new RecordVal(BifType::Record::NFS3::wcc_attr_t);
|
||||
|
||||
attrs->Assign(0, ExtractUint64(buf, n)); // size
|
||||
attrs->Assign(1, ExtractTime(buf, n)); // mtime
|
||||
attrs->Assign(2, ExtractTime(buf, n)); // ctime
|
||||
|
@ -333,18 +352,21 @@ RecordVal* NFS_Interp::nfs3_wcc_attr(const u_char*& buf, int& n)
|
|||
return attrs;
|
||||
}
|
||||
|
||||
StringVal *NFS_Interp::nfs3_filename(const u_char*& buf, int& n)
|
||||
StringVal *NFS_Interp::nfs3_filename(const u_char*& buf, int& n)
|
||||
{
|
||||
int name_len;
|
||||
const u_char* name = extract_XDR_opaque(buf, n, name_len);
|
||||
if ( !name )
|
||||
|
||||
if ( ! name )
|
||||
return 0;
|
||||
|
||||
return new StringVal(new BroString(name, name_len, 0));
|
||||
}
|
||||
|
||||
RecordVal *NFS_Interp::nfs3_diropargs(const u_char*& buf, int& n)
|
||||
{
|
||||
RecordVal *diropargs = new RecordVal(BifType::Record::NFS3::diropargs_t);
|
||||
|
||||
diropargs->Assign(0, nfs3_fh(buf, n));
|
||||
diropargs->Assign(1, nfs3_filename(buf, n));
|
||||
|
||||
|
@ -358,6 +380,7 @@ RecordVal* NFS_Interp::nfs3_post_op_attr(const u_char*& buf, int& n)
|
|||
|
||||
if ( have_attrs )
|
||||
return nfs3_fattr(buf, n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -367,6 +390,7 @@ StringVal* NFS_Interp::nfs3_post_op_fh(const u_char*& buf, int& n)
|
|||
|
||||
if ( have_fh )
|
||||
return nfs3_fh(buf, n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -379,7 +403,7 @@ RecordVal* NFS_Interp::nfs3_pre_op_attr(const u_char*& buf, int& n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
EnumVal *NFS_Interp::nfs3_stable_how(const u_char*& buf, int& n)
|
||||
EnumVal *NFS_Interp::nfs3_stable_how(const u_char*& buf, int& n)
|
||||
{
|
||||
BifEnum::NFS3::stable_how_t stable = (BifEnum::NFS3::stable_how_t)extract_XDR_uint32(buf, n);
|
||||
return new EnumVal(stable, BifType::Enum::NFS3::stable_how_t);
|
||||
|
@ -388,7 +412,8 @@ EnumVal *NFS_Interp::nfs3_stable_how(const u_char*& buf, int& n)
|
|||
RecordVal* NFS_Interp::nfs3_lookup_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status)
|
||||
{
|
||||
RecordVal *rep = new RecordVal(BifType::Record::NFS3::lookup_reply_t);
|
||||
if (status == BifEnum::NFS3::NFS3ERR_OK)
|
||||
|
||||
if ( status == BifEnum::NFS3::NFS3ERR_OK )
|
||||
{
|
||||
rep->Assign(0, nfs3_fh(buf,n));
|
||||
rep->Assign(1, nfs3_post_op_attr(buf, n));
|
||||
|
@ -406,16 +431,19 @@ RecordVal* NFS_Interp::nfs3_lookup_reply(const u_char*& buf, int& n, BifEnum::NF
|
|||
RecordVal *NFS_Interp::nfs3_readargs(const u_char*& buf, int& n)
|
||||
{
|
||||
RecordVal *readargs = new RecordVal(BifType::Record::NFS3::readargs_t);
|
||||
|
||||
readargs->Assign(0, nfs3_fh(buf, n));
|
||||
readargs->Assign(1, ExtractUint64(buf, n)); // offset
|
||||
readargs->Assign(2, ExtractUint32(buf,n)); // size
|
||||
readargs->Assign(2, ExtractUint32(buf,n)); // size
|
||||
|
||||
return readargs;
|
||||
}
|
||||
|
||||
RecordVal* NFS_Interp::nfs3_read_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status,
|
||||
RecordVal* NFS_Interp::nfs3_read_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status,
|
||||
bro_uint_t offset)
|
||||
{
|
||||
RecordVal *rep = new RecordVal(BifType::Record::NFS3::read_reply_t);
|
||||
|
||||
if (status == BifEnum::NFS3::NFS3ERR_OK)
|
||||
{
|
||||
uint32_t bytes_read;
|
||||
|
@ -430,12 +458,14 @@ RecordVal* NFS_Interp::nfs3_read_reply(const u_char*& buf, int& n, BifEnum::NFS3
|
|||
{
|
||||
rep->Assign(0, nfs3_post_op_attr(buf, n));
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
RecordVal* NFS_Interp::nfs3_readlink_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status)
|
||||
{
|
||||
RecordVal *rep = new RecordVal(BifType::Record::NFS3::readlink_reply_t);
|
||||
|
||||
if (status == BifEnum::NFS3::NFS3ERR_OK)
|
||||
{
|
||||
rep->Assign(0, nfs3_post_op_attr(buf, n));
|
||||
|
@ -445,6 +475,7 @@ RecordVal* NFS_Interp::nfs3_readlink_reply(const u_char*& buf, int& n, BifEnum::
|
|||
{
|
||||
rep->Assign(0, nfs3_post_op_attr(buf, n));
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
@ -453,40 +484,48 @@ RecordVal *NFS_Interp::nfs3_writeargs(const u_char*& buf, int& n)
|
|||
uint32_t bytes;
|
||||
uint64_t offset;
|
||||
RecordVal *writeargs = new RecordVal(BifType::Record::NFS3::writeargs_t);
|
||||
writeargs->Assign(0, nfs3_fh(buf, n));
|
||||
|
||||
offset = extract_XDR_uint64(buf, n);
|
||||
bytes = extract_XDR_uint32(buf, n);
|
||||
|
||||
writeargs->Assign(0, nfs3_fh(buf, n));
|
||||
writeargs->Assign(1, new Val(offset, TYPE_COUNT));
|
||||
writeargs->Assign(2, new Val(bytes, TYPE_COUNT));
|
||||
writeargs->Assign(3, nfs3_stable_how(buf, n));
|
||||
writeargs->Assign(4, nfs3_file_data(buf, n, offset, bytes));
|
||||
|
||||
return writeargs;
|
||||
}
|
||||
|
||||
RecordVal *NFS_Interp::nfs3_write_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status)
|
||||
{
|
||||
RecordVal *rep = new RecordVal(BifType::Record::NFS3::write_reply_t);
|
||||
if (status == BifEnum::NFS3::NFS3ERR_OK)
|
||||
|
||||
if ( status == BifEnum::NFS3::NFS3ERR_OK )
|
||||
{
|
||||
rep->Assign(0, nfs3_pre_op_attr(buf, n));
|
||||
rep->Assign(1, nfs3_post_op_attr(buf, n));
|
||||
rep->Assign(2, ExtractUint32(buf, n));
|
||||
rep->Assign(3, nfs3_stable_how(buf, n));
|
||||
rep->Assign(4, ExtractUint64(buf, n)); // Writeverf. While the RFC says that this
|
||||
// should be a fixed length opaque, it specifies the lenght as 8 bytes, so we can
|
||||
// also just as easily extract a uint64
|
||||
|
||||
// Writeverf. While the RFC says that this should be a fixed
|
||||
// length opaque, it specifies the lenght as 8 bytes, so we
|
||||
// can also just as easily extract a uint64.
|
||||
rep->Assign(4, ExtractUint64(buf, n));
|
||||
}
|
||||
else
|
||||
{
|
||||
rep->Assign(0, nfs3_post_op_attr(buf, n));
|
||||
rep->Assign(1, nfs3_pre_op_attr(buf, n));
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
RecordVal* NFS_Interp::nfs3_newobj_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status)
|
||||
{
|
||||
RecordVal *rep = new RecordVal(BifType::Record::NFS3::newobj_reply_t);
|
||||
|
||||
if (status == BifEnum::NFS3::NFS3ERR_OK)
|
||||
{
|
||||
int i = 0;
|
||||
|
@ -503,28 +542,34 @@ RecordVal* NFS_Interp::nfs3_newobj_reply(const u_char*& buf, int& n, BifEnum::NF
|
|||
rep->Assign(2, nfs3_pre_op_attr(buf, n));
|
||||
rep->Assign(3, nfs3_post_op_attr(buf, n));
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
RecordVal* NFS_Interp::nfs3_delobj_reply(const u_char*& buf, int& n)
|
||||
{
|
||||
RecordVal *rep = new RecordVal(BifType::Record::NFS3::delobj_reply_t);
|
||||
|
||||
// wcc_data
|
||||
rep->Assign(0, nfs3_pre_op_attr(buf, n));
|
||||
rep->Assign(1, nfs3_post_op_attr(buf, n));
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
RecordVal* NFS_Interp::nfs3_readdirargs(bool isplus, const u_char*& buf, int&n)
|
||||
RecordVal* NFS_Interp::nfs3_readdirargs(bool isplus, const u_char*& buf, int&n)
|
||||
{
|
||||
RecordVal *args = new RecordVal(BifType::Record::NFS3::readdirargs_t);
|
||||
|
||||
args->Assign(0, new Val(isplus, TYPE_BOOL));
|
||||
args->Assign(1, nfs3_fh(buf, n));
|
||||
args->Assign(2, ExtractUint64(buf,n)); // cookie
|
||||
args->Assign(3, ExtractUint64(buf,n)); // cookieverf
|
||||
args->Assign(4, ExtractUint32(buf,n)); // dircount
|
||||
if (isplus)
|
||||
args->Assign(2, ExtractUint64(buf,n)); // cookie
|
||||
args->Assign(3, ExtractUint64(buf,n)); // cookieverf
|
||||
args->Assign(4, ExtractUint32(buf,n)); // dircount
|
||||
|
||||
if ( isplus )
|
||||
args->Assign(5, ExtractUint32(buf,n));
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
|
@ -534,29 +579,36 @@ RecordVal* NFS_Interp::nfs3_readdir_reply(bool isplus, const u_char*& buf,
|
|||
RecordVal *rep = new RecordVal(BifType::Record::NFS3::readdir_reply_t);
|
||||
|
||||
rep->Assign(0, new Val(isplus, TYPE_BOOL));
|
||||
if (status == BifEnum::NFS3::NFS3ERR_OK)
|
||||
|
||||
if ( status == BifEnum::NFS3::NFS3ERR_OK )
|
||||
{
|
||||
VectorVal *entries = new VectorVal(BifType::Vector::NFS3::direntry_vec_t);
|
||||
unsigned pos;
|
||||
VectorVal *entries = new VectorVal(BifType::Vector::NFS3::direntry_vec_t);
|
||||
|
||||
rep->Assign(1, nfs3_post_op_attr(buf,n)); // dir_attr
|
||||
rep->Assign(2, ExtractUint64(buf,n)); // cookieverf
|
||||
|
||||
pos = 1;
|
||||
while ( extract_XDR_uint32(buf,n) )
|
||||
|
||||
while ( extract_XDR_uint32(buf,n) )
|
||||
{
|
||||
RecordVal *entry = new RecordVal(BifType::Record::NFS3::direntry_t);
|
||||
entry->Assign(0, ExtractUint64(buf,n)); // fileid
|
||||
entry->Assign(0, ExtractUint64(buf,n)); // fileid
|
||||
entry->Assign(1, nfs3_filename(buf,n)); // fname
|
||||
entry->Assign(2, ExtractUint64(buf,n)); // cookie
|
||||
if (isplus)
|
||||
entry->Assign(2, ExtractUint64(buf,n)); // cookie
|
||||
|
||||
if ( isplus )
|
||||
{
|
||||
entry->Assign(3, nfs3_post_op_attr(buf,n));
|
||||
entry->Assign(4, nfs3_post_op_fh(buf,n));
|
||||
}
|
||||
|
||||
entries->Assign(pos, entry, 0);
|
||||
pos++;
|
||||
}
|
||||
|
||||
rep->Assign(3, entries);
|
||||
rep->Assign(4, ExtractBool(buf,n)); // eof
|
||||
rep->Assign(4, ExtractBool(buf,n)); // eof
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -565,6 +617,9 @@ RecordVal* NFS_Interp::nfs3_readdir_reply(bool isplus, const u_char*& buf,
|
|||
return rep;
|
||||
}
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
Val* NFS_Interp::ExtractUint32(const u_char*& buf, int& n)
|
||||
{
|
||||
return new Val(extract_XDR_uint32(buf, n), TYPE_COUNT);
|
||||
|
@ -592,7 +647,7 @@ Val* NFS_Interp::ExtractBool(const u_char*& buf, int& n)
|
|||
|
||||
|
||||
NFS_Analyzer::NFS_Analyzer(Connection* conn)
|
||||
: RPC_Analyzer(AnalyzerTag::NFS, conn, new NFS_Interp(this))
|
||||
: RPC_Analyzer(AnalyzerTag::NFS, conn, new NFS_Interp(this))
|
||||
{
|
||||
orig_rpc = resp_rpc = 0;
|
||||
}
|
||||
|
|
97
src/NFS.h
97
src/NFS.h
|
@ -16,31 +16,35 @@ public:
|
|||
protected:
|
||||
int RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n);
|
||||
int RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status rpc_status,
|
||||
const u_char*& buf, int& n, double start_time, double last_time,
|
||||
const u_char*& buf, int& n, double start_time,
|
||||
double last_time, int reply_len);
|
||||
|
||||
// Returns a new val_list that already has a conn_val, rpc_status and
|
||||
// nfs_status. These are the first parameters for each nfs_* event
|
||||
// ...
|
||||
val_list* event_common_vl(RPC_CallInfo *c, BifEnum::rpc_status rpc_status,
|
||||
BifEnum::NFS3::status_t nfs_status,
|
||||
double rep_start_time, double rep_last_time,
|
||||
int reply_len);
|
||||
|
||||
// returns a new val_list that already has a conn_val, rpc_status and nfs_status.
|
||||
// These are the first parameters for each nfs_* event...
|
||||
val_list* event_common_vl(RPC_CallInfo *c, BifEnum::rpc_status rpc_status,
|
||||
BifEnum::NFS3::status_t nfs_status, double rep_start_time, double rep_last_time,
|
||||
int reply_len);
|
||||
|
||||
|
||||
// These methods parse the appropriate NFSv3 "type" out of buf. If
|
||||
// there are any errors (i.e., buffer to short, etc), buf will be
|
||||
// set to 0. However, the methods might still return an allocated
|
||||
// Val * !
|
||||
// So, you might want to Unref() the Val if buf is 0.
|
||||
// Method names are based on the type names of RFC 1813
|
||||
// These methods parse the appropriate NFSv3 "type" out of buf. If
|
||||
// there are any errors (i.e., buffer to short, etc), buf will be set
|
||||
// to 0. However, the methods might still return an allocated Val * !
|
||||
// So, you might want to Unref() the Val if buf is 0. Method names
|
||||
// are based on the type names of RFC 1813.
|
||||
StringVal* nfs3_fh(const u_char*& buf, int& n);
|
||||
RecordVal* nfs3_fattr(const u_char*& buf, int& n);
|
||||
EnumVal* nfs3_ftype(const u_char*& buf, int& n);
|
||||
RecordVal* nfs3_wcc_attr(const u_char*& buf, int& n);
|
||||
RecordVal* nfs3_diropargs(const u_char*&buf, int &n);
|
||||
StringVal* nfs3_filename(const u_char*& buf, int& n);
|
||||
StringVal* nfs3_nfspath(const u_char*& buf, int& n) { return nfs3_filename(buf,n); }
|
||||
RecordVal* nfs3_post_op_attr(const u_char*&buf, int &n); // Return 0 or an fattr
|
||||
RecordVal* nfs3_pre_op_attr(const u_char*&buf, int &n); // Return 0 or an wcc_attr
|
||||
StringVal* nfs3_nfspath(const u_char*& buf, int& n)
|
||||
{
|
||||
return nfs3_filename(buf,n);
|
||||
}
|
||||
|
||||
RecordVal* nfs3_post_op_attr(const u_char*&buf, int &n); // Return 0 or an fattr
|
||||
RecordVal* nfs3_pre_op_attr(const u_char*&buf, int &n); // Return 0 or an wcc_attr
|
||||
RecordVal* nfs3_lookup_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status);
|
||||
RecordVal* nfs3_readargs(const u_char*& buf, int& n);
|
||||
RecordVal* nfs3_read_reply(const u_char*& buf, int& n, BifEnum::NFS3::status_t status, bro_uint_t offset);
|
||||
|
@ -53,14 +57,13 @@ protected:
|
|||
StringVal* nfs3_post_op_fh(const u_char*& buf, int& n);
|
||||
RecordVal* nfs3_readdirargs(bool isplus, const u_char*& buf, int&n);
|
||||
RecordVal* nfs3_readdir_reply(bool isplus, const u_char*& buf, int&n, BifEnum::NFS3::status_t status);
|
||||
// consumes the file data in the RPC message. Depending on NFS::return_data* consts
|
||||
// in bro.init returns NULL or the data as string val
|
||||
|
||||
// Consumes the file data in the RPC message. Depending on NFS::return_data* consts
|
||||
// in bro.init returns NULL or the data as string val:
|
||||
// * offset is the offset of the read/write call
|
||||
// * size is the amount of bytes read (or requested to be written),
|
||||
StringVal* nfs3_file_data(const u_char*& buf, int& n, uint64_t offset, int size);
|
||||
|
||||
|
||||
|
||||
RecordVal* ExtractOptAttrs(const u_char*& buf, int& n);
|
||||
Val* ExtractUint32(const u_char*& buf, int& n);
|
||||
Val* ExtractUint64(const u_char*& buf, int& n);
|
||||
|
@ -77,50 +80,18 @@ public:
|
|||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new NFS_Analyzer(conn); }
|
||||
|
||||
//static bool Available() { return nfs_request_getattr || rpc_call; }
|
||||
static bool Available() { return true; }
|
||||
};
|
||||
|
||||
#if 0
|
||||
namespace nfs3_types {
|
||||
#define NFS3_MAX_FHSIZE 64
|
||||
class nfs3_type {
|
||||
public:
|
||||
//nfs3_type(const u_char*&buf, int& len) = 0;
|
||||
virtual ~nfs3_type()
|
||||
{
|
||||
}
|
||||
virtual Val *GetVal() = 0;
|
||||
bool IsValid() { return valid; };
|
||||
|
||||
bool valid;
|
||||
};
|
||||
|
||||
// A file handle
|
||||
class nfs3_fh : public nfs3_type {
|
||||
public:
|
||||
nfs3_fh(const u_char*&buf, int& len) {
|
||||
const u_char *fh_tmp;
|
||||
int fh_len;
|
||||
valid = false;
|
||||
fh_tmp = extract_XDR_opaque(buf,len,fh_len,NFS3_MAX_FHSIZE);
|
||||
if (fh_tmp) {
|
||||
fh = new StringVal(new BroString(fh, fh_len, 0));
|
||||
valid = true;
|
||||
}
|
||||
else
|
||||
fh = 0;
|
||||
static bool Available()
|
||||
{
|
||||
return ( nfs_proc_null || nfs_proc_not_implemented || nfs_proc_getattr ||
|
||||
nfs_proc_lookup || nfs_proc_read || nfs_proc_readlink ||
|
||||
nfs_proc_write || nfs_proc_create || nfs_proc_mkdir ||
|
||||
nfs_proc_remove || nfs_proc_rmdir || nfs_proc_readdir ||
|
||||
nfs_reply_status ||
|
||||
rpc_dialogue || rpc_call || rpc_reply );
|
||||
}
|
||||
|
||||
~nfs3_fh() { printf("~nfs3_fh\n"); }
|
||||
|
||||
Val *GetVal() { return fh; }
|
||||
|
||||
// Data
|
||||
StringVal *fh;
|
||||
}; // nfs3_fh
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,7 @@ RecordType* pcap_packet;
|
|||
RecordType* signature_state;
|
||||
EnumType* transport_proto;
|
||||
TableType* string_set;
|
||||
TableType* count_set;
|
||||
|
||||
RecordType* net_stats;
|
||||
|
||||
|
@ -195,8 +196,6 @@ StringVal* ssl_private_key;
|
|||
StringVal* ssl_passphrase;
|
||||
|
||||
StringVal* x509_crl_file;
|
||||
TableType* x509_extension;
|
||||
TableType* SSL_sessionID;
|
||||
|
||||
Val* profiling_file;
|
||||
double profiling_interval;
|
||||
|
@ -367,10 +366,7 @@ void init_net_var()
|
|||
x509_trusted_cert_path = opt_internal_string("X509_trusted_cert_path");
|
||||
ssl_store_cert_path = opt_internal_string("ssl_store_cert_path");
|
||||
x509_type = internal_type("X509")->AsRecordType();
|
||||
cipher_suites_list = internal_type("cipher_suites_list")->AsTableType();
|
||||
x509_crl_file = opt_internal_string("X509_crl_file");
|
||||
x509_extension = internal_type("X509_extension")->AsTableType();
|
||||
SSL_sessionID = internal_type("SSL_sessionID")->AsTableType();
|
||||
|
||||
non_analyzed_lifetime = opt_internal_double("non_analyzed_lifetime");
|
||||
tcp_inactivity_timeout = opt_internal_double("tcp_inactivity_timeout");
|
||||
|
|
|
@ -21,6 +21,7 @@ extern RecordType* SYN_packet;
|
|||
extern RecordType* pcap_packet;
|
||||
extern EnumType* transport_proto;
|
||||
extern TableType* string_set;
|
||||
extern TableType* count_set;
|
||||
|
||||
extern RecordType* net_stats;
|
||||
|
||||
|
@ -61,11 +62,8 @@ extern int ssl_store_key_material;
|
|||
extern int ssl_max_cipherspec_size;
|
||||
extern StringVal* ssl_store_cert_path;
|
||||
extern StringVal* x509_trusted_cert_path;
|
||||
extern TableType* cipher_suites_list;
|
||||
extern RecordType* x509_type;
|
||||
extern StringVal* x509_crl_file;
|
||||
extern TableType* x509_extension;
|
||||
extern TableType* SSL_sessionID;
|
||||
|
||||
extern double non_analyzed_lifetime;
|
||||
extern double tcp_inactivity_timeout;
|
||||
|
|
|
@ -295,7 +295,7 @@ void OSFingerprint::load_config(const char* file)
|
|||
uint32 ln=0;
|
||||
char buf[MAXLINE];
|
||||
char* p;
|
||||
FILE* c = search_for_file( file, "osf", 0);
|
||||
FILE* c = search_for_file( file, "osf", 0, false);
|
||||
|
||||
if (!c)
|
||||
{
|
||||
|
|
|
@ -576,9 +576,11 @@ void POP3_Analyzer::ProcessReply(int length, const char* line)
|
|||
if ( multiLine == true )
|
||||
{
|
||||
bool terminator =
|
||||
length > 1 && line[0] == '.' &&
|
||||
(line[1] == '\n' ||
|
||||
(length > 2 && line[1] == '\r' && line[2] == '\n'));
|
||||
line[0] == '.' &&
|
||||
(length == 1 ||
|
||||
(length > 1 &&
|
||||
(line[1] == '\n' ||
|
||||
(length > 2 && line[1] == '\r' && line[2] == '\n'))));
|
||||
|
||||
if ( terminator )
|
||||
{
|
||||
|
|
|
@ -72,12 +72,13 @@ int PortmapperInterp::RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n)
|
|||
}
|
||||
|
||||
int PortmapperInterp::RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status status,
|
||||
const u_char*& buf, int& n, double start_time, double last_time,
|
||||
int reply_len)
|
||||
const u_char*& buf, int& n,
|
||||
double start_time, double last_time,
|
||||
int reply_len)
|
||||
{
|
||||
EventHandlerPtr event;
|
||||
Val *reply = 0;
|
||||
int success = status == BifEnum::RPC_SUCCESS;
|
||||
int success = (status == BifEnum::RPC_SUCCESS);
|
||||
|
||||
|
||||
switch ( c->Proc() ) {
|
||||
|
@ -283,6 +284,7 @@ void PortmapperInterp::Event(EventHandlerPtr f, Val* request, BifEnum::rpc_statu
|
|||
val_list* vl = new val_list;
|
||||
|
||||
vl->append(analyzer->BuildConnVal());
|
||||
|
||||
if ( status == BifEnum::RPC_SUCCESS )
|
||||
{
|
||||
if ( request )
|
||||
|
|
|
@ -14,8 +14,8 @@ public:
|
|||
protected:
|
||||
int RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n);
|
||||
int RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status success,
|
||||
const u_char*& buf, int& n, double start_time, double last_time,
|
||||
int reply_len);
|
||||
const u_char*& buf, int& n, double start_time,
|
||||
double last_time, int reply_len);
|
||||
uint32 CheckPort(uint32 port);
|
||||
|
||||
void Event(EventHandlerPtr f, Val* request, BifEnum::rpc_status status, Val* reply);
|
||||
|
|
242
src/RPC.cc
242
src/RPC.cc
|
@ -2,10 +2,12 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "NetVar.h"
|
||||
#include "XDR.h"
|
||||
#include "RPC.h"
|
||||
|
@ -83,11 +85,12 @@ RPC_Interpreter::~RPC_Interpreter()
|
|||
{
|
||||
}
|
||||
|
||||
int RPC_Interpreter::DeliverRPC(const u_char* buf, int n, int rpclen, int is_orig, double start_time, double last_time)
|
||||
int RPC_Interpreter::DeliverRPC(const u_char* buf, int n, int rpclen,
|
||||
int is_orig, double start_time, double last_time)
|
||||
{
|
||||
uint32 xid = extract_XDR_uint32(buf, n);
|
||||
uint32 msg_type = extract_XDR_uint32(buf, n);
|
||||
int rpc_len = n;
|
||||
int rpc_len = n;
|
||||
|
||||
if ( ! buf )
|
||||
return 0;
|
||||
|
@ -107,9 +110,14 @@ int RPC_Interpreter::DeliverRPC(const u_char* buf, int n, int rpclen, int is_ori
|
|||
call->SetStartTime(start_time);
|
||||
call->SetLastTime(last_time);
|
||||
|
||||
// TODO: Should we update start_time and last_time or
|
||||
// not??
|
||||
call->SetStartTime(start_time);
|
||||
call->SetLastTime(last_time);
|
||||
|
||||
// TODO: Not sure whether the handling if rexmit
|
||||
// inconsistencies are correct. Maybe we should use the info in the new
|
||||
// call for further processing.
|
||||
// inconsistencies are correct. Maybe we should use
|
||||
// the info in the new call for further processing.
|
||||
if ( call->HeaderLen() > n )
|
||||
{
|
||||
Weird("RPC_underflow");
|
||||
|
@ -137,6 +145,11 @@ int RPC_Interpreter::DeliverRPC(const u_char* buf, int n, int rpclen, int is_ori
|
|||
// TODO: What to do in case of a rexmit_inconistency??
|
||||
Event_RPC_Call(call);
|
||||
|
||||
// We now have a valid RPC_CallInfo (either the previous one
|
||||
// in case of a rexmit or the current one).
|
||||
// TODO: What to do in case of a rexmit_inconistency??
|
||||
Event_RPC_Call(call);
|
||||
|
||||
if ( RPC_BuildCall(call, buf, n) )
|
||||
call->SetValidCall();
|
||||
else
|
||||
|
@ -217,10 +230,9 @@ int RPC_Interpreter::DeliverRPC(const u_char* buf, int n, int rpclen, int is_ori
|
|||
else
|
||||
Weird("bad_RPC");
|
||||
|
||||
// We now have extracted the status we want to use.
|
||||
// We now have extracted the status we want to use.
|
||||
Event_RPC_Reply(xid, status, n);
|
||||
|
||||
|
||||
if ( call )
|
||||
{
|
||||
if ( ! call->IsValidCall() )
|
||||
|
@ -277,10 +289,12 @@ void RPC_Interpreter::Timeout()
|
|||
while ( (c = calls.NextEntry(cookie)) )
|
||||
{
|
||||
Event_RPC_Dialogue(c, BifEnum::RPC_TIMEOUT, 0);
|
||||
|
||||
if ( c->IsValidCall() )
|
||||
{
|
||||
const u_char* buf;
|
||||
int n = 0;
|
||||
|
||||
if ( ! RPC_BuildReply(c, BifEnum::RPC_TIMEOUT, buf, n, network_time, network_time, 0) )
|
||||
Weird("bad_RPC");
|
||||
}
|
||||
|
@ -339,7 +353,7 @@ void RPC_Interpreter::Weird(const char* msg)
|
|||
|
||||
|
||||
void RPC_Reasm_Buffer::Init(int64_t arg_maxsize, int64_t arg_expected) {
|
||||
if (buf)
|
||||
if ( buf )
|
||||
delete [] buf;
|
||||
expected = arg_expected;
|
||||
maxsize = arg_maxsize;
|
||||
|
@ -347,23 +361,25 @@ void RPC_Reasm_Buffer::Init(int64_t arg_maxsize, int64_t arg_expected) {
|
|||
buf = new u_char[maxsize];
|
||||
};
|
||||
|
||||
bool RPC_Reasm_Buffer::ConsumeChunk(const u_char*& data, int& len)
|
||||
bool RPC_Reasm_Buffer::ConsumeChunk(const u_char*& data, int& len)
|
||||
{
|
||||
// How many bytes to we want to process with this call?
|
||||
// Either the all of the bytes available or the number of bytes
|
||||
// that we are still missing
|
||||
int64_t to_process = min( int64_t(len), (expected-processed) );
|
||||
// How many bytes do we want to process with this call? Either the
|
||||
// all of the bytes available or the number of bytes that we are
|
||||
// still missing.
|
||||
int64_t to_process = min(int64_t(len), (expected-processed));
|
||||
|
||||
if (fill < maxsize)
|
||||
if ( fill < maxsize )
|
||||
{
|
||||
// We haven't yet filled the buffer.
|
||||
// How many bytes to copy into the buff. Either all of the bytes
|
||||
// we want to process or the number of bytes until we reach maxsize
|
||||
int64_t to_copy = min( to_process, (maxsize-fill) );
|
||||
if (to_copy)
|
||||
// We haven't yet filled the buffer. How many bytes to copy
|
||||
// into the buff. Either all of the bytes we want to process
|
||||
// or the number of bytes until we reach maxsize.
|
||||
int64_t to_copy = min( to_process, (maxsize-fill) );
|
||||
if ( to_copy )
|
||||
memcpy(buf+fill, data, to_copy);
|
||||
|
||||
fill += to_copy;
|
||||
}
|
||||
|
||||
processed += to_process;
|
||||
len -= to_process;
|
||||
data += to_process;
|
||||
|
@ -372,7 +388,7 @@ bool RPC_Reasm_Buffer::ConsumeChunk(const u_char*& data, int& len)
|
|||
|
||||
Contents_RPC::Contents_RPC(Connection* conn, bool orig,
|
||||
RPC_Interpreter* arg_interp)
|
||||
: TCP_SupportAnalyzer(AnalyzerTag::Contents_RPC, conn, orig)
|
||||
: TCP_SupportAnalyzer(AnalyzerTag::Contents_RPC, conn, orig)
|
||||
{
|
||||
interp = arg_interp;
|
||||
state = WAIT_FOR_MESSAGE;
|
||||
|
@ -387,7 +403,6 @@ void Contents_RPC::Init()
|
|||
TCP_SupportAnalyzer::Init();
|
||||
}
|
||||
|
||||
|
||||
Contents_RPC::~Contents_RPC()
|
||||
{
|
||||
}
|
||||
|
@ -407,7 +422,7 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
|
||||
bool discard_this_chunk = false;
|
||||
|
||||
if (resync_state == RESYNC_INIT)
|
||||
if ( resync_state == RESYNC_INIT )
|
||||
{
|
||||
// First time CheckResync is called. If the TCP endpoint
|
||||
// is fully established we are in sync (since it's the first chunk
|
||||
|
@ -417,8 +432,8 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
static_cast<TCP_ApplicationAnalyzer*>(Parent())->TCP();
|
||||
assert(tcp);
|
||||
|
||||
if ((IsOrig() ? tcp->OrigState() : tcp->RespState()) !=
|
||||
TCP_ENDPOINT_ESTABLISHED)
|
||||
if ( (IsOrig() ? tcp->OrigState() : tcp->RespState()) !=
|
||||
TCP_ENDPOINT_ESTABLISHED )
|
||||
{
|
||||
NeedResync();
|
||||
}
|
||||
|
@ -426,41 +441,41 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
resync_state = INSYNC;
|
||||
}
|
||||
|
||||
if (resync_state == INSYNC)
|
||||
if ( resync_state == INSYNC )
|
||||
return true;
|
||||
|
||||
// This is an attempt to re-synchronize the stream with RPC
|
||||
// frames after a content gap.
|
||||
// Returns true if we are in sync.
|
||||
// Returns false otherwise (we are in resync mode)
|
||||
// This is an attempt to re-synchronize the stream with RPC frames
|
||||
// after a content gap. Returns true if we are in sync. Returns
|
||||
// false otherwise (we are in resync mode)
|
||||
//
|
||||
// We try to look for the beginning of a RPC frame, assuming
|
||||
// RPC frames begin at packet boundaries (though they may span
|
||||
// over multiple packets) (note that the data* of DeliverStream()
|
||||
// usually starts at a packet boundrary).
|
||||
// We try to look for the beginning of a RPC frame, assuming RPC
|
||||
// frames begin at packet boundaries (though they may span over
|
||||
// multiple packets) (note that the data* of DeliverStream() usually
|
||||
// starts at a packet boundrary).
|
||||
//
|
||||
// If we see a frame start that makes sense (direction and
|
||||
// frame lenght seem ok), we try to read (skip over) the next RPC
|
||||
// message. If this is successfull and we the place we are seems
|
||||
// like a valid start of a RPC msg (direction and frame length
|
||||
// seem ok). We assume that we have successfully resync'ed.
|
||||
|
||||
// If we see a frame start that makes sense (direction and frame
|
||||
// lenght seem ok), we try to read (skip over) the next RPC message.
|
||||
// If this is successfull and we the place we are seems like a valid
|
||||
// start of a RPC msg (direction and frame length seem ok). We assume
|
||||
// that we have successfully resync'ed.
|
||||
|
||||
// Assuming RPC frames align with packet boundaries ...
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
if (resync_toskip)
|
||||
if ( resync_toskip )
|
||||
{
|
||||
if ( DEBUG_rpc_resync )
|
||||
DEBUG_MSG("RPC resync: skipping %d bytes.\n", len);
|
||||
// We have some bytes to skip over.
|
||||
if (resync_toskip < len)
|
||||
|
||||
// We have some bytes to skip over.
|
||||
if ( resync_toskip < len )
|
||||
{
|
||||
len -= resync_toskip;
|
||||
data += resync_toskip;
|
||||
resync_toskip = 0;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
resync_toskip -= len;
|
||||
data += len;
|
||||
|
@ -469,22 +484,18 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
}
|
||||
}
|
||||
|
||||
if (resync_toskip != 0)
|
||||
{
|
||||
// Should never happen
|
||||
if ( resync_toskip != 0 )
|
||||
// Should never happen.
|
||||
internal_error("RPC resync: skipping over data failed");
|
||||
NeedResync();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now lets see whether data points to the beginning of a
|
||||
// RPC frame. If the resync processs is successful, we should
|
||||
// be at the beginning of a frame.
|
||||
// Now lets see whether data points to the beginning of a RPC
|
||||
// frame. If the resync processs is successful, we should be
|
||||
// at the beginning of a frame.
|
||||
|
||||
|
||||
|
||||
if ( len < 12 )
|
||||
{
|
||||
// Ignore small chunks.
|
||||
// Ignore small chunks.
|
||||
if ( len != 1 && DEBUG_rpc_resync )
|
||||
{
|
||||
// One-byte fragments are likely caused by
|
||||
|
@ -496,6 +507,7 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
fmt("RPC resync: discard %d bytes\n",
|
||||
len));
|
||||
}
|
||||
|
||||
NeedResync();
|
||||
return false;
|
||||
}
|
||||
|
@ -517,22 +529,23 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
|
||||
discard_this_chunk = true;
|
||||
|
||||
// Make sure the frame isn't too long.
|
||||
// TODO: Could possible even reduce this number even further.
|
||||
if (frame_len > MAX_RPC_LEN)
|
||||
// Make sure the frame isn't too long.
|
||||
// TODO: Could possible even reduce this number even further.
|
||||
if ( frame_len > MAX_RPC_LEN )
|
||||
discard_this_chunk = true;
|
||||
|
||||
if (discard_this_chunk)
|
||||
if ( discard_this_chunk )
|
||||
{
|
||||
// Skip this chunk
|
||||
if ( DEBUG_rpc_resync )
|
||||
DEBUG_MSG("RPC resync: Need to resync. dicarding %d bytes.\n", len);
|
||||
|
||||
NeedResync(); // let's try the resync again from the beginning
|
||||
return false;
|
||||
}
|
||||
|
||||
// Looks like we are at the start of a frame and have successfully
|
||||
// extracted the frame length (marker).
|
||||
// Looks like we are at the start of a frame and have successfully
|
||||
// extracted the frame length (marker).
|
||||
|
||||
switch (resync_state) {
|
||||
case NEED_RESYNC:
|
||||
|
@ -540,30 +553,33 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
// Initial phase of resyncing. Skip frames until we get a frame
|
||||
// with the last_fragment bit set.
|
||||
resync_toskip = frame_len + 4;
|
||||
if (last_frag)
|
||||
|
||||
if ( last_frag )
|
||||
resync_state = RESYNC_WAIT_FOR_FULL_MSG;
|
||||
else
|
||||
else
|
||||
resync_state = RESYNC_WAIT_FOR_MSG_START;
|
||||
break;
|
||||
|
||||
case RESYNC_WAIT_FOR_FULL_MSG:
|
||||
// If the resync was successful so far, we should now be the start
|
||||
// If the resync was successful so far, we should now be the start
|
||||
// of a new RPC message. Try to skip over it.
|
||||
resync_toskip = frame_len + 4;
|
||||
if (last_frag)
|
||||
|
||||
if ( last_frag )
|
||||
resync_state = RESYNC_HAD_FULL_MSG;
|
||||
break;
|
||||
|
||||
|
||||
case RESYNC_HAD_FULL_MSG:
|
||||
// We have now successfully skipped over a full RPC message.
|
||||
// If we got that far, we are in sync.
|
||||
// We have now successfully skipped over a full RPC message.
|
||||
// If we got that far, we are in sync.
|
||||
resync_state = INSYNC;
|
||||
|
||||
if ( DEBUG_rpc_resync )
|
||||
DEBUG_MSG("RPC resync: success.\n");
|
||||
return true;
|
||||
|
||||
default:
|
||||
// Shoult never happen
|
||||
// Should never happen.
|
||||
NeedResync();
|
||||
return false;
|
||||
} // end switch
|
||||
|
@ -571,7 +587,7 @@ bool Contents_RPC::CheckResync(int& len, const u_char*& data, bool orig)
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -581,41 +597,45 @@ void Contents_RPC::DeliverStream(int len, const u_char* data, bool orig)
|
|||
uint32 marker;
|
||||
bool last_frag;
|
||||
|
||||
if (!CheckResync(len, data, orig))
|
||||
return; // Not in sync yet. Still resyncing
|
||||
if ( ! CheckResync(len, data, orig) )
|
||||
return; // Not in sync yet. Still resyncing.
|
||||
|
||||
// Should be in sync now.
|
||||
|
||||
|
||||
// Should be in sync now
|
||||
|
||||
while (len > 0)
|
||||
while (len > 0)
|
||||
{
|
||||
last_time = network_time;
|
||||
switch (state) {
|
||||
case WAIT_FOR_MESSAGE:
|
||||
// A new RPC message is starting. Initialize state
|
||||
|
||||
// We expect and want 4 bytes of the frame markers
|
||||
switch (state) {
|
||||
case WAIT_FOR_MESSAGE:
|
||||
// A new RPC message is starting. Initialize state.
|
||||
|
||||
// We expect and want 4 bytes of the frame markers.
|
||||
marker_buf.Init(4,4);
|
||||
// We want at most 64KB of message data and we don't know
|
||||
// yet how much we expect, so we set expected to 0
|
||||
|
||||
// We want at most 64KB of message data and we don't
|
||||
// know yet how much we expect, so we set expected to
|
||||
// 0.
|
||||
msg_buf.Init(MAX_RPC_LEN, 0);
|
||||
last_frag = 0;
|
||||
state = WAIT_FOR_MARKER;
|
||||
start_time = network_time;
|
||||
// no break. fall through
|
||||
|
||||
|
||||
case WAIT_FOR_MARKER:
|
||||
{
|
||||
bool got_marker = marker_buf.ConsumeChunk(data,len);
|
||||
if (got_marker)
|
||||
|
||||
if ( got_marker )
|
||||
{
|
||||
const u_char *dummy_p = marker_buf.GetBuf();
|
||||
int dummy_len = (int) marker_buf.GetFill();
|
||||
|
||||
// have full marker
|
||||
marker = extract_XDR_uint32(dummy_p, dummy_len);
|
||||
marker_buf.Init(4,4);
|
||||
if ( ! dummy_p )
|
||||
|
||||
if ( ! dummy_p )
|
||||
{
|
||||
internal_error("inconsistent RPC record marker extraction");
|
||||
}
|
||||
|
@ -624,41 +644,49 @@ void Contents_RPC::DeliverStream(int len, const u_char* data, bool orig)
|
|||
marker &= 0x7fffffff;
|
||||
//printf("%.6f %d marker= %u <> last_frag= %d <> expected=%llu <> processed= %llu <> len = %d\n",
|
||||
// network_time, IsOrig(), marker, last_frag, msg_buf.GetExpected(), msg_buf.GetProcessed(), len);
|
||||
if (!msg_buf.AddToExpected(marker))
|
||||
|
||||
if ( ! msg_buf.AddToExpected(marker) )
|
||||
Conn()->Weird(fmt("RPC_message_too_long (%" PRId64 ")" , msg_buf.GetExpected()));
|
||||
if (last_frag)
|
||||
|
||||
if ( last_frag )
|
||||
state = WAIT_FOR_LAST_DATA;
|
||||
else
|
||||
state = WAIT_FOR_DATA;
|
||||
}
|
||||
}
|
||||
// else remain in state. Haven't got the full 4 bytes for the marker yet
|
||||
// Else remain in state. Haven't got the full 4 bytes
|
||||
// for the marker yet.
|
||||
break;
|
||||
|
||||
case WAIT_FOR_DATA:
|
||||
case WAIT_FOR_LAST_DATA:
|
||||
{
|
||||
bool got_all_data = msg_buf.ConsumeChunk(data, len);
|
||||
if (got_all_data)
|
||||
|
||||
if ( got_all_data )
|
||||
{
|
||||
// got all the data we expected. Now let's see whether there is
|
||||
// another fragment coming or whether we just finished the last
|
||||
// fragment.
|
||||
if (state == WAIT_FOR_LAST_DATA)
|
||||
// Got all the data we expected. Now let's
|
||||
// see whether there is another fragment
|
||||
// coming or whether we just finished the
|
||||
// last fragment.
|
||||
if ( state == WAIT_FOR_LAST_DATA )
|
||||
{
|
||||
const u_char *dummy_p = msg_buf.GetBuf();
|
||||
int dummy_len = (int) msg_buf.GetFill();
|
||||
|
||||
if ( ! interp->DeliverRPC(dummy_p, dummy_len, (int)msg_buf.GetExpected(), IsOrig(), start_time, last_time) )
|
||||
Conn()->Weird("partial_RPC");
|
||||
|
||||
state = WAIT_FOR_MESSAGE;
|
||||
}
|
||||
else
|
||||
state = WAIT_FOR_MARKER;
|
||||
}
|
||||
// else remain in state. Haven't read all the data yet.
|
||||
// Else remain in state. Haven't read all the data
|
||||
// yet.
|
||||
}
|
||||
break;
|
||||
} // end switch
|
||||
} // end switch
|
||||
} // end while
|
||||
}
|
||||
|
||||
|
@ -701,29 +729,6 @@ void RPC_Analyzer::Done()
|
|||
TCP_ApplicationAnalyzer::Done();
|
||||
|
||||
interp->Timeout();
|
||||
#if 0
|
||||
TODO: maybe put this check back in. But there are so many other
|
||||
things the RPC analyzer might miss....
|
||||
// This code was replicated in NFS.cc and Portmap.cc, so we factor
|
||||
// it into here. The semantics have slightly changed - it used
|
||||
// to be we'd always execute interp->Timeout(), but now we only
|
||||
// do for UDP.
|
||||
|
||||
if ( Conn()->ConnTransport() == TRANSPORT_TCP && TCP() )
|
||||
{
|
||||
|
||||
if ( orig_rpc->State() != RPC_COMPLETE &&
|
||||
(TCP()->OrigState() == TCP_ENDPOINT_CLOSED ||
|
||||
TCP()->OrigPrevState() == TCP_ENDPOINT_CLOSED) &&
|
||||
// Sometimes things like tcpwrappers will immediately
|
||||
// close the connection, without any data having been
|
||||
// transferred. Don't bother flagging these.
|
||||
TCP()->Orig()->Size() > 0 )
|
||||
Weird("partial_RPC_request");
|
||||
}
|
||||
else
|
||||
interp->Timeout();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RPC_Analyzer::ExpireTimer(double /* t */)
|
||||
|
@ -731,4 +736,3 @@ void RPC_Analyzer::ExpireTimer(double /* t */)
|
|||
Event(connection_timeout);
|
||||
sessions->Remove(Conn());
|
||||
}
|
||||
|
||||
|
|
75
src/RPC.h
75
src/RPC.h
|
@ -49,7 +49,8 @@ enum {
|
|||
|
||||
class RPC_CallInfo {
|
||||
public:
|
||||
RPC_CallInfo(uint32 xid, const u_char*& buf, int& n, double start_time, double last_time, int rpc_len);
|
||||
RPC_CallInfo(uint32 xid, const u_char*& buf, int& n, double start_time,
|
||||
double last_time, int rpc_len);
|
||||
~RPC_CallInfo();
|
||||
|
||||
void AddVal(Val* arg_v) { Unref(v); v = arg_v; }
|
||||
|
@ -63,11 +64,11 @@ public:
|
|||
uint32 Proc() const { return proc; }
|
||||
|
||||
double StartTime() const { return start_time; }
|
||||
void SetStartTime(double t) { start_time = t; }
|
||||
void SetStartTime(double t) { start_time = t; }
|
||||
double LastTime() const { return last_time; }
|
||||
void SetLastTime(double t) { last_time = t; }
|
||||
void SetLastTime(double t) { last_time = t; }
|
||||
int CallLen() const { return call_n; }
|
||||
int RPCLen() const { return rpc_len; }
|
||||
int RPCLen() const { return rpc_len; }
|
||||
int HeaderLen() const { return header_len; }
|
||||
|
||||
uint32 XID() const { return xid; }
|
||||
|
@ -81,7 +82,7 @@ protected:
|
|||
u_char* call_buf; // copy of original call buffer
|
||||
double start_time;
|
||||
double last_time;
|
||||
int rpc_len; // size of the full RPC call, incl. xid and msg_type
|
||||
int rpc_len; // size of the full RPC call, incl. xid and msg_type
|
||||
int call_n; // size of call buf
|
||||
int header_len; // size of data before the arguments
|
||||
bool valid_call; // whether call was well-formed
|
||||
|
@ -106,8 +107,8 @@ public:
|
|||
protected:
|
||||
virtual int RPC_BuildCall(RPC_CallInfo* c, const u_char*& buf, int& n) = 0;
|
||||
virtual int RPC_BuildReply(RPC_CallInfo* c, BifEnum::rpc_status success,
|
||||
const u_char*& buf, int& n, double start_time, double last_time,
|
||||
int reply_len) = 0;
|
||||
const u_char*& buf, int& n, double start_time, double last_time,
|
||||
int reply_len) = 0;
|
||||
|
||||
void Event_RPC_Dialogue(RPC_CallInfo* c, BifEnum::rpc_status status, int reply_len);
|
||||
void Event_RPC_Call(RPC_CallInfo* c);
|
||||
|
@ -121,22 +122,22 @@ protected:
|
|||
|
||||
|
||||
/* A simple buffer for reassembling the fragments that RPC-over-TCP
|
||||
* uses. Only needed by RPC_Contents
|
||||
* uses. Only needed by RPC_Contents.
|
||||
|
||||
* However, RPC messages can be quite large. As a first step, we only
|
||||
* extract and analyzer the first part of an RPC message and skip
|
||||
* extract and analyzer the first part of an RPC message and skip
|
||||
* over the rest.
|
||||
*
|
||||
* We specify:
|
||||
* maxsize ... the number of bytes we want to copy into the buffer
|
||||
* to analyze.
|
||||
* expected .. the total number of bytes in the RPC message. Can be
|
||||
* quite large. We will be "skipping over" expected-maxsize bytes.
|
||||
* maxsize: the number of bytes we want to copy into the buffer to analyze.
|
||||
* expected: the total number of bytes in the RPC message. Can be
|
||||
* quite large. We will be "skipping over" expected-maxsize bytes.
|
||||
*
|
||||
* We can extend "expected" (by calling AddToExpected()), but maxsize is
|
||||
* fixed.
|
||||
* fixed.
|
||||
*
|
||||
* TODO: grow buffer dynamically
|
||||
*/
|
||||
*/
|
||||
class RPC_Reasm_Buffer {
|
||||
public:
|
||||
RPC_Reasm_Buffer() {
|
||||
|
@ -149,33 +150,34 @@ public:
|
|||
|
||||
void Init(int64_t arg_maxsize, int64_t arg_expected);
|
||||
|
||||
const u_char *GetBuf() { return buf; } // Pointer to the buffer
|
||||
int64_t GetFill() { return fill; } // Number of bytes in buf
|
||||
int64_t GetSkipped() { return processed-fill; } // How many bytes did we skipped?
|
||||
int64_t GetExpected() { return expected; } // How many bytes are we expecting?
|
||||
int64_t GetProcessed() { return processed; } // How many bytes are we expecting?
|
||||
const u_char *GetBuf() { return buf; } // Pointer to the buffer
|
||||
int64_t GetFill() { return fill; } // Number of bytes in buf
|
||||
int64_t GetSkipped() { return processed-fill; } // How many bytes did we skipped?
|
||||
int64_t GetExpected() { return expected; } // How many bytes are we expecting?
|
||||
int64_t GetProcessed() { return processed; } // How many bytes are we expecting?
|
||||
|
||||
// Expand expected by delta bytes.
|
||||
// Returns false if the number of expected bytes exceeds maxsize
|
||||
// (which means that we will truncate the message).
|
||||
bool AddToExpected(int64_t delta) { expected += delta; return !(expected>maxsize); }
|
||||
// Expand expected by delta bytes. Returns false if the number of
|
||||
// expected bytes exceeds maxsize (which means that we will truncate
|
||||
// the message).
|
||||
bool AddToExpected(int64_t delta)
|
||||
{ expected += delta; return ! (expected > maxsize); }
|
||||
|
||||
// Consume a chunk of input data (pointed to by data, up len in size).
|
||||
// data and len will be adjustes accordingly.
|
||||
// Returns true if "exptected" bytes have been processed, i.e., returns
|
||||
// true when we don't expect any more data.
|
||||
// Consume a chunk of input data (pointed to by data, up len in
|
||||
// size). data and len will be adjusted accordingly. Returns true if
|
||||
// "expected" bytes have been processed, i.e., returns true when we
|
||||
// don't expect any more data.
|
||||
bool ConsumeChunk(const u_char*& data, int& len);
|
||||
|
||||
protected:
|
||||
int64_t fill; // how many bytes we currently have in the buffer
|
||||
int64_t maxsize; // maximum buffer size we want to allocate
|
||||
int64_t processed; // number of bytes we have processed so far
|
||||
int64_t expected; // number of input bytes we expect
|
||||
int64_t fill; // how many bytes we currently have in the buffer
|
||||
int64_t maxsize; // maximum buffer size we want to allocate
|
||||
int64_t processed; // number of bytes we have processed so far
|
||||
int64_t expected; // number of input bytes we expect
|
||||
u_char *buf;
|
||||
|
||||
};
|
||||
|
||||
/* Support Analyzer for reassembling RPC-over-TCP messages */
|
||||
/* Support Analyzer for reassembling RPC-over-TCP messages */
|
||||
class Contents_RPC : public TCP_SupportAnalyzer {
|
||||
public:
|
||||
Contents_RPC(Connection* conn, bool orig, RPC_Interpreter* interp);
|
||||
|
@ -188,6 +190,7 @@ protected:
|
|||
WAIT_FOR_DATA,
|
||||
WAIT_FOR_LAST_DATA,
|
||||
} state_t;
|
||||
|
||||
typedef enum {
|
||||
NEED_RESYNC,
|
||||
RESYNC_WAIT_FOR_MSG_START,
|
||||
|
@ -196,6 +199,7 @@ protected:
|
|||
INSYNC,
|
||||
RESYNC_INIT,
|
||||
} resync_state_t;
|
||||
|
||||
virtual void Init();
|
||||
virtual bool CheckResync(int& len, const u_char*& data, bool orig);
|
||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||
|
@ -209,8 +213,8 @@ protected:
|
|||
|
||||
RPC_Interpreter* interp;
|
||||
|
||||
RPC_Reasm_Buffer marker_buf; // Reassembles the 32bit RPC-over-TCP marker
|
||||
RPC_Reasm_Buffer msg_buf; // Reassembles RPC messages
|
||||
RPC_Reasm_Buffer marker_buf; // reassembles the 32bit RPC-over-TCP marker
|
||||
RPC_Reasm_Buffer msg_buf; // reassembles RPC messages
|
||||
state_t state;
|
||||
|
||||
double start_time;
|
||||
|
@ -240,5 +244,4 @@ protected:
|
|||
Contents_RPC* resp_rpc;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "Reassem.h"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// $Id: RuleMatcher.cc 6724 2009-06-07 09:23:03Z vern $
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "Analyzer.h"
|
||||
|
@ -197,7 +199,7 @@ bool RuleMatcher::ReadFiles(const name_list& files)
|
|||
|
||||
for ( int i = 0; i < files.length(); ++i )
|
||||
{
|
||||
rules_in = search_for_file( files[i], "sig", 0);
|
||||
rules_in = search_for_file( files[i], "sig", 0, false);
|
||||
if ( ! rules_in )
|
||||
{
|
||||
error("Can't open signature file", files[i]);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// $Id:$
|
||||
|
||||
#include "SSL-binpac.h"
|
||||
#include "TCP_Reassembler.h"
|
||||
#include "util.h"
|
||||
|
@ -8,13 +6,10 @@
|
|||
bool SSL_Analyzer_binpac::warnings_generated = false;
|
||||
|
||||
SSL_Analyzer_binpac::SSL_Analyzer_binpac(Connection* c)
|
||||
: TCP_ApplicationAnalyzer(AnalyzerTag::SSL_BINPAC, c)
|
||||
: TCP_ApplicationAnalyzer(AnalyzerTag::SSL, c)
|
||||
{
|
||||
ssl = new binpac::SSL::SSLAnalyzer;
|
||||
ssl->set_bro_analyzer(this);
|
||||
|
||||
records = new binpac::SSLRecordLayer::SSLRecordLayerAnalyzer;
|
||||
records->set_ssl_analyzer(ssl);
|
||||
interp = new binpac::SSL::SSLAnalyzer;
|
||||
interp->set_bro_analyzer(this);
|
||||
|
||||
if ( ! warnings_generated )
|
||||
generate_warnings();
|
||||
|
@ -22,23 +17,21 @@ SSL_Analyzer_binpac::SSL_Analyzer_binpac(Connection* c)
|
|||
|
||||
SSL_Analyzer_binpac::~SSL_Analyzer_binpac()
|
||||
{
|
||||
delete records;
|
||||
delete ssl;
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void SSL_Analyzer_binpac::Done()
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Done();
|
||||
|
||||
records->FlowEOF(true);
|
||||
records->FlowEOF(false);
|
||||
interp->FlowEOF(true);
|
||||
interp->FlowEOF(false);
|
||||
}
|
||||
|
||||
void SSL_Analyzer_binpac::EndpointEOF(TCP_Reassembler* endp)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::EndpointEOF(endp);
|
||||
records->FlowEOF(endp->IsOrig());
|
||||
ssl->FlowEOF(endp->IsOrig());
|
||||
interp->FlowEOF(endp->IsOrig());
|
||||
}
|
||||
|
||||
void SSL_Analyzer_binpac::DeliverStream(int len, const u_char* data, bool orig)
|
||||
|
@ -50,13 +43,13 @@ void SSL_Analyzer_binpac::DeliverStream(int len, const u_char* data, bool orig)
|
|||
if ( TCP()->IsPartial() )
|
||||
return;
|
||||
|
||||
records->NewData(orig, data, data + len);
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
||||
|
||||
void SSL_Analyzer_binpac::Undelivered(int seq, int len, bool orig)
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
records->NewGap(orig, len);
|
||||
interp->NewGap(orig, len);
|
||||
}
|
||||
|
||||
void SSL_Analyzer_binpac::warn_(const char* msg)
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
// $Id:$
|
||||
|
||||
#ifndef ssl_binpac_h
|
||||
#define ssl_binpac_h
|
||||
|
||||
#include "TCP.h"
|
||||
|
||||
#include "ssl_pac.h"
|
||||
#include "ssl-record-layer_pac.h"
|
||||
|
||||
class SSL_Analyzer_binpac : public TCP_ApplicationAnalyzer {
|
||||
public:
|
||||
|
@ -23,11 +20,9 @@ public:
|
|||
|
||||
static bool Available()
|
||||
{
|
||||
return FLAGS_use_binpac &&
|
||||
(ssl_certificate_seen || ssl_certificate ||
|
||||
ssl_conn_attempt || ssl_conn_server_reply ||
|
||||
ssl_conn_established || ssl_conn_reused ||
|
||||
ssl_conn_alert);
|
||||
return ( ssl_client_hello || ssl_server_hello ||
|
||||
ssl_established || ssl_extension || ssl_alert ||
|
||||
x509_certificate || x509_extension || x509_error );
|
||||
}
|
||||
|
||||
static bool warnings_generated;
|
||||
|
@ -35,8 +30,7 @@ public:
|
|||
static void generate_warnings();
|
||||
|
||||
protected:
|
||||
binpac::SSLRecordLayer::SSLRecordLayerAnalyzer* records;
|
||||
binpac::SSL::SSLAnalyzer* ssl;
|
||||
binpac::SSL::SSLAnalyzer* interp;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
1073
src/SSLCiphers.cc
1073
src/SSLCiphers.cc
File diff suppressed because it is too large
Load diff
367
src/SSLCiphers.h
367
src/SSLCiphers.h
|
@ -1,367 +0,0 @@
|
|||
// $Id: SSLCiphers.h 1678 2005-11-08 19:16:37Z vern $
|
||||
|
||||
#ifndef SSL_CIPHERS_H
|
||||
#define SSL_CIPHERS_H
|
||||
|
||||
#include "Dict.h"
|
||||
|
||||
// --- definitions for sslv3x cipher handling ---------------------------------
|
||||
|
||||
/*!
|
||||
* In SSLv2, a cipher spec consists of three bytes.
|
||||
*/
|
||||
enum SSLv2_CipherSpec {
|
||||
// --- standard SSLv2 ciphers
|
||||
SSL_CK_RC4_128_WITH_MD5 = 0x010080,
|
||||
SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x020080,
|
||||
SSL_CK_RC2_128_CBC_WITH_MD5 = 0x030080,
|
||||
SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x040080,
|
||||
SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x050080,
|
||||
SSL_CK_DES_64_CBC_WITH_MD5 = 0x060040,
|
||||
SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x0700C0,
|
||||
SSL_CK_RC4_64_WITH_MD5 = 0x080080
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* In SSLv3x, a cipher spec consists of two bytes.
|
||||
*/
|
||||
enum SSL3_1_CipherSpec {
|
||||
// --- standard SSLv3x ciphers
|
||||
TLS_NULL_WITH_NULL_NULL = 0x0000,
|
||||
TLS_RSA_WITH_NULL_MD5 = 0x0001,
|
||||
TLS_RSA_WITH_NULL_SHA = 0x0002,
|
||||
TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003,
|
||||
TLS_RSA_WITH_RC4_128_MD5 = 0x0004,
|
||||
TLS_RSA_WITH_RC4_128_SHA = 0x0005,
|
||||
TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006,
|
||||
TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007,
|
||||
TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008,
|
||||
TLS_RSA_WITH_DES_CBC_SHA = 0x0009,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A,
|
||||
TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B,
|
||||
TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C,
|
||||
TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D,
|
||||
TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E,
|
||||
TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F,
|
||||
TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010,
|
||||
TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011,
|
||||
TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012,
|
||||
TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013,
|
||||
TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014,
|
||||
TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015,
|
||||
TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016,
|
||||
TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017,
|
||||
TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018,
|
||||
TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019,
|
||||
TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A,
|
||||
TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B,
|
||||
// --- special SSLv3 ciphers
|
||||
SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x001C,
|
||||
SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA = 0x001D,
|
||||
//SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x001E,
|
||||
// -- RFC 2712 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_KRB5_WITH_DES_CBC_SHA = 0x001E,
|
||||
TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F,
|
||||
TLS_KRB5_WITH_RC4_128_SHA = 0x0020,
|
||||
TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021,
|
||||
TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022,
|
||||
TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023,
|
||||
TLS_KRB5_WITH_RC4_128_MD5 = 0x0024,
|
||||
TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025,
|
||||
TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026,
|
||||
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027,
|
||||
TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028,
|
||||
TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029,
|
||||
TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A,
|
||||
TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B,
|
||||
|
||||
// --- new AES ciphers
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F,
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030,
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031,
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032,
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
|
||||
TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035,
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036,
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037,
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038,
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
|
||||
TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A,
|
||||
TLS_RSA_WITH_NULL_SHA256 = 0x003B,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
|
||||
TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E,
|
||||
TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F,
|
||||
TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040,
|
||||
// -- RFC 4132
|
||||
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045,
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046,
|
||||
// -- Non-RFC. Widely deployed implementation (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_RSA_EXPORT1024_WITH_RC4_56_MD5 = 0x0060,
|
||||
TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 = 0x0061,
|
||||
TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA = 0x0062,
|
||||
TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA = 0x0063,
|
||||
TLS_RSA_EXPORT1024_WITH_RC4_56_SHA = 0x0064,
|
||||
TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA = 0x0065,
|
||||
TLS_DHE_DSS_WITH_RC4_128_SHA = 0x0066,
|
||||
// -- RFC 5246 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
|
||||
TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068,
|
||||
TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069,
|
||||
TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A,
|
||||
TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
|
||||
TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C,
|
||||
TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D,
|
||||
// -- RFC 5932
|
||||
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088,
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089,
|
||||
// -- RFC 4279 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_PSK_WITH_RC4_128_SHA = 0x008A,
|
||||
TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B,
|
||||
TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
|
||||
TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D,
|
||||
TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E,
|
||||
TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F,
|
||||
TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090,
|
||||
TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091,
|
||||
TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092,
|
||||
TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093,
|
||||
TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094,
|
||||
TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095,
|
||||
// -- RFC 4162
|
||||
TLS_RSA_WITH_SEED_CBC_SHA = 0x0096,
|
||||
TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097,
|
||||
TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098,
|
||||
TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099,
|
||||
TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A,
|
||||
TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B,
|
||||
// -- RFC 5288 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
|
||||
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
|
||||
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
|
||||
TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0,
|
||||
TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1,
|
||||
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2,
|
||||
TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3,
|
||||
TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4,
|
||||
TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5,
|
||||
TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6,
|
||||
TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7,
|
||||
// -- RFC 5487 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8,
|
||||
TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9,
|
||||
TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA,
|
||||
TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB,
|
||||
TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC,
|
||||
TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD,
|
||||
TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE,
|
||||
TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF,
|
||||
TLS_PSK_WITH_NULL_SHA256 = 0x00B0,
|
||||
TLS_PSK_WITH_NULL_SHA384 = 0x00B1,
|
||||
TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2,
|
||||
TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3,
|
||||
TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4,
|
||||
TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5,
|
||||
TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6,
|
||||
TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7,
|
||||
TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8,
|
||||
TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9,
|
||||
// -- RFC 5932 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE,
|
||||
TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF,
|
||||
TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0,
|
||||
TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1,
|
||||
TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2,
|
||||
TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3,
|
||||
TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4,
|
||||
TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5,
|
||||
// -- RFC 4492
|
||||
TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001,
|
||||
TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002,
|
||||
TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003,
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004,
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005,
|
||||
TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006,
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007,
|
||||
TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
|
||||
TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B,
|
||||
TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C,
|
||||
TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D,
|
||||
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E,
|
||||
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F,
|
||||
TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010,
|
||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011,
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
|
||||
TLS_ECDH_anon_WITH_NULL_SHA = 0xC015,
|
||||
TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016,
|
||||
TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017,
|
||||
TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018,
|
||||
TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019,
|
||||
// -- RFC 5054 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A,
|
||||
TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B,
|
||||
TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C,
|
||||
TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D,
|
||||
TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E,
|
||||
TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F,
|
||||
TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020,
|
||||
TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021,
|
||||
TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022,
|
||||
// -- RFC 5289 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025,
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
|
||||
TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029,
|
||||
TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
|
||||
TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D,
|
||||
TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
|
||||
TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031,
|
||||
TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032,
|
||||
// -- RFC 5489 (ciphers not fully described in SSLCiphers.cc)
|
||||
TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033,
|
||||
TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034,
|
||||
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035,
|
||||
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036,
|
||||
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037,
|
||||
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038,
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039,
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A,
|
||||
TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B,
|
||||
|
||||
// --- special SSLv3 FIPS ciphers
|
||||
SSL_RSA_FIPS_WITH_DES_CBC_SHA = 0xFEFE,
|
||||
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA = 0xFEFF,
|
||||
SSL_RSA_FIPS_WITH_DES_CBC_SHA_2 = 0xFFE1,
|
||||
SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA_2 = 0xFFE0,
|
||||
|
||||
// Tags for SSL 2 cipher kinds which are not specified for SSL 3.
|
||||
SSL_RSA_WITH_RC2_CBC_MD5 = 0xFF80,
|
||||
SSL_RSA_WITH_IDEA_CBC_MD5 = 0xFF81,
|
||||
SSL_RSA_WITH_DES_CBC_MD5 = 0xFF82,
|
||||
SSL_RSA_WITH_3DES_EDE_CBC_MD5 = 0xFF83,
|
||||
|
||||
TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
|
||||
};
|
||||
|
||||
enum SSL_CipherType {
|
||||
SSL_CIPHER_TYPE_STREAM,
|
||||
SSL_CIPHER_TYPE_BLOCK,
|
||||
SSL_CIPHER_TYPE_NULL
|
||||
};
|
||||
|
||||
enum SSL_BulkCipherAlgorithm {
|
||||
SSL_CIPHER_NULL,
|
||||
SSL_CIPHER_RC4,
|
||||
SSL_CIPHER_RC2,
|
||||
SSL_CIPHER_DES,
|
||||
SSL_CIPHER_3DES,
|
||||
SSL_CIPHER_DES40,
|
||||
SSL_CIPHER_FORTEZZA,
|
||||
SSL_CIPHER_IDEA,
|
||||
SSL_CIPHER_AES,
|
||||
SSL_CIPHER_CAMELLIA,
|
||||
SSL_CIPHER_SEED,
|
||||
};
|
||||
|
||||
enum SSL_MACAlgorithm {
|
||||
SSL_MAC_NULL,
|
||||
SSL_MAC_MD5,
|
||||
SSL_MAC_SHA
|
||||
};
|
||||
|
||||
enum SSL_KeyExchangeAlgorithm {
|
||||
SSL_KEY_EXCHANGE_NULL,
|
||||
SSL_KEY_EXCHANGE_RSA,
|
||||
SSL_KEY_EXCHANGE_RSA_EXPORT,
|
||||
SSL_KEY_EXCHANGE_DH,
|
||||
SSL_KEY_EXCHANGE_DH_DSS,
|
||||
SSL_KEY_EXCHANGE_DH_DSS_EXPORT,
|
||||
SSL_KEY_EXCHANGE_DH_RSA,
|
||||
SSL_KEY_EXCHANGE_DH_RSA_EXPORT,
|
||||
SSL_KEY_EXCHANGE_DHE_DSS,
|
||||
SSL_KEY_EXCHANGE_DHE_DSS_EXPORT,
|
||||
SSL_KEY_EXCHANGE_DHE_RSA,
|
||||
SSL_KEY_EXCHANGE_DHE_RSA_EXPORT,
|
||||
SSL_KEY_EXCHANGE_DH_anon,
|
||||
SSL_KEY_EXCHANGE_DH_anon_EXPORT,
|
||||
SSL_KEY_EXCHANGE_FORTEZZA_KEA,
|
||||
// --- new 56 bit export ciphers
|
||||
SSL_KEY_EXCHANGE_RSA_EXPORT1024,
|
||||
SSL_KEY_EXCHANGE_DHE_DSS_EXPORT1024,
|
||||
// -- Elliptic Curve key change algorithms (rfc4492)
|
||||
SSL_KEY_EXCHANGE_ECDH_ECDSA,
|
||||
SSL_KEY_EXCHANGE_ECDHE_ECDSA,
|
||||
SSL_KEY_EXCHANGE_ECDH_RSA,
|
||||
SSL_KEY_EXCHANGE_ECDHE_RSA,
|
||||
SSL_KEY_EXCHANGE_ECDH_anon,
|
||||
};
|
||||
|
||||
#if 0
|
||||
struct SSL_CipherSpecImprove {
|
||||
uint32 identifier;
|
||||
|
||||
// SSL_CipherType cipherType;
|
||||
SSL_BulkCipherAlgorithm encryptionAlgorithm;
|
||||
SSL_BulkCipherAlgorithm authenticationAlgorithm;
|
||||
SSL_BulkCipherAlgorithm keyAlgorithm;
|
||||
SSL_MACAlgorithm macAlgorithm;
|
||||
|
||||
int clearkeySize;
|
||||
int encryptedkeySize;
|
||||
uint32 flags; // IsExportable IsSSLv2 IsSSLv30 IsSSLv31
|
||||
const char* fullName = "TLS_WITH_NULL_NULL";
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
struct SSL_CipherSpec {
|
||||
uint32 identifier; ///< type code of the CIPHER-SPEC (2 or 3 Bytes)
|
||||
|
||||
SSL_CipherType cipherType;
|
||||
uint32 flags;
|
||||
SSL_BulkCipherAlgorithm bulkCipherAlgorithm;
|
||||
SSL_MACAlgorithm macAlgorithm;
|
||||
SSL_KeyExchangeAlgorithm keyExchangeAlgorithm;
|
||||
|
||||
int clearKeySize; ///< size in bits of plaintext part of master key
|
||||
int encryptedKeySize; ///< size in bits of encrypted part of master key
|
||||
int hashSize;
|
||||
};
|
||||
|
||||
const uint32 SSL_FLAG_EXPORT = 0x0001; ///< set if exportable cipher
|
||||
const uint32 SSL_FLAG_SSLv20 = 0x0002; ///< set if cipher defined for SSLv20
|
||||
const uint32 SSL_FLAG_SSLv30 = 0x0004; ///< set if cipher defined for SSLv30
|
||||
const uint32 SSL_FLAG_SSLv31 = 0x0008; ///< set if cipher defined for SSLv31
|
||||
|
||||
declare(PDict, SSL_CipherSpec);
|
||||
extern PDict(SSL_CipherSpec) SSL_CipherSpecDict;
|
||||
extern SSL_CipherSpec SSL_CipherSpecs[];
|
||||
extern const uint SSL_CipherSpecs_Count;
|
||||
|
||||
#endif
|
|
@ -1,48 +0,0 @@
|
|||
// $Id: SSLDefines.h 80 2004-07-14 20:15:50Z jason $
|
||||
|
||||
// Defines the states and transitions in the ssl-protocol-machine.
|
||||
|
||||
#ifndef SSL_DEFINES_H
|
||||
#define SSL_DEFINES_H
|
||||
|
||||
const int SSL3_1_NUM_STATES = 20;
|
||||
enum SSL3_1_States {
|
||||
SSL3_1_STATE_ERROR = 0,
|
||||
SSL3_1_STATE_INIT = 1,
|
||||
SSL3_1_STATE_SERVER_HELLO_REQ_SENT = 2,
|
||||
SSL3_1_STATE_CLIENT_HELLO_SENT = 3,
|
||||
SSL3_1_STATE_SERVER_HELLO_SENT = 4,
|
||||
SSL3_1_STATE_SERVER_CERT_SENT = 5,
|
||||
SSL3_1_STATE_SERVER_KEY_EXCHANGE_SENT = 6,
|
||||
SSL3_1_STATE_SERVER_CERT_REQ_SENT = 7,
|
||||
SSL3_1_STATE_SERVER_HELLO_DONE_SENT_A = 8,
|
||||
SSL3_1_STATE_SERVER_HELLO_DONE_SENT_B = 9,
|
||||
SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_A = 10,
|
||||
SSL3_1_STATE_CLIENT_KEY_EXCHANGE_SENT_B = 11,
|
||||
SSL3_1_STATE_CLIENT_CERT_SENT = 12,
|
||||
SSL3_1_STATE_CLIENT_CERT_VERIFY_SENT = 13,
|
||||
SSL3_1_STATE_CLIENT_FIN_SENT_A = 14,
|
||||
SSL3_1_STATE_SERVER_FIN_SENT_A = 15,
|
||||
SSL3_1_STATE_CLIENT_FIN_SENT_B = 16,
|
||||
SSL3_1_STATE_SERVER_FIN_SENT_B = 17,
|
||||
SSL3_1_STATE_HS_FIN_A = 18,
|
||||
SSL3_1_STATE_HS_FIN_B = 19
|
||||
};
|
||||
|
||||
const int SSL3_1_NUM_TRANS = 11;
|
||||
enum SSL_3_1_Transitions {
|
||||
SSL3_1_TRANS_SERVER_HELLO_REQ = 0,
|
||||
SSL3_1_TRANS_CLIENT_HELLO = 1,
|
||||
SSL3_1_TRANS_SERVER_HELLO = 2,
|
||||
SSL3_1_TRANS_SERVER_CERT = 3,
|
||||
SSL3_1_TRANS_SERVER_KEY_EXCHANGE = 4,
|
||||
SSL3_1_TRANS_SERVER_CERT_REQ = 5,
|
||||
SSL3_1_TRANS_SERVER_HELLO_DONE = 6,
|
||||
SSL3_1_TRANS_CLIENT_CERT = 3,
|
||||
SSL3_1_TRANS_CLIENT_KEY_EXCHANGE = 7,
|
||||
SSL3_1_TRANS_CLIENT_CERT_VERIFY = 8,
|
||||
SSL3_1_TRANS_CLIENT_FIN = 9,
|
||||
SSL3_1_TRANS_SERVER_FIN = 10
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,553 +0,0 @@
|
|||
// $Id: SSLInterpreter.cc 5988 2008-07-19 07:02:12Z vern $
|
||||
|
||||
#include "SSLInterpreter.h"
|
||||
#include "SSLv2.h"
|
||||
|
||||
#include "X509.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
declare(PDict, CertStore);
|
||||
PDict(CertStore) cert_states;
|
||||
|
||||
// --- Initalization of static variables --------------------------------------
|
||||
|
||||
uint32 SSL_Interpreter::analyzedCertificates = 0;
|
||||
uint32 SSL_Interpreter::verifiedCertificates = 0;
|
||||
uint32 SSL_Interpreter::failedCertificates = 0;
|
||||
uint32 SSL_Interpreter::certificateChains = 0;
|
||||
|
||||
// --- SSL_Interpreter --------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* The constructor.
|
||||
*
|
||||
* \param proxy Pointer to the SSLProxy_Analyzer which created this instance.
|
||||
*/
|
||||
SSL_Interpreter::SSL_Interpreter(SSLProxy_Analyzer* proxy)
|
||||
{
|
||||
this->proxy = proxy;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The destructor.
|
||||
*/
|
||||
SSL_Interpreter::~SSL_Interpreter()
|
||||
{
|
||||
delete orig;
|
||||
delete resp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Analogous to TCP_Connection::Init(), this method calls
|
||||
* BuildInterpreterEndpoints() to create the corresponding endpoints.
|
||||
*/
|
||||
void SSL_Interpreter::Init()
|
||||
{
|
||||
BuildInterpreterEndpoints();
|
||||
orig->SetPeer(resp);
|
||||
resp->SetPeer(orig);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyzes a given certificate (chain), using the OpenSSL library.
|
||||
*
|
||||
* \param s Pointer to the SSL_InterpreterEndpoint which received the
|
||||
* cerificate (chain).
|
||||
* \param data Pointer to the data block which contains the certificate (chain).
|
||||
* \param length Size of the data block.
|
||||
* \param type the certificate type
|
||||
* \param isChain false if data is pointing to a single certificate,
|
||||
* true if data is pointing to a certificate chain
|
||||
* mod by scott in:
|
||||
* uint32 ip_address = *(s->proxyEndpoint->Endpoint()->dst_addr);
|
||||
* uint16 port = (uint16) s->proxyEndpoint->Endpoint()->conn->RespPort();
|
||||
* inserting endpoint
|
||||
*/
|
||||
void SSL_Interpreter::analyzeCertificate(SSL_InterpreterEndpoint* s,
|
||||
const u_char* data, int length, uint8 type, bool isChain)
|
||||
{
|
||||
// See if we should continue with this certificate.
|
||||
if ( ssl_certificate_seen )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(new Val(! s->IsOrig(), TYPE_BOOL));
|
||||
proxy->ConnectionEvent(ssl_certificate_seen, vl);
|
||||
}
|
||||
|
||||
++analyzedCertificates;
|
||||
|
||||
const u_char* pCert = data;
|
||||
uint32 certLength = length;
|
||||
uint certCount = 0;
|
||||
|
||||
if ( isChain )
|
||||
{
|
||||
++certificateChains;
|
||||
|
||||
// Sum of all cert sizes has to match certListLength.
|
||||
int tempLength = 0;
|
||||
while ( tempLength < length )
|
||||
{
|
||||
++certCount;
|
||||
uint32 certLength =
|
||||
uint32((data[tempLength + 0] << 16) |
|
||||
data[tempLength + 1] << 8) |
|
||||
data[tempLength + 2];
|
||||
|
||||
tempLength += certLength + 3;
|
||||
}
|
||||
|
||||
if ( tempLength > length )
|
||||
{
|
||||
Weird( "SSLv3x: sum of size of certificates doesn't match size of certificate chain" );
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the first certificate.
|
||||
pCert = data + 3;
|
||||
certLength = uint32((data[0] << 16) | data[1] << 8) | data[2];
|
||||
}
|
||||
|
||||
// Create a hashsum of the current certificate.
|
||||
hash_t hashsum = HashKey::HashBytes(pCert, certLength);
|
||||
|
||||
if ( ! proxy->TCP() )
|
||||
return;
|
||||
|
||||
TCP_Endpoint* endp = s->IsOrig() ? proxy->TCP()->Orig() : proxy->TCP()->Resp();
|
||||
|
||||
// Check if we've seen a certificate from this addr/port before.
|
||||
uint8 key[6];
|
||||
// ### Won't work for IPv6.
|
||||
uint32 ip_address = *(endp->dst_addr);
|
||||
uint16 port = uint16(proxy->Conn()->RespPort());
|
||||
memcpy(key, &ip_address, 4);
|
||||
memcpy(&key[4], &port, 2);
|
||||
|
||||
HashKey h(key, sizeof(key));
|
||||
CertStore* pCertState = 0;
|
||||
pCertState = (CertStore*) cert_states.Lookup(&h);
|
||||
if ( ! pCertState )
|
||||
{ // new address
|
||||
pCertState = new CertStore(ip_address, port, hashsum, certLength);
|
||||
cert_states.Insert(&h, pCertState);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've seen this address/certificate before. Check if
|
||||
// certificate changed.
|
||||
if ( ! pCertState->isSameCert(hashsum, certLength) )
|
||||
{
|
||||
// This shouldn't happen; ### make a stronger error.
|
||||
Weird("SSL: Certificate changed for this ip+port !!!");
|
||||
|
||||
// Update status.
|
||||
++pCertState->changes;
|
||||
pCertState->certHash = hashsum;
|
||||
pCertState->certSize = certLength;
|
||||
pCertState->isValid = -1;
|
||||
}
|
||||
else
|
||||
{ // cert didn't change
|
||||
if ( pCertState->isValid == 0 )
|
||||
{
|
||||
// This is an invalid cert, but we
|
||||
// warned before.
|
||||
}
|
||||
|
||||
// Save time - don't analyze it any further.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Certificate verification.
|
||||
if ( ssl_verify_certificates != 0 )
|
||||
{
|
||||
++verifiedCertificates;
|
||||
int invalid = 0;
|
||||
switch ( type ) {
|
||||
case SSLv2_CT_X509_CERTIFICATE:
|
||||
if ( ! isChain )
|
||||
invalid = X509_Cert::verify(s->GetProxyEndpoint(),
|
||||
pCert, certLength);
|
||||
else
|
||||
invalid = X509_Cert::verifyChain(s->GetProxyEndpoint(),
|
||||
data, length);
|
||||
break;
|
||||
|
||||
default:
|
||||
Weird("SSL: Unknown CERTIFICATE-TYPE!");
|
||||
invalid = 1; // quick 'n dirty :)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( invalid )
|
||||
{
|
||||
proxy->Weak("SSL: Certificate check FAILED!");
|
||||
pCertState->isValid = 0;
|
||||
++failedCertificates;
|
||||
}
|
||||
else
|
||||
pCertState->isValid = 1;
|
||||
}
|
||||
|
||||
// Store the certificate.
|
||||
if ( ssl_store_certificates != 0 )
|
||||
{
|
||||
// Let's hope the address is currently in network byte order!
|
||||
in_addr addr;
|
||||
addr.s_addr = ip_address;
|
||||
char* pDummy = inet_ntoa(addr);
|
||||
char sFileName[PATH_MAX];
|
||||
|
||||
if ( ssl_store_cert_path &&
|
||||
ssl_store_cert_path->AsString()->Len() > 0 )
|
||||
{
|
||||
const BroString* pString = ssl_store_cert_path->AsString();
|
||||
safe_snprintf(sFileName, PATH_MAX, "%s/cert.%s-server-c%i.der",
|
||||
pString->Bytes(), pDummy, pCertState->changes);
|
||||
}
|
||||
else
|
||||
safe_snprintf(sFileName, PATH_MAX, "cert.%s-server-c%i.der",
|
||||
pDummy, pCertState->changes);
|
||||
|
||||
FILE* certFile = fopen(sFileName, "wb");
|
||||
if ( ! certFile )
|
||||
{
|
||||
Weird(fmt("SSL_Interpreter::analyzeCertificate(): Error opening '%s'!\n", sFileName));
|
||||
return;
|
||||
}
|
||||
|
||||
fwrite(pCert, 1, certLength, certFile);
|
||||
fclose(certFile);
|
||||
}
|
||||
|
||||
// TODO: test if cert is valid for the address we got it from.
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* \return the originating SSL_InterpreterEndpoint
|
||||
*/
|
||||
SSL_InterpreterEndpoint* SSL_Interpreter::Orig() const
|
||||
{
|
||||
return orig;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return the responding SSL_InterpreterEndpoint
|
||||
*/
|
||||
SSL_InterpreterEndpoint* SSL_Interpreter::Resp() const
|
||||
{
|
||||
return resp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \param p Pointer to an SSL_InterpreterEndpoint to test
|
||||
*
|
||||
* \return true if p is the originating SSL_InterpreterEndpoint,
|
||||
* false otherwise
|
||||
*/
|
||||
int SSL_Interpreter::Is_Orig(SSL_InterpreterEndpoint* p) const
|
||||
{
|
||||
return p == orig;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return the responding SSL_InterpreterEndpoint
|
||||
*/
|
||||
SSLProxy_Analyzer* SSL_Interpreter::Proxy() const
|
||||
{
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This methods prints a string into the "weird" log file.
|
||||
*
|
||||
* \param name String to log into the "weird" file.
|
||||
*/
|
||||
void SSL_Interpreter::Weird(const char* name) const
|
||||
{
|
||||
proxy->Weird(name);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Prints some counters.
|
||||
*/
|
||||
void SSL_Interpreter::printStats()
|
||||
{
|
||||
printf("SSL_Interpreter:\n");
|
||||
printf("analyzedCertificates = %u\n", analyzedCertificates);
|
||||
printf("verifiedCertificates = %u\n", verifiedCertificates);
|
||||
printf("failedCertificates = %u\n", failedCertificates);
|
||||
printf("certificateChains = %u\n", certificateChains);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wrapper function for the event ssl_conn_attempt.
|
||||
*
|
||||
* \param sslVersion the SSL version for which the event occured
|
||||
*
|
||||
* \see SSLProxy_Analyzer::SSL_Versions
|
||||
*/
|
||||
void SSL_Interpreter::fire_ssl_conn_attempt(uint16 sslVersion,
|
||||
TableVal* currentCipherSuites)
|
||||
{
|
||||
EventHandlerPtr event = ssl_conn_attempt;
|
||||
if ( event )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(new Val(sslVersion, TYPE_INT));
|
||||
vl->append(currentCipherSuites);
|
||||
|
||||
proxy->ConnectionEvent(event, vl);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wrapper function for the event ssl_conn_server_reply.
|
||||
*
|
||||
* \param sslVersion the SSL version for which the event occured
|
||||
*
|
||||
* \see SSLProxy_Analyzer::SSL_Versions
|
||||
*/
|
||||
void SSL_Interpreter::fire_ssl_conn_server_reply(uint16 sslVersion,
|
||||
TableVal* currentCipherSuites)
|
||||
{
|
||||
EventHandlerPtr event = ssl_conn_server_reply;
|
||||
if ( event )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(new Val(sslVersion, TYPE_INT));
|
||||
vl->append(currentCipherSuites);
|
||||
|
||||
proxy->ConnectionEvent(event, vl);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wrapper function for the event ssl_conn_established.
|
||||
*
|
||||
* \param sslVersion the SSL version for which the event occured
|
||||
* \param cipherSuite constant indicating the used SSL cipher suite
|
||||
*
|
||||
* \see SSLProxy_Analyzer::SSL_Versions, SSLv2_CipherSpecs and SSL3_1_CipherSpec.
|
||||
*/
|
||||
void SSL_Interpreter::fire_ssl_conn_established(uint16 sslVersion,
|
||||
uint32 cipherSuite)
|
||||
{
|
||||
EventHandlerPtr event = ssl_conn_established;
|
||||
if ( event )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(new Val(sslVersion, TYPE_INT));
|
||||
vl->append(new Val(cipherSuite, TYPE_COUNT));
|
||||
|
||||
proxy->ConnectionEvent(event, vl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wrapper function for the event ssl_conn_reused
|
||||
*
|
||||
* \param pData Pointer to a SSL_DataBlock which contains the SSL session ID
|
||||
* of the originating ssl session.
|
||||
*/
|
||||
void SSL_Interpreter::fire_ssl_conn_reused(const SSL_DataBlock* pData)
|
||||
{
|
||||
EventHandlerPtr event = ssl_conn_reused;
|
||||
if ( event )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(MakeSessionID(pData->data, pData->len));
|
||||
proxy->ConnectionEvent(event, vl);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Wrapper function for the event ssl_conn_alert
|
||||
*
|
||||
* \param sslVersion the SSL version for which the event occured
|
||||
* \param level constant indicating the level of severity
|
||||
* \param description constant indicating the type of alert/error
|
||||
*
|
||||
* \see SSLProxy_Analyzer::SSL_Versions, SSL3x_AlertLevel, SSL3_1_AlertDescription
|
||||
* and SSLv2_ErrorCodes.
|
||||
*/
|
||||
void SSL_Interpreter::fire_ssl_conn_alert(uint16 sslVersion, uint16 level,
|
||||
uint16 description)
|
||||
{
|
||||
if ( ssl_conn_alert )
|
||||
{
|
||||
EventHandlerPtr event = ssl_conn_alert;
|
||||
if ( event )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(new Val(sslVersion, TYPE_INT));
|
||||
vl->append(new Val(level, TYPE_COUNT));
|
||||
vl->append(new Val(description, TYPE_COUNT));
|
||||
|
||||
proxy->ConnectionEvent(event, vl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a session ID table. Returns an empty table
|
||||
// if len is zero.
|
||||
TableVal* SSL_Interpreter::MakeSessionID(const u_char* data, int len)
|
||||
{
|
||||
TableVal* sessionIDTable = new TableVal(SSL_sessionID);
|
||||
|
||||
if ( ! len )
|
||||
return sessionIDTable;
|
||||
|
||||
for ( int i = 0; i < len; i += 4 )
|
||||
{
|
||||
uint32 temp = (data[i] << 24) | (data[i + 1] << 16) |
|
||||
(data[i + 2] << 8) | data[i + 3];
|
||||
|
||||
Val* index = new Val(i / 4, TYPE_COUNT);
|
||||
|
||||
sessionIDTable->Assign(index, new Val(temp, TYPE_COUNT));
|
||||
|
||||
Unref(index);
|
||||
}
|
||||
|
||||
return sessionIDTable;
|
||||
}
|
||||
|
||||
|
||||
//--- SSL_InterpreterEndpoint -------------------------------------------------
|
||||
|
||||
/*!
|
||||
* The constructor.
|
||||
*
|
||||
* \param interpreter Pointer to the instance of an SSL_Interpreter to which
|
||||
* this endpoint belongs to.
|
||||
* \param is_orig true if this endpoint is the originator of the connection,
|
||||
* false otherwise
|
||||
* SC: an adjustment was made here since the endpoints are now assosciated with
|
||||
* TCP_Contents base objects rather than TCP_Endpoint.
|
||||
*/
|
||||
SSL_InterpreterEndpoint::SSL_InterpreterEndpoint(SSL_Interpreter* arg_interpreter,
|
||||
bool arg_is_orig )
|
||||
{
|
||||
interpreter = arg_interpreter;
|
||||
is_orig = arg_is_orig;
|
||||
proxyEndpoint = new Contents_SSL(interpreter->Proxy()->Conn(), is_orig);
|
||||
ourProxyEndpoint = true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The destructor.
|
||||
*/
|
||||
SSL_InterpreterEndpoint::~SSL_InterpreterEndpoint()
|
||||
{
|
||||
SetProxyEndpoint(0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return true if there's currently data pending for this endpoint,
|
||||
* false otherwise
|
||||
*/
|
||||
bool SSL_InterpreterEndpoint::isDataPending()
|
||||
{
|
||||
return proxyEndpoint->isDataPending();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Sets the peer of this endpoint.
|
||||
*
|
||||
* \param p Pointer to an interpreter endpoint which will be set as the peer
|
||||
* of this endpoint.
|
||||
*/
|
||||
void SSL_InterpreterEndpoint::SetPeer(SSL_InterpreterEndpoint* p)
|
||||
{
|
||||
peer = p;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Sets the proxy endpoint of this endpoint.
|
||||
*
|
||||
* \param p Pointer to a Contents_SSL analyzer which will be set as the proxy endpoint
|
||||
* of this endpoint.
|
||||
*/
|
||||
void SSL_InterpreterEndpoint::SetProxyEndpoint(Contents_SSL* p)
|
||||
{
|
||||
if ( ourProxyEndpoint )
|
||||
{
|
||||
proxyEndpoint->Done();
|
||||
delete proxyEndpoint;
|
||||
ourProxyEndpoint = false;
|
||||
}
|
||||
|
||||
proxyEndpoint = p;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return is_orig true if this endpoint is the originator of the connection,
|
||||
* false otherwise
|
||||
*/
|
||||
int SSL_InterpreterEndpoint::IsOrig() const
|
||||
{
|
||||
return is_orig;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return the peer of this endpoint
|
||||
*/
|
||||
SSL_InterpreterEndpoint* SSL_InterpreterEndpoint::Peer() const
|
||||
{
|
||||
return peer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return the interpreter of this endpoint
|
||||
*/
|
||||
SSL_Interpreter* SSL_InterpreterEndpoint::Interpreter() const
|
||||
{
|
||||
return interpreter;
|
||||
}
|
||||
|
||||
// --- CertStore --------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* The constructor.
|
||||
*
|
||||
* \param ip ip adress where this certificate came from
|
||||
* \param port port number where this certificate came from
|
||||
* \param hash hahssum for this certificate
|
||||
* \param size of this certificate in bytes
|
||||
*/
|
||||
CertStore::CertStore(uint32 ip, uint32 arg_port, hash_t hash, int size)
|
||||
{
|
||||
ip_addr = ip;
|
||||
certHash = hash;
|
||||
certSize = size;
|
||||
isValid = -1;
|
||||
changes = 0;
|
||||
port = arg_port;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method can be used to compare certificates by certain criterias.
|
||||
*
|
||||
* \param hash hashsum of the certificate to compare
|
||||
* \param size size of the certificate to compare
|
||||
*
|
||||
* \return true if the criterias match, false otherwise
|
||||
*/
|
||||
bool CertStore::isSameCert(hash_t hash, int length)
|
||||
{
|
||||
return hash == certHash && length == certSize;
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
// $Id: SSLInterpreter.h 5988 2008-07-19 07:02:12Z vern $
|
||||
|
||||
#ifndef sslinterpreter_h
|
||||
#define sslinterpreter_h
|
||||
|
||||
#include "util.h"
|
||||
#include "SSLProxy.h"
|
||||
|
||||
// --- forward declarations ----------------------------------------------------
|
||||
|
||||
class SSLProxy_Analyzer;
|
||||
class Contents_SSL;
|
||||
class SSL_InterpreterEndpoint;
|
||||
class SSL_DataBlock;
|
||||
|
||||
// --- SSL_Interpreter --------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* \brief This class is the abstract base-class for the different ssl
|
||||
* interpreters used for the different ssl versions.
|
||||
*
|
||||
* Since there is currently no support in Bro for a change of the connection
|
||||
* type (IMAP -> TLS, for example), we decided not to inherit from the class
|
||||
* Connection. This way, we can easily switch to SSLv3x after we've seen (and
|
||||
* analyzed) a SSLv2 client hello record with a version number > SSLv2.
|
||||
*
|
||||
* There currently two (non-abstract) interpreters: SSLv2_Interpreter and
|
||||
* SSLv3_Interpreter. The first one supports SSL 2.0, the second one supports
|
||||
* both SSL 3.0 and SSL 3.1/TLS 1.0.
|
||||
*
|
||||
* See SSLProxy_Analyzer for additional information.
|
||||
*/
|
||||
class SSL_Interpreter {
|
||||
public:
|
||||
SSL_Interpreter(SSLProxy_Analyzer* proxy);
|
||||
virtual ~SSL_Interpreter();
|
||||
|
||||
static uint32 analyzedCertificates; ///< how often analyzeCertificate() has been called
|
||||
static uint32 verifiedCertificates; ///< how many certificates have actually been verified
|
||||
static uint32 failedCertificates; ///< how many certificates have failed verification
|
||||
static uint32 certificateChains; ///< counter for certificate chains
|
||||
|
||||
// In order to initialize the correct SSL_InterpreterEndpoints,
|
||||
// override it in the corresponding subclass.
|
||||
virtual void BuildInterpreterEndpoints() = 0;
|
||||
virtual void Init();
|
||||
|
||||
SSL_InterpreterEndpoint* Orig() const;
|
||||
SSL_InterpreterEndpoint* Resp() const;
|
||||
SSLProxy_Analyzer* Proxy() const;
|
||||
int Is_Orig(SSL_InterpreterEndpoint* p) const;
|
||||
|
||||
virtual void analyzeCertificate(SSL_InterpreterEndpoint* s,
|
||||
const u_char* data, int length,
|
||||
uint8 type, bool isChain);
|
||||
|
||||
void Weird(const char* name) const;
|
||||
|
||||
static void printStats();
|
||||
|
||||
void fire_ssl_conn_attempt(uint16 sslVersion,
|
||||
TableVal* currentCipherSuites);
|
||||
void fire_ssl_conn_server_reply(uint16 sslVersion,
|
||||
TableVal* currentCipherSuites);
|
||||
void fire_ssl_conn_established(uint16 sslVersion, uint32 cipherSuite);
|
||||
void fire_ssl_conn_reused(const SSL_DataBlock* pData);
|
||||
void fire_ssl_conn_alert(uint16 sslVersion, uint16 level,
|
||||
uint16 description);
|
||||
|
||||
protected:
|
||||
TableVal* MakeSessionID(const u_char* data, int len);
|
||||
|
||||
SSLProxy_Analyzer* proxy;
|
||||
SSL_InterpreterEndpoint* orig;
|
||||
SSL_InterpreterEndpoint* resp;
|
||||
};
|
||||
|
||||
// --- SSL_InterpreterEndpoint ------------------------------------------------
|
||||
|
||||
/*!
|
||||
* \brief This abstract class represents the SSL_InterpreterEndpoints for the
|
||||
* SSL_Interpreter.
|
||||
*
|
||||
* The key-method is Deliver() which receives the ssl records
|
||||
* from the SSLProxy_Analyzer. So overwrite the Deliver()-method and do
|
||||
* whatever analysis on the record content (and/or pass it to the corresponding
|
||||
* SSL_Interpreter).
|
||||
*/
|
||||
class SSL_InterpreterEndpoint {
|
||||
public:
|
||||
SSL_InterpreterEndpoint(SSL_Interpreter* interpreter, bool is_orig);
|
||||
virtual ~SSL_InterpreterEndpoint();
|
||||
|
||||
/**This method is called by corresponding SSLProxy_Analyzer and
|
||||
* delivers the data.
|
||||
* @param t time, when the segment was received by bro (?)
|
||||
* @param len length of TCP-Segment
|
||||
* @param data content of TCP-Segment
|
||||
*/
|
||||
virtual void Deliver(int len, const u_char* data) = 0;
|
||||
bool isDataPending();
|
||||
void SetPeer(SSL_InterpreterEndpoint* p);
|
||||
int IsOrig() const;
|
||||
SSL_InterpreterEndpoint* Peer() const;
|
||||
SSL_Interpreter* Interpreter() const;
|
||||
|
||||
Contents_SSL* GetProxyEndpoint() { return proxyEndpoint; }
|
||||
|
||||
void SetProxyEndpoint(Contents_SSL* proxyEndpoint);
|
||||
|
||||
protected:
|
||||
SSL_Interpreter* interpreter; ///< Pointer to the SSL_Interpreter to which this endpoint belongs to
|
||||
SSL_InterpreterEndpoint* peer; ///< Pointer to the peer of this endpoint
|
||||
Contents_SSL* proxyEndpoint; ///< Pointer to the corresponding Contents_SSL
|
||||
bool ourProxyEndpoint; // true if we need to delete the proxyEndpoint
|
||||
int is_orig; ///< true if this endpoint is the originator of the connection, false otherwise
|
||||
};
|
||||
|
||||
// --- class CertStore --------------------------------------------------------
|
||||
/*!
|
||||
* \brief This class is used to store some information about a X509 certificate.
|
||||
*
|
||||
* To save memory, we only store some characteristic criterias about a
|
||||
* certificate, that's currently it's size and a hashsum.
|
||||
*
|
||||
* \note This class is currently <b>experimental</b>.
|
||||
*/
|
||||
class CertStore {
|
||||
public:
|
||||
uint32 ip_addr; ///< ip address where this certificate is from
|
||||
uint32 port; ///< port number where this certificate is from
|
||||
|
||||
int certSize; ///< size of the certificate in bytes
|
||||
hash_t certHash; ///< hashsum obver the entire certificate
|
||||
int isValid; ///< boolean value indicating if the certificate is valid
|
||||
int changes; ///< counter for how often this certificate has changed for the above ip + port number
|
||||
|
||||
CertStore(uint32 ip, uint32 port, hash_t hash, int size);
|
||||
bool isSameCert(hash_t hash, int length);
|
||||
};
|
||||
|
||||
#endif
|
830
src/SSLProxy.cc
830
src/SSLProxy.cc
|
@ -1,830 +0,0 @@
|
|||
// $Id: SSLProxy.cc 6008 2008-07-23 00:24:22Z vern $
|
||||
|
||||
#include "SSLProxy.h"
|
||||
#include "SSLv3.h"
|
||||
#include "SSLv2.h"
|
||||
|
||||
// --- Initalization of static variables --------------------------------------
|
||||
|
||||
uint SSLProxy_Analyzer::totalPackets = 0;
|
||||
uint SSLProxy_Analyzer::totalRecords = 0;
|
||||
uint SSLProxy_Analyzer::nonSSLConnections = 0;
|
||||
|
||||
// --- SSL_DataBlock --------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* This constructor will allocate a block of data on the heap. If min_len is
|
||||
* given, it will determine the minimum size of the new block. The data block
|
||||
* referenced by data will be then be copied into the new block.
|
||||
*
|
||||
* \param data Pointer to the data which will be copied into the newly
|
||||
* allocated heap block.
|
||||
* \param len Length of the data block to copy.
|
||||
* \param min_len The minimum size of data to allocate on the heap, can be omitted.
|
||||
*/
|
||||
|
||||
SSL_DataBlock::SSL_DataBlock(const u_char* arg_data, int len, int min_len)
|
||||
{
|
||||
// For performance reasons, we allocate at least min_len.
|
||||
if ( len < min_len )
|
||||
{
|
||||
data = new u_char[min_len];
|
||||
size = min_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = new u_char[len];
|
||||
this->size = len;
|
||||
}
|
||||
|
||||
memcpy(data, arg_data, len);
|
||||
this->len = len;
|
||||
next = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This is an experimental function which will print the contents of the
|
||||
* internal data block in a human-readable fashion to a stream.
|
||||
*
|
||||
* \param stream The stream for printing the data block to.
|
||||
*/
|
||||
|
||||
void SSL_DataBlock::toStream(FILE* stream) const
|
||||
{
|
||||
if ( len <= 0 )
|
||||
return;
|
||||
|
||||
int idx;
|
||||
for ( idx = 0; idx < len-1; ++idx )
|
||||
fprintf(stream, "%02X:", data[idx]);
|
||||
|
||||
fprintf(stream, "%02X", data[idx]);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This is an experimental function which will print the contents of the
|
||||
* internal data block in a human-readable fashion to a string.
|
||||
*
|
||||
* \return A string which has to be freed by the caller.
|
||||
*/
|
||||
|
||||
char* SSL_DataBlock::toString() const
|
||||
{
|
||||
if ( len <= 0 )
|
||||
{
|
||||
// Currently, we return an empty string if data block is empty.
|
||||
char* pDummy = new char[1];
|
||||
pDummy[0] = '\0';
|
||||
return pDummy;
|
||||
}
|
||||
|
||||
char* pString = new char[len*3];
|
||||
char* pItx = pString;
|
||||
|
||||
int idx;
|
||||
for ( idx = 0; idx < len-1; ++idx )
|
||||
{
|
||||
sprintf(pItx, "%02X:", data[idx]);
|
||||
pItx += 3;
|
||||
}
|
||||
|
||||
sprintf(pItx, "%02X", data[idx]);
|
||||
|
||||
return pString;
|
||||
}
|
||||
|
||||
// --- SSL_RecordBuilder ------------------------------------------------------
|
||||
|
||||
uint SSL_RecordBuilder::maxAllocCount = 0;
|
||||
uint SSL_RecordBuilder::maxFragmentCount = 0;
|
||||
uint SSL_RecordBuilder::fragmentedHeaders = 0;
|
||||
|
||||
/*!
|
||||
* The constructor takes an Contents_SSL as parameter. Whenever a SSL
|
||||
* record has been reassembled, the DoDeliver() function of this
|
||||
* Contents_SSL will be called.
|
||||
*
|
||||
* \param sslEndpoint The Contents_SSL to which this instance of
|
||||
* SSL_RecordBuilder is bound.
|
||||
*/
|
||||
|
||||
SSL_RecordBuilder::SSL_RecordBuilder(Contents_SSL* arg_sslEndpoint)
|
||||
{
|
||||
head = tail = 0;
|
||||
currentSize = 0;
|
||||
expectedSize = -1; // -1 means we don't know yet
|
||||
hasPendingData = false;
|
||||
fragmentCounter = 0;
|
||||
neededSize = 5; // we need at least 5 bytes to determine version
|
||||
|
||||
sslEndpoint = arg_sslEndpoint;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The destructor frees the chain of SSL_DataBlocks.
|
||||
*/
|
||||
|
||||
SSL_RecordBuilder::~SSL_RecordBuilder()
|
||||
{
|
||||
// Free the data chain.
|
||||
SSL_DataBlock* idx = head;
|
||||
SSL_DataBlock* rm;
|
||||
|
||||
while ( idx )
|
||||
{
|
||||
rm = idx;
|
||||
idx = idx->next;
|
||||
delete rm;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This function is the main entry point of the class. Call it with a segment
|
||||
* of data to process.
|
||||
*
|
||||
* \param data pointer to a data segment that will be reassembled
|
||||
* \param length length of the data segment to be reassembled
|
||||
*
|
||||
* \return true if succesfull, false otherwise
|
||||
*/
|
||||
|
||||
bool SSL_RecordBuilder::addSegment(const u_char* data, int length)
|
||||
{
|
||||
while ( length > 0 )
|
||||
{
|
||||
if ( ! head )
|
||||
{
|
||||
// This is the first fragment of a SSLv2 record,
|
||||
// so we analyze the header.
|
||||
|
||||
// Special case: SSL header has been fragmented.
|
||||
if ( length < neededSize )
|
||||
{
|
||||
// We can't determine the record size yet,
|
||||
// so we just add this stuff.
|
||||
++fragmentedHeaders;
|
||||
head = tail = new SSL_DataBlock(data, length,
|
||||
MIN_ALLOC_SIZE);
|
||||
currentSize += length;
|
||||
expectedSize = -1; // special meaning
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the expected length of this record.
|
||||
if ( ! computeExpectedSize(data, length) )
|
||||
return false;
|
||||
|
||||
if ( neededSize > expectedSize )
|
||||
{
|
||||
sslEndpoint->Weird("SSL_RecordBuilder::addSegment neededSize > expectedSize");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( tail != 0 )
|
||||
{
|
||||
sslEndpoint->Parent()->Weird("SSL_RecordBuilder::addSegment tail != 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( length > expectedSize )
|
||||
{
|
||||
// No fragmentation -> no memory-reallocation.
|
||||
// We have additional data pending.
|
||||
hasPendingData = true;
|
||||
sslEndpoint->DoDeliver(expectedSize, data);
|
||||
length -= expectedSize;
|
||||
data += expectedSize;
|
||||
expectedSize = -1;
|
||||
}
|
||||
|
||||
else if ( length == expectedSize )
|
||||
{
|
||||
// No fragmentation -> no memory-reallocation.
|
||||
// No additional data pending.
|
||||
hasPendingData = false;
|
||||
sslEndpoint->DoDeliver(expectedSize, data);
|
||||
length -= expectedSize;
|
||||
data += expectedSize;
|
||||
expectedSize = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
{
|
||||
// First fragment of a record.
|
||||
head = tail = new SSL_DataBlock(data, length,
|
||||
MIN_ALLOC_SIZE);
|
||||
currentSize += length;
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// ! head.
|
||||
// We already have some data, so add the current
|
||||
// segment special case.
|
||||
if ( expectedSize < 0 )
|
||||
{
|
||||
// We don't know the expected size of
|
||||
// this record yet.
|
||||
if ( currentSize + length < neededSize )
|
||||
{
|
||||
// We still can't determine the expected size,
|
||||
// so we just add the current fragment.
|
||||
addData(data, length);
|
||||
break;
|
||||
}
|
||||
|
||||
// Now we can determine the expected size the
|
||||
// header has been fragmented, so we have to
|
||||
// reassemble it.
|
||||
uint8 Header[neededSize];
|
||||
memcpy(Header, head->data, head->len);
|
||||
memcpy(Header + head->len, data, neededSize - head->len);
|
||||
if ( ! computeExpectedSize(Header, neededSize) )
|
||||
{
|
||||
// Since neededSize <= MIN_ALLOC_SIZE,
|
||||
// we free only head.
|
||||
delete head;
|
||||
head = tail = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( neededSize > expectedSize )
|
||||
{
|
||||
sslEndpoint->Parent()->Weird("SSL_RecordBuilder::addSegment neededSize > expectedSize");
|
||||
return false;
|
||||
}
|
||||
|
||||
// No break, go on with this packet.
|
||||
}
|
||||
|
||||
if ( currentSize + length == expectedSize )
|
||||
{ // this is exactly the last segment of the record
|
||||
hasPendingData = false;
|
||||
|
||||
// Create a continuous data structure and call
|
||||
// DoDeliver().
|
||||
u_char* pBlock = assembleBlocks(data, length);
|
||||
sslEndpoint->DoDeliver(expectedSize, pBlock);
|
||||
delete [] pBlock;
|
||||
expectedSize = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
else if ( currentSize + length < expectedSize )
|
||||
{ // another (middle) segment
|
||||
if ( length <= MIN_FRAGMENT_SIZE )
|
||||
sslEndpoint->Parent()->Weird("SSLProxy: Excessive small TCP Segment!");
|
||||
addData(data, length);
|
||||
break;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// This is the last fragment of the current record,
|
||||
// but there's more data in this segment.
|
||||
int deltaSize = expectedSize - currentSize;
|
||||
hasPendingData = true;
|
||||
|
||||
// Create a continuous data structure and call
|
||||
// DoDeliver().
|
||||
u_char* pBlock = assembleBlocks(data, deltaSize);
|
||||
sslEndpoint->DoDeliver(expectedSize, pBlock);
|
||||
delete [] pBlock;
|
||||
expectedSize = -1;
|
||||
|
||||
// Process the rest.
|
||||
length -= deltaSize;
|
||||
data += deltaSize;
|
||||
}
|
||||
} // while
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This function is called internally by addSegment(), and add's a new SSL
|
||||
* record fragment to the internally used list of SSL_DataBlocks. Note that
|
||||
* the data will be copied!
|
||||
*
|
||||
* \param data pointer to the data that will be added
|
||||
* \param length length of the data that will be added
|
||||
*/
|
||||
|
||||
inline void SSL_RecordBuilder::addData(const u_char* data, int length)
|
||||
{
|
||||
++fragmentCounter;
|
||||
|
||||
// Check if there's some space left in the last datablock.
|
||||
int bytesLeft = tail->size - tail->len;
|
||||
if ( bytesLeft > 0 )
|
||||
{
|
||||
// There's some space left in the last data block.
|
||||
if ( bytesLeft >= length )
|
||||
{
|
||||
// We can store all bytes in the last data block.
|
||||
memcpy(tail->data + tail->len, data, length);
|
||||
tail->len += length;
|
||||
currentSize += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We cannot store all bytes in the last data block,
|
||||
// so we also need to add a new one.
|
||||
memcpy(tail->data + tail->len, data, bytesLeft);
|
||||
tail->len = tail->size;
|
||||
currentSize += length;
|
||||
|
||||
data += bytesLeft;
|
||||
length -= bytesLeft;
|
||||
|
||||
tail->next = new SSL_DataBlock(data, length, MIN_ALLOC_SIZE);
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// Last data block is full.
|
||||
tail->next = new SSL_DataBlock(data, length, MIN_ALLOC_SIZE);
|
||||
tail = tail->next;
|
||||
currentSize += length;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This function is called internally by addSegment(), whenever a SSL record
|
||||
* has been fully received. It creates a single data block from the list of
|
||||
* SSL record fragments while freeing them.
|
||||
*
|
||||
* \param data pointer to the last SSL record fragment
|
||||
* \param length size of the last SSL record fragment
|
||||
*
|
||||
* \return pointer to a data block which contains the reassembled SSL record
|
||||
*/
|
||||
|
||||
u_char* SSL_RecordBuilder::assembleBlocks(const u_char* data, int length)
|
||||
{
|
||||
// We don't store the last SSL record fragment in a DataBlock,
|
||||
// instead we get it directly as parameter.
|
||||
u_char* dataptr = new u_char[currentSize + length];
|
||||
u_char* nextseg = dataptr;
|
||||
|
||||
SSL_DataBlock* idx = head;
|
||||
SSL_DataBlock* rm;
|
||||
uint allocCounter = 0;
|
||||
|
||||
while ( idx )
|
||||
{
|
||||
++allocCounter;
|
||||
memcpy(nextseg, idx->data, idx->len);
|
||||
nextseg += idx->len;
|
||||
rm = idx;
|
||||
idx = idx->next;
|
||||
delete rm;
|
||||
}
|
||||
|
||||
// The last fragment isn't stored in a datablock.
|
||||
memcpy(nextseg, data, length);
|
||||
|
||||
// The first and last fragments aren't counted.
|
||||
fragmentCounter += 2;
|
||||
|
||||
// Update statistics.
|
||||
if ( allocCounter > maxAllocCount )
|
||||
maxAllocCount = allocCounter;
|
||||
|
||||
if ( fragmentCounter > maxFragmentCount )
|
||||
maxFragmentCount = fragmentCounter;
|
||||
|
||||
fragmentCounter = 0;
|
||||
currentSize = 0;
|
||||
head = tail = 0;
|
||||
|
||||
return dataptr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called internally by computeExpectedSize(), when the SSL
|
||||
* record format has not been determined yet. It tries to do so by using
|
||||
* heuristics, since there's no definitive way to distinguish SSLv2 vs. SSLv3
|
||||
* record headers.
|
||||
*
|
||||
* \param data pointer to a data block containing the SSL record to analyze
|
||||
* \param length length of the SSL record to analyze, has to be >= neededSize!
|
||||
*
|
||||
* \return
|
||||
* - 2 for SSLv2 record format
|
||||
* - 3 for SSLv3 record format
|
||||
* - -1 if an error occurred
|
||||
*/
|
||||
|
||||
int SSL_RecordBuilder::analyzeSSLRecordFormat(const u_char* data, int length)
|
||||
{
|
||||
// We have to use heuristics for this one.
|
||||
|
||||
if ( length < neededSize )
|
||||
{
|
||||
sslEndpoint->Parent()->Weird("SSLProxy: analyzeSSLRecordFormat length < neededSize");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool found_ssl3x = 0;
|
||||
bool found_ssl2x = 0;
|
||||
|
||||
// SSLv3x-check.
|
||||
SSL3_1_ContentType ct = SSL3_1_ContentType(uint8(*data));
|
||||
switch ( ct ) {
|
||||
case SSL3_1_TYPE_CHANGE_CIPHER_SPEC:
|
||||
case SSL3_1_TYPE_ALERT:
|
||||
case SSL3_1_TYPE_HANDSHAKE:
|
||||
case SSL3_1_TYPE_APPLICATION_DATA:
|
||||
{
|
||||
sslEndpoint->sslVersion = ((data[1]) << 8) | data[2];
|
||||
uint16 v = sslEndpoint->sslVersion;
|
||||
if ( v == uint16(SSLProxy_Analyzer::SSLv30) ||
|
||||
v == uint16(SSLProxy_Analyzer::SSLv31) )
|
||||
found_ssl3x = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SSLv2 check.
|
||||
// We look for CLIENT-HELLOs, SERVER-HELLOs and ERRORs.
|
||||
const u_char* pContents = data;
|
||||
uint offset = 0;
|
||||
uint16 size = 0;
|
||||
if ( (data[0] & 0x80) > 0 )
|
||||
{ // we have a two-byte record header
|
||||
offset = 2;
|
||||
size = (((data[0] & 0x7f) << 8) | data[1]) + 2;
|
||||
}
|
||||
else
|
||||
{ // we have a three-byte record header
|
||||
offset = 3;
|
||||
size = (((data[0] & 0x3f) << 8) | data[1]) + 3;
|
||||
}
|
||||
pContents += offset;
|
||||
|
||||
switch ( SSLv2_MessageTypes(pContents[0]) ) {
|
||||
case SSLv2_MT_ERROR:
|
||||
if ( size == SSLv2_ERROR_RECORD_SIZE + offset)
|
||||
{
|
||||
found_ssl2x = true;
|
||||
sslEndpoint->sslVersion =
|
||||
uint16(SSLProxy_Analyzer::SSLv20);
|
||||
}
|
||||
break;
|
||||
|
||||
case SSLv2_MT_CLIENT_HELLO:
|
||||
{
|
||||
sslEndpoint->sslVersion =
|
||||
uint16(pContents[1] << 8) | pContents[2];
|
||||
uint16 v = sslEndpoint->sslVersion;
|
||||
|
||||
if ( v == SSLProxy_Analyzer::SSLv20 ||
|
||||
v == SSLProxy_Analyzer::SSLv30 ||
|
||||
v == SSLProxy_Analyzer::SSLv31 )
|
||||
found_ssl2x = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case SSLv2_MT_SERVER_HELLO:
|
||||
{
|
||||
sslEndpoint->sslVersion =
|
||||
uint16(pContents[3] << 8) | pContents[4];
|
||||
uint16 v = sslEndpoint->sslVersion;
|
||||
|
||||
if ( v == SSLProxy_Analyzer::SSLv20 ||
|
||||
v == SSLProxy_Analyzer::SSLv30 ||
|
||||
v == SSLProxy_Analyzer::SSLv31 )
|
||||
found_ssl2x = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Consistency checks.
|
||||
if ( (found_ssl3x || found_ssl2x) == false )
|
||||
{
|
||||
sslEndpoint->Parent()->Weird("SSLProxy: Could not determine SSL version!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( (found_ssl3x && found_ssl2x) == true )
|
||||
{
|
||||
sslEndpoint->Parent()->Weird("SSLProxy: Found ambigous SSL version!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( found_ssl2x )
|
||||
return 2;
|
||||
else
|
||||
return 3;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called internally by addSegment() to determine the expected
|
||||
* size of a SSL record.
|
||||
*
|
||||
* \param data pointer to the SSL record to analyze
|
||||
* \param length length of the SSL record to analyze
|
||||
*
|
||||
* \return true if succesfull, false otherwise
|
||||
*/
|
||||
|
||||
bool SSL_RecordBuilder::computeExpectedSize(const u_char* data, int length)
|
||||
{
|
||||
if ( sslEndpoint->sslRecordVersion < 0 )
|
||||
{
|
||||
// We don't know the ssl record format yet, so we try
|
||||
// to find out.
|
||||
sslEndpoint->sslRecordVersion =
|
||||
analyzeSSLRecordFormat(data, length);
|
||||
|
||||
if ( sslEndpoint->sslRecordVersion != 2 &&
|
||||
sslEndpoint->sslRecordVersion != 3 )
|
||||
// We could not determine the ssl record version.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the expected length of this record.
|
||||
if ( sslEndpoint->sslRecordVersion == 2 )
|
||||
{
|
||||
if ( (data[0] & 0x80) > 0 )
|
||||
// We have a two-byte record header.
|
||||
expectedSize = (((data[0] & 0x7f) << 8) | data[1]) + 2;
|
||||
else
|
||||
// We have a three-byte record header.
|
||||
expectedSize = (((data[0] & 0x3f) << 8) | data[1]) + 3;
|
||||
}
|
||||
|
||||
else if ( sslEndpoint->sslRecordVersion == 3 )
|
||||
expectedSize = ((data[3] << 8) | data[4]) + 5;
|
||||
|
||||
if ( expectedSize < neededSize )
|
||||
{
|
||||
// This should never happen (otherwise: UNTESTED).
|
||||
sslEndpoint->Parent()->Weird( "SSLProxy: expectedSize < neededSize in RecordBuilder!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// --- SSL_Connection_Proxy ---------------------------------------------------
|
||||
|
||||
bool SSLProxy_Analyzer::bInited = false;
|
||||
|
||||
SSLProxy_Analyzer::SSLProxy_Analyzer(Connection* conn)
|
||||
: TCP_ApplicationAnalyzer(AnalyzerTag::SSL, conn)
|
||||
{
|
||||
sSLv2Interpreter = new SSLv2_Interpreter(this);
|
||||
sSLv3xInterpreter = new SSLv3_Interpreter(this);
|
||||
sSLInterpreter = 0;
|
||||
bPassThrough = false;
|
||||
if ( ! bInited )
|
||||
{
|
||||
BuildCipherDict();
|
||||
bInited = true;
|
||||
}
|
||||
|
||||
AddSupportAnalyzer(sslpeo = new Contents_SSL(conn, true));
|
||||
AddSupportAnalyzer(sslper = new Contents_SSL(conn, false));
|
||||
}
|
||||
|
||||
SSLProxy_Analyzer::~SSLProxy_Analyzer()
|
||||
{
|
||||
delete sSLv2Interpreter;
|
||||
delete sSLv3xInterpreter;
|
||||
}
|
||||
|
||||
void SSLProxy_Analyzer::Init()
|
||||
{
|
||||
TCP_ApplicationAnalyzer::Init();
|
||||
|
||||
sSLv2Interpreter->Init();
|
||||
sSLv3xInterpreter->Init();
|
||||
|
||||
sSLv2Interpreter->Orig()
|
||||
->SetProxyEndpoint(sSLv3xInterpreter->Orig()->GetProxyEndpoint());
|
||||
sSLv2Interpreter->Resp()
|
||||
->SetProxyEndpoint(sSLv3xInterpreter->Resp()->GetProxyEndpoint());
|
||||
}
|
||||
|
||||
void SSLProxy_Analyzer::BuildCipherDict()
|
||||
{
|
||||
for ( uint idx = 0; idx < SSL_CipherSpecs_Count; ++idx )
|
||||
{
|
||||
HashKey h(static_cast<bro_uint_t>(SSL_CipherSpecs[idx].identifier));
|
||||
SSL_CipherSpecDict.Insert(&h, &SSL_CipherSpecs[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
void SSLProxy_Analyzer::NewSSLRecord(Contents_SSL* endp,
|
||||
int len, const u_char* data)
|
||||
{
|
||||
// This is to extract only SSLv2 traffic.
|
||||
if ( recordSSLv2Traffic )
|
||||
{
|
||||
uint16 sslVersion = 0;
|
||||
if ( (data[0] & 0x80) > 0 )
|
||||
// We have a two-byte record header.
|
||||
sslVersion = (data[3] << 8) | data[4];
|
||||
else
|
||||
// We have a three-byte record header.
|
||||
sslVersion = (data[4] << 8) | data[5];
|
||||
|
||||
if ( ! endp->IsSSLv2Record() ||
|
||||
sslVersion != SSLProxy_Analyzer::SSLv20 )
|
||||
{
|
||||
SetSkip(1);
|
||||
Conn()->SetRecordPackets(0);
|
||||
Conn()->SetRecordContents(0);
|
||||
// FIXME: Could do memory cleanup here.
|
||||
}
|
||||
else
|
||||
// No analysis - only recording.
|
||||
SetSkip(1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( bPassThrough )
|
||||
{
|
||||
DoDeliver(len, data, endp->IsOrig());
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! endp->IsSSLv2Record() )
|
||||
{
|
||||
// It's TLS or SSLv3, so we are done ...
|
||||
sSLInterpreter = sSLv3xInterpreter;
|
||||
bPassThrough = true;
|
||||
// Tell the other record builder we have SSLv3x.
|
||||
endp->sslRecordVersion = 3;
|
||||
DoDeliver(len, data, endp->IsOrig());
|
||||
}
|
||||
|
||||
else
|
||||
{ // we have a SSLv2 record ...
|
||||
sSLInterpreter = sSLv2Interpreter;
|
||||
|
||||
// Check whether it's the first or second we've seen ...
|
||||
if ( sslpeo->VersionRecognized() &&
|
||||
sslper->VersionRecognized() )
|
||||
{
|
||||
// Second record we've seen.
|
||||
// O.K. Both endpoints recognized the version.
|
||||
// So this needs to be an SSLv2-Connection ...
|
||||
bPassThrough = true;
|
||||
DoDeliver(len, data, endp->IsOrig());
|
||||
}
|
||||
|
||||
// First record we see.
|
||||
// The next one may be SSLv2 or SSLv3x,
|
||||
// we don't know yet ...
|
||||
else if ( endp->sslVersion == SSLv20 )
|
||||
{
|
||||
// The client supports only SSLv2, so we're done.
|
||||
bPassThrough = true;
|
||||
endp->sslRecordVersion = 2;
|
||||
endp->sslVersion = SSLv20;
|
||||
DoDeliver(len, data, endp->IsOrig());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
bPassThrough = false;
|
||||
DoDeliver(len, data, endp->IsOrig());
|
||||
|
||||
// Transfer the state of the SSLv2-Interpreter
|
||||
// to the state of the SSLv3x-Interpreter ...
|
||||
if ( ((SSLv2_Interpreter*) sSLInterpreter)->ConnState() == CLIENT_HELLO_SEEN )
|
||||
((SSLv3_Interpreter*) sSLv3xInterpreter)->SetState(SSL3_1_STATE_CLIENT_HELLO_SENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSLProxy_Analyzer::DoDeliver(int len, const u_char* data, bool orig)
|
||||
{
|
||||
if ( orig )
|
||||
sSLInterpreter->Orig()->Deliver(len, data);
|
||||
else
|
||||
sSLInterpreter->Resp()->Deliver(len, data);
|
||||
}
|
||||
|
||||
void SSLProxy_Analyzer::printStats()
|
||||
{
|
||||
printf("SSLProxy_Analyzer::totalPackets = %u\n", totalPackets);
|
||||
printf("SSLProxy_Analyzer::totalRecords = %u\n", totalRecords);
|
||||
printf("SSLProxy_Analyzer::nonSSLConnections = %u\n", nonSSLConnections);
|
||||
}
|
||||
|
||||
|
||||
void SSLProxy_Analyzer::Weak(const char* name)
|
||||
{
|
||||
if ( ssl_conn_weak )
|
||||
Event(ssl_conn_weak, name);
|
||||
}
|
||||
|
||||
// --- Contents_SSL ------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* mod Contents_SSL::Contents_SSL( TCP_Endpoint* arg_endpt, int stop_on_gap )
|
||||
* : TCP_Contents( arg_conn, stop_on_gap, punt_on_partial )
|
||||
*/
|
||||
|
||||
Contents_SSL::Contents_SSL(Connection* conn, bool orig)
|
||||
: TCP_SupportAnalyzer(AnalyzerTag::Contents_SSL, conn, orig)
|
||||
{
|
||||
sslRecordBuilder = new SSL_RecordBuilder(this);
|
||||
bVersionRecognized = false;
|
||||
bIsSSLv2Record = false;
|
||||
|
||||
sslRecordVersion = -1; // -1 means we don't know yet
|
||||
sslVersion = 0; // 0 means we don't know yet
|
||||
}
|
||||
|
||||
Contents_SSL::~Contents_SSL()
|
||||
{
|
||||
delete sslRecordBuilder;
|
||||
}
|
||||
|
||||
bool Contents_SSL::isDataPending()
|
||||
{
|
||||
return sslRecordBuilder->isDataPending();
|
||||
}
|
||||
|
||||
void Contents_SSL::DeliverStream(int len, const u_char* data, bool orig)
|
||||
{
|
||||
TCP_SupportAnalyzer::DeliverStream(len, data, orig);
|
||||
|
||||
TCP_Analyzer* tcp = static_cast<TCP_ApplicationAnalyzer *>(Parent())->TCP();
|
||||
assert(tcp);
|
||||
|
||||
if ( tcp->HadGap(orig) || tcp->IsPartial() )
|
||||
return;
|
||||
|
||||
++SSLProxy_Analyzer::totalPackets;
|
||||
|
||||
TCP_Endpoint* endp = orig ? tcp->Orig() : tcp->Resp();
|
||||
|
||||
#if 0
|
||||
// FIXME: What's this???
|
||||
int ack = endp->AckSeq() - endp->StartSeq();
|
||||
int top_seq = seq + len;
|
||||
|
||||
if ( top_seq <= ack )
|
||||
// There is no new data in this packet.
|
||||
return;
|
||||
#endif
|
||||
|
||||
if ( len <= 0 )
|
||||
return;
|
||||
|
||||
// No further processing if we have a partial connection.
|
||||
if ( endp->state == TCP_ENDPOINT_PARTIAL ||
|
||||
endp->peer->state == TCP_ENDPOINT_PARTIAL )
|
||||
{
|
||||
Parent()->SetSkip(1);
|
||||
Conn()->SetRecordPackets(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! sslRecordBuilder->addSegment(data, len) )
|
||||
{
|
||||
// The RecordBuilder failed to determine the SSL record version,
|
||||
// so we can't analyze this connection any further.
|
||||
++SSLProxy_Analyzer::nonSSLConnections;
|
||||
Parent()->Weird("SSL: Skipping connection (not an SSL connection?!)!");
|
||||
Parent()->SetSkip(1);
|
||||
Conn()->SetRecordPackets(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the RecordBuilder with a complete SSL record.
|
||||
void Contents_SSL::DoDeliver(int len, const u_char* data)
|
||||
{
|
||||
++SSLProxy_Analyzer::totalRecords;
|
||||
|
||||
bIsSSLv2Record = sslRecordVersion == 2;
|
||||
bVersionRecognized = true;
|
||||
|
||||
((SSLProxy_Analyzer*) Parent())->NewSSLRecord(this, len, data);
|
||||
}
|
||||
|
||||
bool Contents_SSL::IsSSLv2Record()
|
||||
{
|
||||
return bIsSSLv2Record;
|
||||
}
|
||||
|
||||
bool Contents_SSL::VersionRecognized()
|
||||
{
|
||||
return bVersionRecognized;
|
||||
}
|
289
src/SSLProxy.h
289
src/SSLProxy.h
|
@ -1,289 +0,0 @@
|
|||
// $Id: SSLProxy.h 5952 2008-07-13 19:45:15Z vern $
|
||||
|
||||
#ifndef SSLPROXY_H
|
||||
#define SSLPROXY_H
|
||||
|
||||
#include "TCP.h"
|
||||
#include "SSLInterpreter.h"
|
||||
#include "binpac_bro.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// --- forward declarations ---------------------------------------------------
|
||||
|
||||
class SSL_Interpreter;
|
||||
class SSL_RecordBuilder;
|
||||
class Contents_SSL;
|
||||
|
||||
// --- class SSL_DataBlock ----------------------------------------------------
|
||||
|
||||
/*!
|
||||
* \brief This class is used to store a block of data on the heap, which is
|
||||
* allocated and copied by the constructor, and freed by the destructor.
|
||||
*
|
||||
* It is mainly used by the SSL_RecordBuilder to store the received data. To
|
||||
* reduce heap operations (HeapOps), which can be quite expensive, it is
|
||||
* possible to let the constructor allocate a minimum heap block size. The
|
||||
* class members keep track of how much data has been allocated and how much of
|
||||
* it has been used. Plus, there's a pointer to the next SSL_DataBlock, for
|
||||
* easy creation of a single-linked list.
|
||||
*/
|
||||
|
||||
class SSL_DataBlock {
|
||||
public:
|
||||
SSL_DataBlock(const u_char* data, int len, int min_len = 0);
|
||||
|
||||
int len; ///< The <b>used</b> size of the reserved heap block.
|
||||
int size; ///< The <b>allocated</b> size of the reserved heap block.
|
||||
u_char* data; ///< Pointer to the allocated heap block.
|
||||
SSL_DataBlock* next; ///< Pointer to the next SSL_Datablock in the chain.
|
||||
|
||||
/*!
|
||||
* The destructor will free the allocated data block.
|
||||
*/
|
||||
~SSL_DataBlock() { delete [] data; }
|
||||
|
||||
void toStream(FILE* stream) const;
|
||||
char* toString() const;
|
||||
};
|
||||
|
||||
// --- class SSL_RecordBuilder ------------------------------------------------
|
||||
|
||||
/*!
|
||||
* \brief This class is used to reassemble SSL records from a stream of data.
|
||||
*
|
||||
* It supports both SSLv2 and SSLv3 record formats at the same time. The record
|
||||
* builder has been designed to be robust, efficient and hard to attack. To add
|
||||
* a segments of data, call addSegment(). Whenever a SSL record has been
|
||||
* reassembled, the DoDeliver() function of the corresponding Contents_SSL
|
||||
* will be called.
|
||||
*
|
||||
* Two forms of attack have been taken into consideration:
|
||||
* -# The "fake size" attack, where the actual size of the SSL record is much
|
||||
* smaller then the size given in the record header. This way, an attacker
|
||||
* could force Bro to allocate a huge amount of memory and make it crash.
|
||||
* -# The "small fragment" attack, where an attacker sends huge SSL records
|
||||
* in very small (1 byte or so) TCP segments. This could lead to a huge
|
||||
* amount of very small memory blocks allocated by Bro. After the last byte
|
||||
* of an SSL record has been received, all allocated blocks have to be
|
||||
* freed. Freeing something like 32K blocks of memory can be quite expensive,
|
||||
* so packet drops may occur, which could prevent Bro from detecting an
|
||||
* attacker.
|
||||
*
|
||||
* The current implementation always allocates a minimum size of data on the
|
||||
* heap, which is MIN_ALLOC_SIZE. The processed SSL record fragments are stored
|
||||
* in a single-linked list of type SSL_DataBlock.
|
||||
*
|
||||
* The following assumptions are made:
|
||||
* - neededSize <= min( expectedSize )
|
||||
* - neededSize <= MIN_ALLOC_SIZE, so the data needed to determine the SSL
|
||||
* record version fits in one SSL_DataBlock
|
||||
*/
|
||||
|
||||
class SSL_RecordBuilder {
|
||||
public:
|
||||
SSL_RecordBuilder(Contents_SSL* sslEndpoint);
|
||||
~SSL_RecordBuilder();
|
||||
|
||||
static const uint MIN_ALLOC_SIZE = 16; ///< min. size of memory to alloc
|
||||
static const int MIN_FRAGMENT_SIZE = 100; ///< min. size of a middle TCP Segment
|
||||
static uint maxAllocCount; ///< max. number of allocated data blocks for an instance of a reassembler
|
||||
static uint maxFragmentCount; ///< max. number of fragments for a ssl record
|
||||
static uint fragmentedHeaders; ///< counter for the number of fragmented headers (header=neededSize)
|
||||
|
||||
bool addSegment(const u_char* data, int length);
|
||||
|
||||
/*!
|
||||
* Calls this method to see if there's currently data in the
|
||||
* record builder pending.
|
||||
* \return true if there's data pending, false otherwise
|
||||
*/
|
||||
bool isDataPending() { return hasPendingData; };
|
||||
|
||||
protected:
|
||||
u_char* assembleBlocks(const u_char* data, int length);
|
||||
int analyzeSSLRecordFormat(const u_char* data, int length);
|
||||
bool computeExpectedSize (const u_char* data, int length);
|
||||
void addData(const u_char* data, int length);
|
||||
|
||||
SSL_DataBlock* head; ///< pointer to the first element in the linked list of SSL_DataBlocks
|
||||
SSL_DataBlock* tail; ///< pointer to the last element in the linked list of SSL_DataBlocks
|
||||
Contents_SSL* sslEndpoint; ///< pointer to the containing Contents_SSL
|
||||
int expectedSize; ///< expected size of SSLv2 record including header
|
||||
int currentSize; ///< current bytes stored in data blocks (that is, processed size of actual record)
|
||||
int neededSize; ///< min. size in bytes so that the length of the current record can be determinded
|
||||
bool hasPendingData; ///< true if there's data following in the current tcp segment
|
||||
uint fragmentCounter; ///< counter for the number of tcp segments for the current record
|
||||
};
|
||||
|
||||
|
||||
// --- class SSLProxy_Analyzer ----------------------------------------------
|
||||
|
||||
/** This class represents an SSL_Connection with two SSL_ConnectionEndpoints.
|
||||
* Note, that this class acts as a proxy, because there are different versions
|
||||
* of the SSL protocol in use and you don't know in advance which SSL version
|
||||
* really will be used. This depends on the first two messages of the SSL handshake
|
||||
* process. Because Bro offers no possibility for switching connections we
|
||||
* decided only to inherit this proxy from TCP_Connection.
|
||||
* The different SSL versions are implemented in classed derived from
|
||||
* SSL_Interpreter/SSL_InterpreterEndpoint and so, we can easily switch the flow
|
||||
* of data to the appropriate SSL Interpreter.
|
||||
* Currently, we support SSL Version 2.0 and 3.0/3.1(TLS)(@see SSLv2_Interpreter and @see
|
||||
* SSLv3_Interpreter).
|
||||
* This class holds an instance of both SSLv2- and SSLv3_Interpreter. The version
|
||||
* of the SSL that is used for a connection is negotiated within the first
|
||||
* two records (SSL messages): client hello and server hello.
|
||||
* So after scanning this two records (which is mainly done in @see SSL_RecordBuilder and
|
||||
* @see Contents_SSL) and determing the versions, it is clear which
|
||||
* SSL version will be used for the succeding SSL records. From now
|
||||
* on, they can be directly passed through to the appropriate SSL_Interpreter.
|
||||
*
|
||||
* FIXME: Now we have a dynamic analyzer framework so this could be restructured.
|
||||
*/
|
||||
class SSLProxy_Analyzer: public TCP_ApplicationAnalyzer {
|
||||
public:
|
||||
SSLProxy_Analyzer(Connection* conn);
|
||||
virtual ~SSLProxy_Analyzer();
|
||||
|
||||
static uint totalPackets; ///< counter for total ssl packets seen
|
||||
static uint totalRecords; ///< counter for total ssl records seen
|
||||
static uint nonSSLConnections; ///< counter for connections where we couldn't reassemble a ssl record
|
||||
|
||||
static const bool recordSSLv2Traffic = false; ///< if true, only recording of SSLv2 connections is done (no analysis)
|
||||
|
||||
static bool bInited;
|
||||
|
||||
enum SSL_Versions {
|
||||
SSLv20 = 0x0002,
|
||||
SSLv30 = 0x0300,
|
||||
SSLv31 = 0x0301 // = TLS 1.0
|
||||
};
|
||||
|
||||
/* This method is called from the corresponding Contents_SSL to
|
||||
* deliver the data to the SSL_ProxyConnection. It decides which
|
||||
* SSL_Interpreter (Version 2 or Version 3x) gets the record or
|
||||
* directly passes it through, if it's already clear which version
|
||||
* this SSL connection uses.
|
||||
* @param endp the sending endpoint
|
||||
* @param len length of SSL record
|
||||
* @param data the SSL record
|
||||
*
|
||||
* SC mod - pass a TCP_Contents rather than endpoint in terms of an actual
|
||||
* Contents_SSL. There is much less overall work to do since we
|
||||
* have already done the assosciation.
|
||||
*/
|
||||
void NewSSLRecord(Contents_SSL* endp, int len, const u_char* data);
|
||||
|
||||
// Initialises the SSLv2- and SSLv3_Interpreters.
|
||||
virtual void Init();
|
||||
|
||||
// This method is used for passing messages to Bro that contain
|
||||
// information about weaknesses in the choosen SSL encryption
|
||||
// (short keys, unverifyable certificates, ...)
|
||||
// @param name the name of the weakness.
|
||||
void Weak(const char* name);
|
||||
|
||||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new SSLProxy_Analyzer(conn); }
|
||||
|
||||
static bool Available()
|
||||
{
|
||||
return (ssl_certificate_seen || ssl_certificate ||
|
||||
ssl_conn_attempt || ssl_conn_server_reply ||
|
||||
ssl_conn_established || ssl_conn_reused ||
|
||||
ssl_conn_alert)
|
||||
&& ! FLAGS_use_binpac;
|
||||
}
|
||||
|
||||
static void printStats();
|
||||
|
||||
protected:
|
||||
bool bPassThrough; ///< whether it is clear which SSL version the connection will use
|
||||
|
||||
SSL_Interpreter* sSLv2Interpreter; ///< Interpreter for SSL version 2
|
||||
SSL_Interpreter* sSLv3xInterpreter; ///< Interpreter for SSL version 3.0 and 3.1
|
||||
SSL_Interpreter* sSLInterpreter; ///< Pointer to the interpreter currently in use
|
||||
|
||||
Contents_SSL* sslpeo;
|
||||
Contents_SSL* sslper;
|
||||
|
||||
/** Internally called from this class Deliver()-method.
|
||||
* It delivers the data to the correct corresponding
|
||||
* SSL_InterpreterEndpoint.
|
||||
* @param endp the sending endpoint
|
||||
* @param t time, when the segment was received by bro (not used)
|
||||
* @param seq relative sequenze number (from Endpoint::start_seq) (not used)
|
||||
* @param len length of SSL record
|
||||
* @param data the SSL record
|
||||
*/
|
||||
void DoDeliver(int len, const u_char* data, bool orig);
|
||||
|
||||
// Initialises the dictionary where the SSL cipher specs are stored.
|
||||
// It needs only to be called once for a whole bro. @see SSLDefines.h
|
||||
void BuildCipherDict();
|
||||
};
|
||||
|
||||
// --- class Contents_SSL ------------------------------------------------
|
||||
|
||||
/** This class represents an endpoint of a SSLProxy_Analyzer.
|
||||
* It receives the new data (TCP segments) within the Deliver()-method, does
|
||||
* some basic checks on the segment and passes it on to the SSL_RecordBuilder,
|
||||
* which reassembles the segments into SSL records and determines the
|
||||
* versions of the records. If the SSL_RecordBuilder was able to determine
|
||||
* the versions of the records it delivers the reassembled records back tho this
|
||||
* Contents_SSL by calling the DoDeliver()-method.
|
||||
* The Contents_SSL then hands the record over to the corresponding
|
||||
* SSLProxy_Analyzer by invoking it's NewSSLRecord()-method.
|
||||
*
|
||||
* SC mod: change class Contents_SSL: public TCP_EndpointContents
|
||||
* to class Contents_SSL: public TCP_Contents
|
||||
* this is done since the class uses the Deliver() method to take care of data.
|
||||
*
|
||||
*/
|
||||
class Contents_SSL: public TCP_SupportAnalyzer {
|
||||
public:
|
||||
/* The constructor builds up and initialises the Contents_SSL.
|
||||
* @param conn the corresponding Connection
|
||||
* @param whether this is the originator
|
||||
*/
|
||||
Contents_SSL(Connection* conn, bool orig);
|
||||
~Contents_SSL();
|
||||
|
||||
int sslRecordVersion; ///< record version of the first SSL record seen (set by SSLProxy_Analyzer and SSL_RecordBuilder)
|
||||
uint16 sslVersion; ///< SSL version of the SSL record seen (set by SSL_RecordBuilder)
|
||||
|
||||
/** Via this method, this Contents_SSL receives the
|
||||
* TCP segments.
|
||||
* @param len length of TCP-Segment
|
||||
* @param data content of TCP-Segment
|
||||
* @param orig whether sending endpoint is originator
|
||||
*/
|
||||
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||
|
||||
/** This method is called by the corresponding SSL_RecordBuilder
|
||||
* upon delivering a new reassembled SSL record.
|
||||
* @param len the length of the record
|
||||
* @param data the record
|
||||
*/
|
||||
void DoDeliver(int len, const u_char* data);
|
||||
|
||||
/* @return whether we have already seen the first record of the connection of this endpoint yet
|
||||
*/
|
||||
bool VersionRecognized();
|
||||
|
||||
/* @return whether the first record was of SSL version 2.0
|
||||
*/
|
||||
bool IsSSLv2Record();
|
||||
|
||||
/* @return whether the corresponding SSL_RecordBuilder has pending data
|
||||
*/
|
||||
bool isDataPending(); // should be inline
|
||||
|
||||
SSL_RecordBuilder* sslRecordBuilder;
|
||||
|
||||
protected:
|
||||
bool bVersionRecognized; ///< False, if we haven't seen the first record of the connection of this endpoint yet
|
||||
bool bIsSSLv2Record; ///< Was the first record of SSL version 2.0
|
||||
};
|
||||
|
||||
#endif
|
946
src/SSLv2.cc
946
src/SSLv2.cc
|
@ -1,946 +0,0 @@
|
|||
// $Id: SSLv2.cc 5988 2008-07-19 07:02:12Z vern $
|
||||
|
||||
#include "SSLv2.h"
|
||||
#include "SSLv3.h"
|
||||
|
||||
// --- Initalization of static variables --------------------------------------
|
||||
|
||||
uint SSLv2_Interpreter::totalConnections = 0;
|
||||
uint SSLv2_Interpreter::analyzedConnections = 0;
|
||||
uint SSLv2_Interpreter::openedConnections = 0;
|
||||
uint SSLv2_Interpreter::failedConnections = 0;
|
||||
uint SSLv2_Interpreter::weirdConnections = 0;
|
||||
uint SSLv2_Interpreter::totalRecords = 0;
|
||||
uint SSLv2_Interpreter::clientHelloRecords = 0;
|
||||
uint SSLv2_Interpreter::serverHelloRecords = 0;
|
||||
uint SSLv2_Interpreter::clientMasterKeyRecords = 0;
|
||||
uint SSLv2_Interpreter::errorRecords = 0;
|
||||
|
||||
|
||||
// --- SSLv2_Interpreter -------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* The Constructor.
|
||||
*
|
||||
* \param proxy Pointer to the SSLProxy_Analyzer who created this instance.
|
||||
*/
|
||||
SSLv2_Interpreter::SSLv2_Interpreter(SSLProxy_Analyzer* proxy)
|
||||
: SSL_Interpreter(proxy)
|
||||
{
|
||||
++totalConnections;
|
||||
records = 0;
|
||||
bAnalyzedCounted = false;
|
||||
connState = START;
|
||||
|
||||
pServerCipherSpecs = 0;
|
||||
pClientCipherSpecs = 0;
|
||||
bClientWantsCachedSession = false;
|
||||
usedCipherSpec = (SSLv2_CipherSpec) 0;
|
||||
|
||||
pConnectionId = 0;
|
||||
pChallenge = 0;
|
||||
pSessionId = 0;
|
||||
pMasterClearKey = 0;
|
||||
pMasterEncryptedKey = 0;
|
||||
pClientReadKey = 0;
|
||||
pServerReadKey = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The Destructor.
|
||||
*/
|
||||
SSLv2_Interpreter::~SSLv2_Interpreter()
|
||||
{
|
||||
if ( connState != CLIENT_MASTERKEY_SEEN &&
|
||||
connState != CACHED_SESSION &&
|
||||
connState != START && // we only complain if we saw some data
|
||||
connState != ERROR_SEEN )
|
||||
++failedConnections;
|
||||
|
||||
if ( connState != CLIENT_MASTERKEY_SEEN && connState != CACHED_SESSION )
|
||||
++weirdConnections;
|
||||
|
||||
delete pServerCipherSpecs;
|
||||
delete pClientCipherSpecs;
|
||||
delete pConnectionId;
|
||||
delete pChallenge;
|
||||
delete pSessionId;
|
||||
delete pMasterClearKey;
|
||||
delete pMasterEncryptedKey;
|
||||
delete pClientReadKey;
|
||||
delete pServerReadKey;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method implements SSL_Interpreter::BuildInterpreterEndpoints()
|
||||
*/
|
||||
void SSLv2_Interpreter::BuildInterpreterEndpoints()
|
||||
{
|
||||
orig = new SSLv2_Endpoint(this, 1);
|
||||
resp = new SSLv2_Endpoint(this, 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method prints some counters.
|
||||
*/
|
||||
void SSLv2_Interpreter::printStats()
|
||||
{
|
||||
printf("SSLv2:\n");
|
||||
printf("totalConnections = %u\n", totalConnections);
|
||||
printf("analyzedConnections = %u\n", analyzedConnections);
|
||||
printf("openedConnections = %u\n", openedConnections);
|
||||
printf("failedConnections = %u\n", failedConnections);
|
||||
printf("weirdConnections = %u\n", weirdConnections);
|
||||
|
||||
printf("totalRecords = %u\n", totalRecords);
|
||||
printf("clientHelloRecords = %u\n", clientHelloRecords);
|
||||
printf("serverHelloRecords = %u\n", serverHelloRecords);
|
||||
printf("clientMasterKeyRecords = %u\n", clientMasterKeyRecords);
|
||||
printf("errorRecords = %u\n", errorRecords);
|
||||
|
||||
printf("SSL_RecordBuilder::maxAllocCount = %u\n", SSL_RecordBuilder::maxAllocCount);
|
||||
printf("SSL_RecordBuilder::maxFragmentCount = %u\n", SSL_RecordBuilder::maxFragmentCount);
|
||||
printf("SSL_RecordBuilder::fragmentedHeaders = %u\n", SSL_RecordBuilder::fragmentedHeaders);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \return the current state of the ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ConnState()
|
||||
{
|
||||
return connState;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called by SSLv2_Endpoint::Deliver(). It is the main entry
|
||||
* point of this class. The header of the given SSLV2 record is analyzed and
|
||||
* its contents are then passed to the corresponding analyzer method. After
|
||||
* the record has been analyzed, the ssl connection state is updated.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 record
|
||||
* \param data pointer to SSLv2 record to analyze
|
||||
*/
|
||||
void SSLv2_Interpreter::NewSSLRecord(SSL_InterpreterEndpoint* s,
|
||||
int length, const u_char* data)
|
||||
{
|
||||
++records;
|
||||
++totalRecords;
|
||||
|
||||
if ( ! bAnalyzedCounted )
|
||||
{
|
||||
++analyzedConnections;
|
||||
bAnalyzedCounted = true;
|
||||
}
|
||||
|
||||
// We should see a maximum of 4 cleartext records.
|
||||
if ( records == 5 )
|
||||
{ // so this should never happen
|
||||
Weird("SSLv2: Saw more than 4 records, skipping connection...");
|
||||
proxy->SetSkip(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// SSLv2 record header analysis
|
||||
uint32 recordLength = 0; // data length of SSLv2 record
|
||||
bool isEscape = false;
|
||||
uint8 padding = 0;
|
||||
const u_char* contents;
|
||||
|
||||
if ( (data[0] & 0x80) > 0 )
|
||||
{ // we have a two-byte record header
|
||||
recordLength = ((data[0] & 0x7f) << 8) | data[1];
|
||||
contents = data + 2;
|
||||
if ( recordLength + 2 != uint32(length) )
|
||||
{
|
||||
// This should never happen, otherwise
|
||||
// we have a bug in the SSL_RecordBuilder.
|
||||
Weird("SSLv2: FATAL: recordLength doesn't match data block length!");
|
||||
connState = ERROR_REQUIRED;
|
||||
proxy->SetSkip(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // We have a three-byte record header.
|
||||
recordLength = ((data[0] & 0x3f) << 8) | data[1];
|
||||
isEscape = (data[0] & 0x40) != 0;
|
||||
padding = data[2];
|
||||
contents = data + 3;
|
||||
if ( recordLength + 3 != uint32(length) )
|
||||
{
|
||||
// This should never happen, otherwise
|
||||
// we have a bug in the SSL_RecordBuilder.
|
||||
Weird("SSLv2: FATAL: recordLength doesn't match data block length!");
|
||||
connState = ERROR_REQUIRED;
|
||||
proxy->SetSkip(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( padding == 0 && ! isEscape )
|
||||
Weird("SSLv2: 3 Byte record header, but no escape, no padding!");
|
||||
}
|
||||
|
||||
if ( recordLength == 0 )
|
||||
{
|
||||
Weird("SSLv2: Record length is zero (no record data)!");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isEscape )
|
||||
Weird("SSLv2: Record has escape bit set (security escape)!");
|
||||
|
||||
if ( padding > 0 && connState != CACHED_SESSION &&
|
||||
connState != CLIENT_MASTERKEY_SEEN )
|
||||
Weird("SSLv2 record with padding > 0 in cleartext!");
|
||||
|
||||
// MISSING:
|
||||
// A final consistency check is done when a block cipher is used
|
||||
// and the protocol is using encryption. The amount of data present
|
||||
// in a record (RECORD-LENGTH))must be a multiple of the cipher's
|
||||
// block size. If the received record is not a multiple of the
|
||||
// cipher's block size then the record is considered damaged, and it
|
||||
// is to be treated as if an "I/O Error" had occurred (i.e. an
|
||||
// unrecoverable error is asserted and the connection is closed).
|
||||
|
||||
switch ( connState ) {
|
||||
case START:
|
||||
// Only CLIENT-HELLLOs allowed here.
|
||||
if ( contents[0] != SSLv2_MT_CLIENT_HELLO )
|
||||
{
|
||||
Weird("SSLv2: First packet is not a CLIENT-HELLO!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
else
|
||||
connState = ClientHelloRecord(s, recordLength, contents);
|
||||
break;
|
||||
|
||||
case CLIENT_HELLO_SEEN:
|
||||
// Only SERVER-HELLOs or ERRORs allowed here.
|
||||
if ( contents[0] == SSLv2_MT_SERVER_HELLO )
|
||||
connState = ServerHelloRecord(s, recordLength, contents);
|
||||
else if ( contents[0] == SSLv2_MT_ERROR )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
Weird("SSLv2: State violation in CLIENT_HELLO_SEEN!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEW_SESSION:
|
||||
// We expect a client master key.
|
||||
if ( contents[0] == SSLv2_MT_CLIENT_MASTER_KEY )
|
||||
connState = ClientMasterKeyRecord(s, recordLength, contents);
|
||||
else if ( contents[0] == SSLv2_MT_ERROR )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
Weird("SSLv2: State violation in NEW_SESSION or encrypted record!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
delete pServerCipherSpecs;
|
||||
pServerCipherSpecs = 0;
|
||||
break;
|
||||
|
||||
case CACHED_SESSION:
|
||||
delete pServerCipherSpecs;
|
||||
pServerCipherSpecs = 0;
|
||||
// No break here.
|
||||
|
||||
case CLIENT_MASTERKEY_SEEN:
|
||||
// If no error record, no further analysis.
|
||||
if ( contents[0] == SSLv2_MT_ERROR &&
|
||||
recordLength == SSLv2_ERROR_RECORD_SIZE )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
// So we finished the cleartext handshake.
|
||||
// Skip all further data.
|
||||
|
||||
proxy->SetSkip(1);
|
||||
++openedConnections;
|
||||
}
|
||||
break;
|
||||
|
||||
case ERROR_REQUIRED:
|
||||
if ( contents[0] == SSLv2_MT_ERROR )
|
||||
connState = ErrorRecord(s, recordLength, contents);
|
||||
else
|
||||
{
|
||||
// We lost tracking: this should not happen.
|
||||
Weird("SSLv2: State inconsistency in ERROR_REQUIRED (lost tracking!)!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
connState = ERROR_REQUIRED;
|
||||
}
|
||||
break;
|
||||
|
||||
case ERROR_SEEN:
|
||||
// We don't have recoverable errors in cleartext phase,
|
||||
// so we shouldn't see anymore packets.
|
||||
Weird("SSLv2: Traffic after error record!");
|
||||
analyzeRecord(s, recordLength, contents);
|
||||
break;
|
||||
|
||||
default:
|
||||
internal_error("SSLv2: unknown state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called whenever the connection tracking failed. It calls
|
||||
* the corresponding analyzer method for the given SSLv2 record, but does not
|
||||
* update the ssl connection state.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 record
|
||||
* \param data pointer to SSLv2 record to analyze
|
||||
*/
|
||||
void SSLv2_Interpreter::analyzeRecord(SSL_InterpreterEndpoint* s,
|
||||
int length, const u_char* data)
|
||||
{
|
||||
switch ( data[0] ) {
|
||||
case SSLv2_MT_ERROR:
|
||||
ErrorRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_CLIENT_HELLO:
|
||||
ClientHelloRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_CLIENT_MASTER_KEY:
|
||||
ClientMasterKeyRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_SERVER_HELLO:
|
||||
ServerHelloRecord(s, length, data);
|
||||
break;
|
||||
|
||||
case SSLv2_MT_CLIENT_FINISHED:
|
||||
case SSLv2_MT_SERVER_VERIFY:
|
||||
case SSLv2_MT_SERVER_FINISHED:
|
||||
case SSLv2_MT_REQUEST_CERTIFICATE:
|
||||
case SSLv2_MT_CLIENT_CERTIFICATE:
|
||||
Weird("SSLv2: Encrypted record type seems to be in cleartext");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown record type.
|
||||
Weird("SSLv2: Unknown record type or encrypted record");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 CLIENT-HELLO record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 CLIENT-HELLO record
|
||||
* \param data pointer to SSLv2 CLIENT-HELLO record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ClientHelloRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength, const u_char* recordData)
|
||||
{
|
||||
// This method gets the record's data (without the header).
|
||||
++clientHelloRecords;
|
||||
|
||||
if ( s != orig )
|
||||
Weird("SSLv2: CLIENT-HELLO record from server!");
|
||||
|
||||
// There should not be any pending data in the SSLv2 reassembler,
|
||||
// because the client should wait for a server response.
|
||||
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
|
||||
Weird("SSLv2: Pending data in SSL_RecordBuilder after CLIENT-HELLO!");
|
||||
|
||||
// Client hello minimum header size check.
|
||||
if ( recordLength < SSLv2_CLIENT_HELLO_HEADER_SIZE )
|
||||
{
|
||||
Weird("SSLv2: CLIENT-HELLO is too small!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Extract the data of the client hello header.
|
||||
SSLv2_ClientHelloHeader ch;
|
||||
ch.clientVersion = uint16(recordData[1] << 8) | recordData[2];
|
||||
ch.cipherSpecLength = uint16(recordData[3] << 8) | recordData[4];
|
||||
ch.sessionIdLength = uint16(recordData[5] << 8) | recordData[6];
|
||||
ch.challengeLength = uint16(recordData[7] << 8) | recordData[8];
|
||||
|
||||
if ( ch.clientVersion != SSLProxy_Analyzer::SSLv20 &&
|
||||
ch.clientVersion != SSLProxy_Analyzer::SSLv30 &&
|
||||
ch.clientVersion != SSLProxy_Analyzer::SSLv31 )
|
||||
{
|
||||
Weird("SSLv2: Unsupported SSL-Version in CLIENT-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
if ( ch.challengeLength + ch.cipherSpecLength + ch.sessionIdLength +
|
||||
SSLv2_CLIENT_HELLO_HEADER_SIZE != recordLength )
|
||||
{
|
||||
Weird("SSLv2: Size inconsistency in CLIENT-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// The CIPHER-SPECS-LENGTH must be > 0 and a multiple of 3.
|
||||
if ( ch.cipherSpecLength == 0 || ch.cipherSpecLength % 3 != 0 )
|
||||
{
|
||||
Weird("SSLv2: Nonconform CIPHER-SPECS-LENGTH in CLIENT-HELLO.");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// The SESSION-ID-LENGTH must either be zero or 16.
|
||||
if ( ch.sessionIdLength != 0 && ch.sessionIdLength != 16 )
|
||||
Weird("SSLv2: Nonconform SESSION-ID-LENGTH in CLIENT-HELLO.");
|
||||
|
||||
if ( (ch.challengeLength < 16) || (ch.challengeLength > 32))
|
||||
Weird("SSLv2: Nonconform CHALLENGE-LENGTH in CLIENT-HELLO.");
|
||||
|
||||
const u_char* ptr = recordData;
|
||||
ptr += SSLv2_CLIENT_HELLO_HEADER_SIZE + ch.cipherSpecLength;
|
||||
|
||||
pSessionId = new SSL_DataBlock(ptr, ch.sessionIdLength);
|
||||
|
||||
// If decrypting, store the challenge.
|
||||
if ( ssl_store_key_material && ch.challengeLength <= 32 )
|
||||
pChallenge = new SSL_DataBlock(ptr, ch.challengeLength);
|
||||
|
||||
bClientWantsCachedSession = ch.sessionIdLength != 0;
|
||||
|
||||
TableVal* currentCipherSuites =
|
||||
analyzeCiphers(s, ch.cipherSpecLength,
|
||||
recordData + SSLv2_CLIENT_HELLO_HEADER_SIZE);
|
||||
|
||||
fire_ssl_conn_attempt(ch.clientVersion, currentCipherSuites);
|
||||
|
||||
return CLIENT_HELLO_SEEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 SERVER-HELLO record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 SERVER-HELLO record
|
||||
* \param data pointer to SSLv2 SERVER-HELLO record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ServerHelloRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength, const u_char* recordData)
|
||||
{
|
||||
++serverHelloRecords;
|
||||
TableVal* currentCipherSuites = NULL;
|
||||
|
||||
if ( s != resp )
|
||||
Weird("SSLv2: SERVER-HELLO from client!");
|
||||
|
||||
if ( recordLength < SSLv2_SERVER_HELLO_HEADER_SIZE )
|
||||
{
|
||||
Weird("SSLv2: SERVER-HELLO is too small!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Extract the data of the client hello header.
|
||||
SSLv2_ServerHelloHeader sh;
|
||||
sh.sessionIdHit = recordData[1];
|
||||
sh.certificateType = recordData[2];
|
||||
sh.serverVersion = uint16(recordData[3] << 8) | recordData[4];
|
||||
sh.certificateLength = uint16(recordData[5] << 8) | recordData[6];
|
||||
sh.cipherSpecLength = uint16(recordData[7] << 8) | recordData[8];
|
||||
sh.connectionIdLength = uint16(recordData[9] << 8) | recordData[10];
|
||||
|
||||
if ( sh.serverVersion != SSLProxy_Analyzer::SSLv20 )
|
||||
{
|
||||
Weird("SSLv2: Unsupported SSL-Version in SERVER-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
if ( sh.certificateLength + sh.cipherSpecLength +
|
||||
sh.connectionIdLength +
|
||||
SSLv2_SERVER_HELLO_HEADER_SIZE != recordLength )
|
||||
{
|
||||
Weird("SSLv2: Size inconsistency in SERVER-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// The length of the CONNECTION-ID must be between 16 and 32 bytes.
|
||||
if ( sh.connectionIdLength < 16 || sh.connectionIdLength > 32 )
|
||||
Weird("SSLv2: Nonconform CONNECTION-ID-LENGTH in SERVER-HELLO");
|
||||
|
||||
// If decrypting, store the connection ID.
|
||||
if ( ssl_store_key_material && sh.connectionIdLength <= 32 )
|
||||
{
|
||||
const u_char* ptr = recordData;
|
||||
|
||||
ptr += SSLv2_SERVER_HELLO_HEADER_SIZE + sh.cipherSpecLength +
|
||||
sh.certificateLength;
|
||||
|
||||
pConnectionId = new SSL_DataBlock(ptr, sh.connectionIdLength);
|
||||
}
|
||||
|
||||
if ( sh.sessionIdHit == 0 )
|
||||
{
|
||||
// Generating reusing-connection event.
|
||||
EventHandlerPtr event = ssl_session_insertion;
|
||||
|
||||
if ( event )
|
||||
{
|
||||
TableVal* sessionIDTable =
|
||||
MakeSessionID(
|
||||
recordData +
|
||||
SSLv2_SERVER_HELLO_HEADER_SIZE +
|
||||
sh.certificateLength +
|
||||
sh.cipherSpecLength,
|
||||
sh.connectionIdLength);
|
||||
|
||||
val_list* vl = new val_list;
|
||||
vl->append(proxy->BuildConnVal());
|
||||
vl->append(sessionIDTable);
|
||||
|
||||
proxy->ConnectionEvent(ssl_session_insertion, vl);
|
||||
}
|
||||
}
|
||||
|
||||
SSLv2_States nextState;
|
||||
|
||||
if ( sh.sessionIdHit != 0 )
|
||||
{ // we're using a cached session
|
||||
|
||||
// There should not be any pending data in the SSLv2
|
||||
// reassembler, because the server should wait for a
|
||||
// client response.
|
||||
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
|
||||
{
|
||||
// But turns out some SSL Implementations do this
|
||||
// when using a cached session.
|
||||
}
|
||||
|
||||
// Consistency check for SESSION-ID-HIT.
|
||||
if ( ! bClientWantsCachedSession )
|
||||
Weird("SSLv2: SESSION-ID hit in SERVER-HELLO, but no SESSION-ID in CLIENT-HELLO!");
|
||||
|
||||
// If the SESSION-ID-HIT flag is non-zero then the
|
||||
// CERTIFICATE-TYPE, CERTIFICATE-LENGTH and
|
||||
// CIPHER-SPECS-LENGTH fields will be zero.
|
||||
if ( sh.certificateType != 0 || sh.certificateLength != 0 ||
|
||||
sh.cipherSpecLength != 0 )
|
||||
Weird("SSLv2: SESSION-ID-HIT, but session data in SERVER-HELLO");
|
||||
|
||||
// Generate reusing-connection event.
|
||||
if ( pSessionId )
|
||||
{
|
||||
fire_ssl_conn_reused(pSessionId);
|
||||
delete pSessionId;
|
||||
pSessionId = 0;
|
||||
}
|
||||
|
||||
nextState = CACHED_SESSION;
|
||||
}
|
||||
else
|
||||
{ // we're starting a new session
|
||||
|
||||
// There should not be any pending data in the SSLv2
|
||||
// reassembler, because the server should wait for
|
||||
// a client response.
|
||||
if ( ((SSLv2_Endpoint*) s)->isDataPending() )
|
||||
Weird("SSLv2: Pending data in SSL_RecordBuilder after SERVER-HELLO (new session)!");
|
||||
|
||||
// TODO: check certificate length ???
|
||||
if ( sh.certificateLength == 0 )
|
||||
Weird("SSLv2: No certificate in SERVER-HELLO!");
|
||||
|
||||
// The CIPHER-SPECS-LENGTH must be > zero and a multiple of 3.
|
||||
if ( sh.cipherSpecLength == 0 )
|
||||
Weird("SSLv2: No CIPHER-SPECS in SERVER-HELLO!");
|
||||
|
||||
if ( sh.cipherSpecLength % 3 != 0 )
|
||||
{
|
||||
Weird("SSLv2: Nonconform CIPHER-SPECS-LENGTH in SERVER-HELLO");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
const u_char* ptr = recordData;
|
||||
ptr += sh.certificateLength + SSLv2_SERVER_HELLO_HEADER_SIZE;
|
||||
currentCipherSuites = analyzeCiphers(s, sh.cipherSpecLength, ptr);
|
||||
|
||||
nextState = NEW_SESSION;
|
||||
}
|
||||
|
||||
// Check if at least one cipher is supported by the client.
|
||||
if ( pClientCipherSpecs && pServerCipherSpecs )
|
||||
{
|
||||
bool bFound = false;
|
||||
for ( int i = 0; i < pClientCipherSpecs->len; i += 3 )
|
||||
{
|
||||
for ( int j = 0; j < pServerCipherSpecs->len; j += 3 )
|
||||
{
|
||||
if ( memcmp(pClientCipherSpecs + i,
|
||||
pServerCipherSpecs + j, 3) == 0 )
|
||||
{
|
||||
bFound = true;
|
||||
i = pClientCipherSpecs->len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! bFound )
|
||||
{
|
||||
Weird("SSLv2: Client's and server's CIPHER-SPECS don't match!");
|
||||
nextState = ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
delete pClientCipherSpecs;
|
||||
pClientCipherSpecs = 0;
|
||||
}
|
||||
|
||||
// Certificate analysis.
|
||||
if ( sh.certificateLength > 0 && ssl_analyze_certificates != 0 )
|
||||
{
|
||||
analyzeCertificate(s, recordData + SSLv2_SERVER_HELLO_HEADER_SIZE,
|
||||
sh.certificateLength, sh.certificateType, false);
|
||||
}
|
||||
|
||||
if ( nextState == NEW_SESSION )
|
||||
// generate server-reply event
|
||||
fire_ssl_conn_server_reply(sh.serverVersion, currentCipherSuites);
|
||||
|
||||
else if ( nextState == CACHED_SESSION )
|
||||
{ // generate server-reply event
|
||||
fire_ssl_conn_server_reply(sh.serverVersion, currentCipherSuites);
|
||||
// Generate a connection-established event with a dummy
|
||||
// cipher suite, since we can't remember session information
|
||||
// (yet).
|
||||
// Note: A new session identifier is sent encrypted in SSLv2!
|
||||
fire_ssl_conn_established(sh.serverVersion, 0xABCD);
|
||||
}
|
||||
else
|
||||
// Unref, since the table is not delivered to any event.
|
||||
Unref(currentCipherSuites);
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 CLIENT-MASTER-KEY record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 CLIENT-MASTER-KEY record
|
||||
* \param data pointer to SSLv2 CLIENT-MASTER-KEY record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::
|
||||
ClientMasterKeyRecord(SSL_InterpreterEndpoint* s, int recordLength,
|
||||
const u_char* recordData)
|
||||
{
|
||||
++clientMasterKeyRecords;
|
||||
SSLv2_States nextState = CLIENT_MASTERKEY_SEEN;
|
||||
|
||||
if ( s != orig )
|
||||
Weird("SSLv2: CLIENT-MASTER-KEY from server!");
|
||||
|
||||
if ( recordLength < SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE )
|
||||
{
|
||||
Weird("SSLv2: CLIENT-MASTER-KEY is too small!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Extract the data of the client master key header.
|
||||
SSLv2_ClientMasterKeyHeader cmk;
|
||||
cmk.cipherKind =
|
||||
((recordData[1] << 16) | recordData[2] << 8) | recordData[3];
|
||||
cmk.clearKeyLength = uint16(recordData[4] << 8) | recordData[5];
|
||||
cmk.encryptedKeyLength = uint16(recordData[6] << 8) | recordData[7];
|
||||
cmk.keyArgLength = uint16(recordData[8] << 8) | recordData[9];
|
||||
|
||||
if ( cmk.clearKeyLength + cmk.encryptedKeyLength + cmk.keyArgLength +
|
||||
SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE != recordLength )
|
||||
{
|
||||
Weird("SSLv2: Size inconsistency in CLIENT-MASTER-KEY");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// Check if cipher is supported by the server.
|
||||
if ( pServerCipherSpecs )
|
||||
{
|
||||
bool bFound = false;
|
||||
for ( int i = 0; i < pServerCipherSpecs->len; i += 3 )
|
||||
{
|
||||
uint32 cipherSpec =
|
||||
((pServerCipherSpecs->data[i] << 16) |
|
||||
pServerCipherSpecs->data[i+1] << 8) |
|
||||
pServerCipherSpecs->data[i+2];
|
||||
|
||||
if ( cmk.cipherKind == cipherSpec )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! bFound )
|
||||
{
|
||||
Weird("SSLv2: Client chooses unadvertised cipher in CLIENT-MASTER-KEY!");
|
||||
nextState = ERROR_REQUIRED;
|
||||
}
|
||||
else
|
||||
nextState = CLIENT_MASTERKEY_SEEN;
|
||||
|
||||
delete pServerCipherSpecs;
|
||||
pServerCipherSpecs = 0;
|
||||
}
|
||||
|
||||
// TODO: check if cipher has been advertised before.
|
||||
|
||||
SSL_CipherSpec* pCipherSpecTemp = 0;
|
||||
|
||||
HashKey h(static_cast<bro_uint_t>(cmk.cipherKind));
|
||||
pCipherSpecTemp = (SSL_CipherSpec*) SSL_CipherSpecDict.Lookup(&h);
|
||||
if ( ! pCipherSpecTemp || ! (pCipherSpecTemp->flags & SSL_FLAG_SSLv20) )
|
||||
Weird("SSLv2: Unknown CIPHER-SPEC in CLIENT-MASTER-KEY!");
|
||||
else
|
||||
{ // check for conistency of clearKeyLength
|
||||
if ( cmk.clearKeyLength * 8 != pCipherSpecTemp->clearKeySize )
|
||||
{
|
||||
Weird("SSLv2: Inconsistency of clearKeyLength in CLIENT-MASTER-KEY!");
|
||||
// nextState = ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
// TODO: check for consistency of encryptedKeyLength.
|
||||
// TODO: check for consistency of keyArgLength.
|
||||
// switch ( cmk.cipherKind )
|
||||
// {
|
||||
// case SSL_CK_RC4_128_WITH_MD5:
|
||||
// case SSL_CK_RC4_128_EXPORT40_WITH_MD5:
|
||||
// if ( cmk.keyArgLength != 0 )
|
||||
// {
|
||||
// Weird("SSLv2: Inconsistency of keyArgLength in CLIENT-MASTER-KEY!");
|
||||
// //nextState = ERROR_REQUIRED;
|
||||
// }
|
||||
// break;
|
||||
// case SSL_CK_DES_64_CBC_WITH_MD5:
|
||||
// case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5:
|
||||
// case SSL_CK_RC2_128_CBC_WITH_MD5:
|
||||
// case SSL_CK_IDEA_128_CBC_WITH_MD5:
|
||||
// case SSL_CK_DES_192_EDE3_CBC_WITH_MD5:
|
||||
// if ( cmk.keyArgLength != 8 )
|
||||
// {
|
||||
// Weird("SSLv2: Inconsistency of keyArgLength in CLIENT-MASTER-KEY!");
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
// Remember the used cipher spec.
|
||||
usedCipherSpec = SSLv2_CipherSpec(cmk.cipherKind);
|
||||
|
||||
// If decrypting, store the clear key part of the master key.
|
||||
if ( ssl_store_key_material /* && cmk.clearKeyLength == 11 */ )
|
||||
{
|
||||
pMasterClearKey =
|
||||
new SSL_DataBlock((recordData + SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE), cmk.clearKeyLength);
|
||||
|
||||
pMasterEncryptedKey =
|
||||
new SSL_DataBlock((recordData + SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE + cmk.clearKeyLength ), cmk.encryptedKeyLength);
|
||||
}
|
||||
|
||||
if ( nextState == CLIENT_MASTERKEY_SEEN )
|
||||
fire_ssl_conn_established(SSLProxy_Analyzer::SSLv20,
|
||||
cmk.cipherKind);
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* This method analyses a SSLv2 ERROR record.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the record
|
||||
* \param length length of SSLv2 ERROR record
|
||||
* \param data pointer to SSLv2 ERROR record to analyze
|
||||
*
|
||||
* \return the updated state of the current ssl connection
|
||||
*/
|
||||
SSLv2_States SSLv2_Interpreter::ErrorRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength, const u_char* recordData)
|
||||
{
|
||||
++errorRecords;
|
||||
|
||||
if ( unsigned(recordLength) != SSLv2_ERROR_RECORD_SIZE )
|
||||
{
|
||||
Weird("SSLv2: Size mismatch in Error Record!");
|
||||
return ERROR_REQUIRED;
|
||||
}
|
||||
|
||||
SSLv2_ErrorRecord er;
|
||||
er.errorCode = (recordData[1] << 8) | recordData[2];
|
||||
SSL3x_AlertLevel al = SSL3x_AlertLevel(255);
|
||||
|
||||
switch ( er.errorCode ) {
|
||||
case SSLv2_PE_NO_CIPHER:
|
||||
// The client doesn't support a cipher which the server
|
||||
// supports. Only from client to server and not recoverable!
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
break;
|
||||
|
||||
case SSLv2_PE_NO_CERTIFICATE:
|
||||
if ( s == orig )
|
||||
// from client to server: not recoverable
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
else
|
||||
// from server to client: recoverable
|
||||
al = SSL3x_ALERT_LEVEL_WARNING;
|
||||
break;
|
||||
|
||||
case SSLv2_PE_BAD_CERTIFICATE:
|
||||
if ( s == orig )
|
||||
// from client to server: not recoverable
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
else
|
||||
// from server to client: recoverable
|
||||
al = SSL3x_ALERT_LEVEL_WARNING;
|
||||
break;
|
||||
|
||||
case SSLv2_PE_UNSUPPORTED_CERTIFICATE_TYPE:
|
||||
if ( s == orig )
|
||||
// from client to server: not recoverable
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
else
|
||||
// from server to client: recoverable
|
||||
al = SSL3x_ALERT_LEVEL_WARNING;
|
||||
break;
|
||||
|
||||
default:
|
||||
al = SSL3x_ALERT_LEVEL_FATAL;
|
||||
break;
|
||||
}
|
||||
|
||||
fire_ssl_conn_alert(SSLProxy_Analyzer::SSLv20, al, er.errorCode);
|
||||
|
||||
return ERROR_SEEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method analyses a set of SSLv2 cipher suites.
|
||||
*
|
||||
* \param s Pointer to the endpoint which sent the cipher suites
|
||||
* \param length length of cipher suites
|
||||
* \param data pointer to cipher suites to analyze
|
||||
*
|
||||
* \return a pointer to a Bro TableVal (of type cipher_suites_list) which contains
|
||||
* the cipher suites list of the current analyzed record
|
||||
*/
|
||||
TableVal* SSLv2_Interpreter::analyzeCiphers(SSL_InterpreterEndpoint* s,
|
||||
int length, const u_char* data)
|
||||
{
|
||||
if ( length > MAX_CIPHERSPEC_SIZE )
|
||||
{
|
||||
if ( s == orig )
|
||||
Weird("SSLv2: Client has CipherSpecs > MAX_CIPHERSPEC_SIZE");
|
||||
else
|
||||
Weird("SSLv2: Server has CipherSpecs > MAX_CIPHERSPEC_SIZE");
|
||||
}
|
||||
else
|
||||
{ // cipher specs are not too big
|
||||
if ( ssl_compare_cipherspecs )
|
||||
{ // store cipher specs for state analysis
|
||||
if ( s == resp )
|
||||
pServerCipherSpecs =
|
||||
new SSL_DataBlock(data, length);
|
||||
else
|
||||
pClientCipherSpecs =
|
||||
new SSL_DataBlock(data, length);
|
||||
}
|
||||
}
|
||||
|
||||
const u_char* pCipher = data;
|
||||
bool bExtractCipherSuite = false;
|
||||
TableVal* pCipherTable = 0;
|
||||
|
||||
// We only extract the cipher suite when the corresponding
|
||||
// ssl events are defined (otherwise we do work for nothing
|
||||
// and suffer a memory leak).
|
||||
// FIXME: This check needs to be done only once!
|
||||
if ( (s == orig && ssl_conn_attempt) ||
|
||||
(s == resp && ssl_conn_server_reply) )
|
||||
{
|
||||
pCipherTable = new TableVal(cipher_suites_list);
|
||||
bExtractCipherSuite = true;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < length; i += 3 )
|
||||
{
|
||||
SSL_CipherSpec* pCurrentCipherSpec;
|
||||
uint32 cipherSpecID =
|
||||
((pCipher[0] << 16) | pCipher[1] << 8) | pCipher[2];
|
||||
|
||||
// Check for unknown cipher specs.
|
||||
HashKey h(static_cast<bro_uint_t>(cipherSpecID));
|
||||
pCurrentCipherSpec =
|
||||
(SSL_CipherSpec*) SSL_CipherSpecDict.Lookup(&h);
|
||||
|
||||
if ( ! pCurrentCipherSpec )
|
||||
{
|
||||
if ( s == orig )
|
||||
Weird("SSLv2: Unknown CIPHER-SPEC in CLIENT-HELLO!");
|
||||
else
|
||||
Weird("SSLv2: Unknown CIPHER-SPEC in SERVER-HELLO!");
|
||||
}
|
||||
|
||||
if ( bExtractCipherSuite )
|
||||
{
|
||||
Val* index = new Val(cipherSpecID, TYPE_COUNT);
|
||||
pCipherTable->Assign(index, 0);
|
||||
Unref(index);
|
||||
}
|
||||
|
||||
pCipher += 3;
|
||||
}
|
||||
|
||||
return pCipherTable;
|
||||
}
|
||||
|
||||
// --- SSLv2_EndPoint ---------------------------------------------------------
|
||||
|
||||
/*!
|
||||
* The constructor.
|
||||
*
|
||||
* \param interpreter Pointer to the SSLv2 interpreter to whom this endpoint belongs to
|
||||
* \param is_orig true if this is the originating endpoint of the ssl connection,
|
||||
* false otherwise
|
||||
*/
|
||||
SSLv2_Endpoint::SSLv2_Endpoint(SSLv2_Interpreter* interpreter, int is_orig)
|
||||
: SSL_InterpreterEndpoint(interpreter, is_orig)
|
||||
{
|
||||
sentRecords = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* The destructor.
|
||||
*/
|
||||
SSLv2_Endpoint::~SSLv2_Endpoint()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* This method is called by the SSLProxy_Analyzer with a complete reassembled
|
||||
* SSLv2 record. It passes the record to SSLv2_Interpreter::NewSSLRecord().
|
||||
*
|
||||
* \param t <b>reserved</b> (always zero)
|
||||
* \param seq <b>reserved</b> (always zero)
|
||||
* \param len length of the data block containing the ssl record
|
||||
* \param data pointer to the data block containing the ssl record
|
||||
*/
|
||||
void SSLv2_Endpoint::Deliver(int len, const u_char* data)
|
||||
{
|
||||
++((SSLv2_Endpoint*)peer)->sentRecords;
|
||||
|
||||
((SSLv2_Interpreter*)interpreter)->NewSSLRecord(this, len, data);
|
||||
}
|
239
src/SSLv2.h
239
src/SSLv2.h
|
@ -1,239 +0,0 @@
|
|||
// $Id: SSLv2.h 3526 2006-09-12 07:32:21Z vern $
|
||||
|
||||
#ifndef SSLV2_H
|
||||
#define SSLV2_H
|
||||
|
||||
#include "SSLInterpreter.h"
|
||||
#include "SSLCiphers.h"
|
||||
|
||||
// --- constants for SSLv2 ---------------------------------------------------
|
||||
|
||||
/*!
|
||||
* In SSLv2, each record is of a special message type. Note that the message
|
||||
* type is encrypted if the record has been encrypted, so we can determine
|
||||
* the message type only if we have a cleartext record.
|
||||
*/
|
||||
enum SSLv2_MessageTypes {
|
||||
SSLv2_MT_ERROR = 0, ///< can be in cleartext or encrypted
|
||||
SSLv2_MT_CLIENT_HELLO = 1, ///< always in cleartext
|
||||
SSLv2_MT_CLIENT_MASTER_KEY = 2, ///< always in cleartext
|
||||
SSLv2_MT_CLIENT_FINISHED = 3, ///< always encrypted
|
||||
SSLv2_MT_SERVER_HELLO = 4, ///< always in cleartext
|
||||
SSLv2_MT_SERVER_VERIFY = 5, ///< always encrypted
|
||||
SSLv2_MT_SERVER_FINISHED = 6, ///< always encrypted
|
||||
SSLv2_MT_REQUEST_CERTIFICATE = 7, ///< always encrypted
|
||||
SSLv2_MT_CLIENT_CERTIFICATE = 8, ///< always encrypted
|
||||
};
|
||||
|
||||
// Certificate Type Codes.
|
||||
//
|
||||
// Authentication Type Codes
|
||||
// #define SSL_AT_MD5_WITH_RSA_ENCRYPTION 0x01
|
||||
// Upper/Lower Bounds
|
||||
// #define SSL_MAX_MASTER_KEY_LENGTH_IN_BITS 256
|
||||
// #define SSL_MAX_SESSION_ID_LENGTH_IN_BYTES 16
|
||||
// #define SSL_MIN_RSA_MODULUS_LENGTH_IN_BYTES 64
|
||||
// #define SSL_MAX_RECORD_LENGTH_2_BYTE_HEADER 32767
|
||||
// #define SSL_MAX_RECORD_LENGTH_3_BYTE_HEADER 16383
|
||||
|
||||
const uint8 SSLv2_CT_X509_CERTIFICATE = 0x01;
|
||||
|
||||
/*!
|
||||
* Error codes used in the error record.
|
||||
*/
|
||||
enum SSLv2_ErrorCodes {
|
||||
SSLv2_PE_NO_CIPHER = 0x0001,
|
||||
SSLv2_PE_NO_CERTIFICATE = 0x0002,
|
||||
SSLv2_PE_BAD_CERTIFICATE = 0x0004,
|
||||
SSLv2_PE_UNSUPPORTED_CERTIFICATE_TYPE = 0x0006
|
||||
};
|
||||
|
||||
// --- structs ----------------------------------------------------------------
|
||||
|
||||
const int SSLv2_CLIENT_HELLO_HEADER_SIZE = 9;
|
||||
struct SSLv2_ClientHelloHeader {
|
||||
uint8 messageType;
|
||||
uint16 clientVersion;
|
||||
uint16 cipherSpecLength;
|
||||
uint16 sessionIdLength;
|
||||
uint16 challengeLength;
|
||||
};
|
||||
|
||||
const int SSLv2_SERVER_HELLO_HEADER_SIZE = 11;
|
||||
struct SSLv2_ServerHelloHeader {
|
||||
uint8 messageType;
|
||||
uint8 sessionIdHit;
|
||||
uint8 certificateType;
|
||||
uint16 serverVersion;
|
||||
uint16 certificateLength;
|
||||
uint16 cipherSpecLength;
|
||||
uint16 connectionIdLength;
|
||||
};
|
||||
|
||||
const int SSLv2_CLIENT_MASTER_KEY_HEADER_SIZE = 10;
|
||||
struct SSLv2_ClientMasterKeyHeader {
|
||||
uint8 messageType;
|
||||
uint32 cipherKind; // caution: is an uint24
|
||||
uint16 clearKeyLength;
|
||||
uint16 encryptedKeyLength;
|
||||
uint16 keyArgLength;
|
||||
};
|
||||
|
||||
const unsigned int SSLv2_ERROR_RECORD_SIZE = 3;
|
||||
struct SSLv2_ErrorRecord {
|
||||
uint8 messageType;
|
||||
uint16 errorCode;
|
||||
};
|
||||
|
||||
const unsigned int SSLv2_CLIENT_FINISHED_HEADER_SIZE = 1;
|
||||
struct SSLv2_ClientFinished {
|
||||
uint8 messageType;
|
||||
//char CONNECTION-ID[N-1]
|
||||
};
|
||||
|
||||
struct SSLv2_ServerVerify {
|
||||
uint8 messageType;
|
||||
//char CHALLENGE-DATA[N-1]
|
||||
};
|
||||
|
||||
struct SSLv2_ServerFinished {
|
||||
uint8 messageType;
|
||||
//char SESSION-ID-DATA[N-1]
|
||||
};
|
||||
|
||||
// MISSING:
|
||||
// CLIENT-CERTIFICATE
|
||||
// REQUEST-CERTIFICATE
|
||||
|
||||
/*!
|
||||
* States used by the internal SSLv2 automaton.
|
||||
*/
|
||||
enum SSLv2_States {
|
||||
START, ///< start state, no data seen yet
|
||||
CLIENT_HELLO_SEEN, ///< client hello flew by
|
||||
NEW_SESSION, ///< server hello with sessionIdHit == 0 seen
|
||||
CACHED_SESSION, ///< server hello with sessionIdHit != 0 seen
|
||||
CLIENT_MASTERKEY_SEEN, ///< we saw a client master key record
|
||||
ERROR_SEEN, ///< we saw an error record
|
||||
ERROR_REQUIRED ///< one of our critical checks failed, so we think we should see an error record
|
||||
};
|
||||
|
||||
|
||||
// --- forward declarations ---------------------------------------------------
|
||||
|
||||
class SSLv2_Interpreter;
|
||||
class SSLv2_Endpoint;
|
||||
class SSLv2_Record;
|
||||
class SSL_DataBlock;
|
||||
class SSL_RecordBuilder;
|
||||
|
||||
// --- class SSLv2_Interpreter ------------------------------------------------
|
||||
|
||||
/*!
|
||||
* \brief This class is used to analyze SSLv2 connections.
|
||||
*
|
||||
* Since there's currently no support for decrypting ssl connections, analysis
|
||||
* stops when a connection switches to encrypted communication.
|
||||
* The interpreter does several checks, both record- and connection-orientated.
|
||||
*
|
||||
* The record checks mainly consist of consistency checks, where the correct
|
||||
* use of the SSL 2.0 specification is checked. Furthermore, the CIPHER-SPECS
|
||||
* of the client and the server can be compared to detect non-intersecting sets.
|
||||
*
|
||||
* The connection check monitors the handshaking process for invalid transitions,
|
||||
* until the end of the cleartext phase.
|
||||
*
|
||||
* Several events are thrown for BroScript, including client connection attempt,
|
||||
* server reply, ssl connection establishment/reuse of former connection, proposed
|
||||
* cipher suites and certificates seen.
|
||||
*
|
||||
* \see SSLv2_Endpoint
|
||||
*/
|
||||
class SSLv2_Interpreter : public SSL_Interpreter {
|
||||
public:
|
||||
SSLv2_Interpreter(SSLProxy_Analyzer* proxy);
|
||||
~SSLv2_Interpreter();
|
||||
|
||||
void NewSSLRecord(SSL_InterpreterEndpoint* s, int length, const u_char* data);
|
||||
void analyzeRecord(SSL_InterpreterEndpoint* s, int length, const u_char* data);
|
||||
SSLv2_States ClientHelloRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength,
|
||||
const u_char* recordData);
|
||||
SSLv2_States ServerHelloRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength, const u_char* recordData);
|
||||
SSLv2_States ClientMasterKeyRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength,
|
||||
const u_char* recordData);
|
||||
SSLv2_States ErrorRecord(SSL_InterpreterEndpoint* s,
|
||||
int recordLength,
|
||||
const u_char* recordData);
|
||||
|
||||
TableVal* analyzeCiphers(SSL_InterpreterEndpoint* s,
|
||||
int length, const u_char* data);
|
||||
SSLv2_States ConnState();
|
||||
|
||||
static void printStats();
|
||||
|
||||
#define MAX_CIPHERSPEC_SIZE ssl_max_cipherspec_size
|
||||
|
||||
// Global connection counters.
|
||||
static uint totalConnections; ///< counter for total sslv2 connections
|
||||
static uint analyzedConnections; ///< counter for analyzed (=not partial) connections
|
||||
static uint openedConnections; ///< counter for SSLv2 connections with complete handshake
|
||||
static uint failedConnections; ///< counter for SSLv2 connections with failed but correct handshake
|
||||
static uint weirdConnections; ///< counter for SSLv2 connections with failed and weird handshake
|
||||
|
||||
// Global record counters.
|
||||
static uint totalRecords; ///< counter for total SSLv2 records seen
|
||||
static uint clientHelloRecords; ///< counter for SSLv2 CLIENT-HELLOs seen
|
||||
static uint serverHelloRecords; ///< counter for SSLv2 SERVER-HELLOs seen
|
||||
static uint clientMasterKeyRecords; ///< counter for SSLv2 CLIENT-MASTER-KEYSs seen
|
||||
static uint errorRecords; ///< counter for SSLv2 ERRORs seen
|
||||
|
||||
// Counters for this instance.
|
||||
uint32 records; ///< counter for SSLv2 records of this connection
|
||||
SSLv2_States connState; ///< state of connection
|
||||
|
||||
bool bAnalyzedCounted; ///< flag for counting analyzedConnections
|
||||
|
||||
// FIXME: this should be states.
|
||||
bool bClientWantsCachedSession; ///< true if the client wants a cached session, false otherwise
|
||||
|
||||
|
||||
protected:
|
||||
void BuildInterpreterEndpoints();
|
||||
|
||||
SSL_DataBlock* pClientCipherSpecs; ///< the CIPHER-SPECs from the client
|
||||
SSL_DataBlock* pServerCipherSpecs; ///< the CIPHER-SPECs from the server
|
||||
SSLv2_CipherSpec usedCipherSpec; ///< the used CIPHER-SPEC for this connection
|
||||
|
||||
// Currently experimental:
|
||||
SSL_DataBlock* pConnectionId; // 16 <= ConnectionId <= 32
|
||||
SSL_DataBlock* pChallenge; // 16 <= Challenge <= 32
|
||||
SSL_DataBlock* pSessionId; // has to be 16 Bytes
|
||||
SSL_DataBlock* pMasterClearKey;
|
||||
SSL_DataBlock* pMasterEncryptedKey;
|
||||
SSL_DataBlock* pClientReadKey;
|
||||
SSL_DataBlock* pServerReadKey;
|
||||
};
|
||||
|
||||
// --- class SSLv2_Endpoint ---------------------------------------------------
|
||||
|
||||
/*!
|
||||
* \brief This class represents an endpoint of an SSLv2 connection.
|
||||
*
|
||||
* Fully reassembled SSLv2 records are passed to its Deliver() function.
|
||||
* There, some counters are updated and the record is then passed to
|
||||
* SSLv2_Interpreter::NewSSLRecord().
|
||||
*/
|
||||
class SSLv2_Endpoint: public SSL_InterpreterEndpoint {
|
||||
public:
|
||||
SSLv2_Endpoint(SSLv2_Interpreter* interpreter, int is_orig);
|
||||
virtual ~SSLv2_Endpoint();
|
||||
|
||||
void Deliver(int len, const u_char* data);
|
||||
|
||||
uint32 sentRecords; ///< counter for sent records of this endpoint
|
||||
};
|
||||
|
||||
#endif
|
1471
src/SSLv3.cc
1471
src/SSLv3.cc
File diff suppressed because it is too large
Load diff
590
src/SSLv3.h
590
src/SSLv3.h
|
@ -1,590 +0,0 @@
|
|||
// $Id: SSLv3.h 3526 2006-09-12 07:32:21Z vern $
|
||||
|
||||
#ifndef sslv3_h
|
||||
#define sslv3_h
|
||||
|
||||
#include "SSLInterpreter.h"
|
||||
#include "SSLProxy.h"
|
||||
#include "SSLv3Automaton.h"
|
||||
#include "SSLDefines.h"
|
||||
|
||||
// Offsets in SSL record layer header.
|
||||
const int SSL3_1_CONTENTTYPEOFFSET = 0;
|
||||
const int SSL3_1_VERSIONTYPEOFFSET = 1;
|
||||
const int SSL3_1_LENGTHOFFSET = 3;
|
||||
const int SSL3_1_HEADERLENGTH = 5;
|
||||
|
||||
// --- forward declarations ---------------------------------------------------
|
||||
|
||||
class SSL_Interpreter;
|
||||
class SSL_InterpreterEndpoint;
|
||||
class SSLProxy_Analyzer;
|
||||
class SSLv3_Endpoint;
|
||||
class SSLv3_Record;
|
||||
class SSLv3_HandshakeRecord;
|
||||
class SSLv3_AlertRecord;
|
||||
class SSLv3_ApplicationRecord;
|
||||
class SSLv3_ChangeCipherRecord;
|
||||
class CertStore;
|
||||
struct SSL_CipherSpec;
|
||||
|
||||
// --- enums for SSLv3.0/3.1 message handling ---------------------------------
|
||||
|
||||
enum SSL3_1_ContentType {
|
||||
SSL3_1_TYPE_CHANGE_CIPHER_SPEC = 20,
|
||||
SSL3_1_TYPE_ALERT = 21,
|
||||
SSL3_1_TYPE_HANDSHAKE = 22,
|
||||
SSL3_1_TYPE_APPLICATION_DATA = 23
|
||||
};
|
||||
|
||||
enum SSL3_1_HandshakeType {
|
||||
SSL3_1_HELLO_REQUEST = 0,
|
||||
SSL3_1_CLIENT_HELLO = 1,
|
||||
SSL3_1_SERVER_HELLO = 2,
|
||||
SSL3_1_CERTIFICATE = 11,
|
||||
SSL3_1_SERVER_KEY_EXCHANGE = 12,
|
||||
SSL3_1_CERTIFICATE_REQUEST = 13,
|
||||
SSL3_1_SERVER_HELLO_DONE = 14,
|
||||
SSL3_1_CERTIFICATE_VERIFY = 15,
|
||||
SSL3_1_CLIENT_KEY_EXCHANGE = 16,
|
||||
SSL3_1_FINISHED = 20
|
||||
};
|
||||
|
||||
enum SSL3_1_AlertDescription {
|
||||
SSL3_1_CLOSE_NOTIFY = 0,
|
||||
SSL3_1_UNEXPECTED_MESSAGE = 10,
|
||||
SSL3_1_BAD_RECORD_MAC = 20,
|
||||
SSL3_1_DECRYPTION_FAILED = 21,
|
||||
SSL3_1_RECORD_OVERFLOW = 22,
|
||||
SSL3_1_DECOMPRESSION_FAILURE = 30,
|
||||
SSL3_1_HANDSHAKE_FAILURE = 40,
|
||||
SSL3_0_NO_CERTIFICATE = 41,
|
||||
SSL3_1_BAD_CERTIFICATE = 42,
|
||||
SSL3_1_UNSUPPORTED_CERTIFICATE = 43,
|
||||
SSL3_1_CERTIFICATE_REVOKED = 44,
|
||||
SSL3_1_CERTIFICATE_EXPIRED = 45,
|
||||
SSL3_1_CERTIFICATE_UNKNOWN = 46,
|
||||
SSL3_1_ILLEGAL_PARAMETER = 47,
|
||||
SSL3_1_UNKNOWN_CA = 48,
|
||||
SSL3_1_ACCESS_DENIED = 49,
|
||||
SSL3_1_DECODE_ERROR = 50,
|
||||
SSL3_1_DECRYPT_ERROR = 51,
|
||||
SSL3_1_EXPORT_RESTRICTION = 60,
|
||||
SSL3_1_PROTOCOL_VERSION = 70,
|
||||
SSL3_1_INSUFFICIENT_SECURITY = 71,
|
||||
SSL3_1_INTERNAL_ERROR = 80,
|
||||
SSL3_1_USER_CANCELED = 90,
|
||||
SSL3_1_NO_RENEGOTIATION = 100
|
||||
};
|
||||
|
||||
enum SSL3x_AlertLevel {
|
||||
SSL3x_ALERT_LEVEL_WARNING = 1,
|
||||
SSL3x_ALERT_LEVEL_FATAL = 2
|
||||
};
|
||||
|
||||
// --- structs ----------------------------------------------------------------
|
||||
|
||||
struct SSLv3x_Random {
|
||||
uint32 gmt_unix_time;
|
||||
SSL_DataBlock* random_bytes; // 28-bytes
|
||||
};
|
||||
|
||||
struct SSLv3x_ServerRSAParams {
|
||||
SSL_DataBlock* rsa_modulus; // <1..2^16-1>
|
||||
SSL_DataBlock* rsa_exponent; // <1..2^16-1>
|
||||
};
|
||||
|
||||
struct SSLv3x_ServerDHParams{
|
||||
SSL_DataBlock* dh_p; // <1..2^16-1>
|
||||
SSL_DataBlock* dh_g; // <1..2^16-1>
|
||||
SSL_DataBlock* dh_Ys; // <1..2^16-1>
|
||||
};
|
||||
|
||||
struct SSLv3x_EncryptedPremasterSecret{
|
||||
SSL_DataBlock* encryptedSecret;
|
||||
};
|
||||
|
||||
struct SSLv3x_ClientDHPublic{
|
||||
SSL_DataBlock* dh_Yc; // <1..2^16-1>
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class SSLv3_Interpreter is the implementation for a SSLv3.0/SSLv3.1 connection
|
||||
* interpreter (derived from the abstract class SSL_Interpreter).
|
||||
*
|
||||
* The corresponding SSLProxy_Analyzer creates an instance of this class and
|
||||
* properly initialises the corresponding SSLv3_Endpoints by calling the
|
||||
* SSL_Interpreter's Init() method which then invokes the BuildInterpreterEndpoints() method
|
||||
* (of the SSLv3_Interpreter) which causes the SSLv3_Endpoints to be created properly.
|
||||
*
|
||||
* The SSLv3_Interpreter receives the four various SSLv3_Records:
|
||||
* - SSLv3_HandshakeRecord
|
||||
* - SSLv3_AlertRecord
|
||||
* - SSLv3_ChangeCipherRecord
|
||||
* - SSLv3_ApplicationRecord
|
||||
* via the DeliverSSLv3_Record() methods from the two corresponding SSLv3_Endpoints
|
||||
* (which get fed by the SSLProxy_Analyzer).
|
||||
*
|
||||
* There is one global static SSLv3_Automaton which describes the possible transitions
|
||||
* in the SSLv3.0/SSLv3.1 state machine for the handshaking phase. This automaton
|
||||
* is initialised once, when Bro sees the first SSL connection. By the attribute
|
||||
* currentState every instance of SSLv3_Interpreter holds the automaton state it
|
||||
* is currently in and so is able
|
||||
* to track the correctness of the SSL handshaking process. It weird-checks and verifies
|
||||
* all arriving SSL records up to the point where handshaking is finished or an
|
||||
* error occures caused by weird SSL records or not allowed transitions in the
|
||||
* state machine. Information that was negotiated between the client and server
|
||||
* during the handshaking phase is/may also be stored. That is:
|
||||
* - used SSL version
|
||||
* - negotiated cipher suite
|
||||
* - session ID
|
||||
* - client/server random
|
||||
* - key exchange algorithms
|
||||
* - cryptographic parameters
|
||||
* - encrypted pre-master secret
|
||||
*
|
||||
* The certificates are verified using the analyzeCertificate() method of the
|
||||
* SSL_Interpreter class.
|
||||
* Events for Bro scripting are thrown for client connection attempt, server reply,
|
||||
* ssl connection establishment/reuse of former connection, proposed cipher suites and
|
||||
* seen certificates.
|
||||
*
|
||||
* @see SSLv3_Endpoint
|
||||
*/
|
||||
class SSLv3_Interpreter : public SSL_Interpreter {
|
||||
public:
|
||||
/** The constructor takes the SSLProxy_Analyzer as argument,
|
||||
* that created this SSLv3_Interpreter.
|
||||
*
|
||||
* @param proxy the creating SSL_ConectionProxy
|
||||
*/
|
||||
SSLv3_Interpreter(SSLProxy_Analyzer* proxy);
|
||||
~SSLv3_Interpreter();
|
||||
|
||||
/** Delivers an SSLv3_HandshakeRecord to the SSLv3_Interpreter.
|
||||
* The record gets verified and it is checked whether it is allowed
|
||||
* in the current phase of the handshaking process.
|
||||
*
|
||||
* @param rec the SSLv3_HandshakeRecord
|
||||
*/
|
||||
void DeliverSSLv3_Record(SSLv3_HandshakeRecord* rec);
|
||||
|
||||
/** Delivers an SSLv3_AlertRecord to the SSLv3_Interpreter.
|
||||
* The record gets verified and weird checked.
|
||||
*
|
||||
* @param rec the SSLv3_AlertRecord
|
||||
*/
|
||||
void DeliverSSLv3_Record(SSLv3_AlertRecord* rec);
|
||||
|
||||
/** Delivers an SSLv3_ChangeCipherRecord to the SSLv3_Interpreter.
|
||||
* The record gets verified and weird checked. If a change cipher
|
||||
* record is received the next record from this endpoint needs
|
||||
* to be a finished message.
|
||||
*
|
||||
* @param rec the SSLv3_ChangeCipherRecord
|
||||
*/
|
||||
void DeliverSSLv3_Record(SSLv3_ChangeCipherRecord* rec);
|
||||
|
||||
/** Delivers a SSLv3_ApplicationRecord to the SSLv3_Interpreter.
|
||||
* It is checked, whether handshaking phase is already finished
|
||||
* and sending application data is valid (the normal case is,
|
||||
* that after finishing the handshaking phase, all further data
|
||||
* is skipped).
|
||||
*
|
||||
* @param rec the SSLv3_AlertRecord
|
||||
*/
|
||||
void DeliverSSLv3_Record(SSLv3_ApplicationRecord* rec);
|
||||
|
||||
/** This method sets the currentState variable of this SSLv3_Interpreter
|
||||
* and so sets the SSLv3.0/SSLv3.1 state machine to the state passed
|
||||
* as parameter.It is invoked in the SSLProxy_Analyzer to enable
|
||||
* this SSLv3_Interpreter to start work without having seen the first
|
||||
* handshake record. This happens, when the client hello is sent in
|
||||
* SSLv2 format and processed by the SSLv2_Interpreter but the further
|
||||
* SSL connection taking place as a SSLv3.0/SSLv3.1 session.
|
||||
*
|
||||
* @param i the new state of the sslAutomaton
|
||||
*/
|
||||
void SetState(int i);
|
||||
|
||||
static void printStats();
|
||||
|
||||
// Total SSLv3x connections.
|
||||
static uint totalConnections;
|
||||
|
||||
// Total SSLv3x connections with <b>complete</b> handshake.
|
||||
static uint openedConnections;
|
||||
|
||||
static uint totalRecords; ///< counter for total SSLv3x records seen
|
||||
static uint handshakeRecords; ///< counter for SSLv3x handshake records seen
|
||||
static uint clientHelloRecords; ///< counter for SSLv3x client hellos seen
|
||||
static uint serverHelloRecords; ///< counter for SSLv3x server hellos seen
|
||||
static uint alertRecords; ///< counter for SSLv3x alert records seen
|
||||
static uint changeCipherRecords; ///< counter for SSLv3x change cipher records seen
|
||||
|
||||
/**Flags for handling the change-cipher-messages and fin-handshake-
|
||||
* messages
|
||||
*/
|
||||
bool change_cipher_client_seen; ///< whether a client change cipher record was seen
|
||||
bool change_cipher_server_seen; ///< whether a server change cipher record was seen
|
||||
bool fin_client_seen; ///< whether a client finished handshake message was seen (must immediately follow the client change cipher)
|
||||
bool fin_server_seen; ///< whether a server finished handshake message was seen (must immediately follow the server change cipher)
|
||||
|
||||
protected:
|
||||
static SSLv3_Automaton sslAutomaton; ///< represents the SSLv3.0/SSLv3.1 automaton
|
||||
static bool bInited; ///< whether the automaton is already initialised (has to be only done once)
|
||||
int currentState; ///< the current state of the SSL automaton in this SSLv3_Interpreter instance
|
||||
|
||||
// uint16 cipherSuite; ///< the cipher spec client and server agreed upon
|
||||
SSL_DataBlock* pClientCipherSpecs; ///< the CIPHER-SPECs from the client
|
||||
SSL_CipherSpec* pCipherSuite; ///< pointer to the cipher spec definition client and server agreed upon
|
||||
uint32 cipherSuiteIdentifier; ///< only used for unknown cipher-specs
|
||||
SSL_DataBlock* clientSessionID; ///< the session ID of the client hello record
|
||||
SSL_DataBlock* serverSessionID; ///< the session ID for this SSL session
|
||||
|
||||
/**Attributes for cryptographic computations*/
|
||||
SSLv3x_Random* clientRandom;
|
||||
SSLv3x_Random* serverRandom;
|
||||
//SSL_KeyExchangeAlgorithm keyXAlgorithm;
|
||||
SSLv3x_ServerRSAParams* serverRSApars;
|
||||
SSLv3x_ServerDHParams* serverDHPars;
|
||||
SSLv3x_EncryptedPremasterSecret* encryptedPreSecret;
|
||||
SSLv3x_ClientDHPublic* clientDHpublic;
|
||||
|
||||
bool helloRequestValid; ///< Whether sending a hello request is valid (normally after handshaking phase)
|
||||
|
||||
/** This method builds the corresponding SSLv3_Endpoints for this SSLv3_Interpreter.
|
||||
* It is called in the SSL_Interpreter's Init() method.
|
||||
*/
|
||||
void BuildInterpreterEndpoints();
|
||||
|
||||
/** This method initialises the SSL state automaton; sets the states and transitions.
|
||||
* It needs only to be called once for a whole bro. @see SSLDefines.h
|
||||
*/
|
||||
void BuildAutomaton();
|
||||
|
||||
/** This helper method translates the handshake types included in the SSL handshake
|
||||
* records to the corresponding transition of the SSL automaton. It is invoked within
|
||||
* the DeliverSSLv3_Record(SSLv3_HandshakeRecord*) method.
|
||||
*
|
||||
* @param type the handshake type of the handshake record
|
||||
*/
|
||||
int HandshakeType2Trans(int type);
|
||||
|
||||
/** This method is used for event generation during the handshaking phase and
|
||||
* generates the connection-attempt, server-reply, connection-established, connection-reused
|
||||
* events dependant on the currentState of the SSL Automaton.
|
||||
* It calls the appropriate fire_* methods of the SSL_Interpreter for this.
|
||||
* The method is called within the the DeliverSSLv3_Record() methods.
|
||||
*
|
||||
* @param rec the SSLv3_Record which is currently processed
|
||||
*/
|
||||
void GenerateEvents(SSLv3_Record* rec, TableVal* curCipherSuites);
|
||||
|
||||
/** This method analyzes the cipher suites the client and server offer
|
||||
* each other during handshaking phase. It checks, whether it's a 'common'
|
||||
* cipher suite and sets the pCipherSuite attribute according to the
|
||||
* cipher suite client and server agreed on.
|
||||
*
|
||||
* @param s the SSLv3_Endpoint which sent the SSL record with the cipher suite(s) to be analyzed
|
||||
* @param length length of data
|
||||
* @param data pointer to where the cipher suites can be found
|
||||
* @param version SSL version of the SSL record that contained the cipher suite(s)
|
||||
* @return a pointer to a Bro TableVal (of type cipher_suites_list) which contains
|
||||
* the cipher suites list of the current analyzed record
|
||||
*/
|
||||
TableVal* analyzeCiphers(const SSLv3_Endpoint* s, int length, const u_char* data, uint16 version);
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** Class SSLv3_Endpoint is the implementation for SSLv3.0/SSLv3.1 connection
|
||||
* endpoints (derived from the abstract class SSL_InterpreterEndpoint).
|
||||
*
|
||||
* A SSLv3_Endpoint gets completely reassembled ssl records via the method
|
||||
* Deliver(), which is invoked in this Endpoint's corresponding SSLProxy_Analyzer.
|
||||
* The defragmentation and reassembling already took place in the
|
||||
* SSLProxy_Analyzer's SSL_RecordBuilder.
|
||||
* The Deliver()-method does some basic weird checks and then calls
|
||||
* ProcessMessage() which determines the content type of the ssl record
|
||||
* and, dependant on that, creates an instance of the appropriate SSLv3_Record.
|
||||
* This is passed on to this endpoint's corresponding SSLv3_Interpreter via
|
||||
* the DeliverSSLv3_Record()-method.
|
||||
*/
|
||||
class SSLv3_Endpoint : public SSL_InterpreterEndpoint {
|
||||
public:
|
||||
/** The constructor takes the corresponding SSL_Interpreter as argument.
|
||||
* is_orig sets this endpoint as originator of the connection (1), and
|
||||
* responder otherwise (0).
|
||||
*
|
||||
* @param interpreter the SSL_Interpreter this endpoint is bound to.
|
||||
* @param is_orig whether this endpoint is the originator (1) of the
|
||||
* connection or not (0).
|
||||
*/
|
||||
SSLv3_Endpoint(SSL_Interpreter* interpreter, int is_orig);
|
||||
virtual ~SSLv3_Endpoint();
|
||||
|
||||
/** This method is invoked by this endpoint's corresponding
|
||||
* SSLProxy_Analyzer and receives completely reassembled SSL
|
||||
* records (by the data argument).
|
||||
*
|
||||
* @param t time is always 0 (former: when the segment was received
|
||||
* by bro (?))
|
||||
* @param len length of SSL record
|
||||
* @param data content of SSL record
|
||||
*/
|
||||
void Deliver(int len, const u_char* data);
|
||||
|
||||
protected:
|
||||
uint16 sslVersion; ///< holds the version of the just delivered SSL record
|
||||
uint16 currentMessage_length; ///< the length of the just delivered SSL record
|
||||
|
||||
/** This method extracts the content type of the SSL record passed
|
||||
* in the first parameter.
|
||||
*
|
||||
* @param data the SSL record
|
||||
* @param len length of the record
|
||||
* @return SSL3_1_ContentType of SSL record
|
||||
*/
|
||||
SSL3_1_ContentType ExtractContentType(const u_char* data, int len);
|
||||
|
||||
/** This method determines the version of the SSL record passed to it.
|
||||
* It sets the field sslVersion of this endpoint an is called within
|
||||
* the method ProcessMessage().
|
||||
*
|
||||
* @param data the SSL record
|
||||
* @param len length of the record
|
||||
* @return 0 if version is NOT 3.0/3.1, 1 otherwise
|
||||
*/
|
||||
int ExtractVersion(const u_char* data, int len);
|
||||
|
||||
/** This method processes a complete SSL record. It
|
||||
* determines the content type of the SSL record
|
||||
* (handshake, alert, change-cipher-spec, application),
|
||||
* cuts away the SSL record layer header and generates
|
||||
* the appropriate SSLv3_Record. Then it calls the SSLv3_Record's
|
||||
* Deliver() method, which manages the delivery of the record
|
||||
* to the corresponding SSLv3_Interpreter
|
||||
*
|
||||
* @param data the complete SSL record
|
||||
* @param len data's (record's) length
|
||||
*/
|
||||
void ProcessMessage(const u_char* data, int len);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Offsets are now relative to the end of the SSL record layer header
|
||||
#define SSL3_1_CHANGE_CIPHER_TYPE_OFFSET (SSL3_1_HEADERLENGTH - 5)
|
||||
#define SSL3_1_ALERT_LEVEL_OFFSET (SSL3_1_HEADERLENGTH - 5)
|
||||
#define SSL3_1_ALERT_DESCRIPTION_OFFSET (SSL3_1_HEADERLENGTH - 4)
|
||||
#define SSL3_1_SESSION_ID_LENGTH_OFFSET 38
|
||||
#define SSL3_1_SESSION_ID_OFFSET 39
|
||||
|
||||
/** This class is an abstract base class for the four different
|
||||
* SSLv3.0/SSLv3.1 record types (handshake, alert, change-cipher-spec,
|
||||
* application).
|
||||
*
|
||||
* It contains a pointer to the data of the SSL record <b>without</b>
|
||||
* the SSL record layer header, it's length and the version information, which
|
||||
* was present in the now cut away SSL record layer header.
|
||||
*
|
||||
* (Note: the version field of the SSL record layer header may differ from
|
||||
* the version of the record format that was used when sending the record.
|
||||
* (e.g. a client may send a SSLv2 record including
|
||||
* a version field containing 3.1 (for SSLv3.1) to show, that he supports
|
||||
* version 3.1.))
|
||||
*
|
||||
* Every subclass of SSLv3_Record implements the Deliver() method, which
|
||||
* manages the delivery of the record to the corresponding SSLv3_Interpreter.
|
||||
* Instances of SSLv3_Record (resp. it's subclasses) are created within
|
||||
* the ProcessMessage() method of a SSLv3_Endpoint.
|
||||
* */
|
||||
class SSLv3_Record : public BroObj{
|
||||
public:
|
||||
/** The constructor gets a pointer to the SSL record without the
|
||||
* record layer header, it's length, the version information
|
||||
* contained in the record layer header and a pointer to the
|
||||
* SSLv3_Endpoint that created this instance of SSLv3_Record.
|
||||
*
|
||||
* @param data pointer to the SSL record without record layer header
|
||||
* @param data's length
|
||||
* @param version version information contained in the SSL record layer header
|
||||
* @param e the SSLv3_Endpoint that created this instance
|
||||
*/
|
||||
SSLv3_Record(const u_char* data, int len, uint16 version,
|
||||
SSLv3_Endpoint const* e);
|
||||
~SSLv3_Record();
|
||||
|
||||
void Describe(ODesc* d) const;
|
||||
|
||||
int GetRecordLength() const;
|
||||
const u_char* GetData() const;
|
||||
uint16 GetVersion() const;
|
||||
SSLv3_Endpoint const* GetEndpoint() const;
|
||||
|
||||
/** This abstract method is implemented by the various SSLv3_Record
|
||||
* subclasses for handshake, alert, change cipher spec and application
|
||||
* records. It manages the delivery of the SSLv3_Record to the
|
||||
* SSLv3_Interpreter passed as argument.
|
||||
* SSLv3_Records are created within the ProcessMessage() method of the
|
||||
* SSLv3_Endpoint which then calls the just created SSLv3_Records
|
||||
* Deliver() method with it's corresponding SSLv3_Interpreter as
|
||||
* argument which then receives the (evtl. preprocessed) SSLv3_Record
|
||||
* (via the method(s) DeliverSSLv3_Record()).
|
||||
*
|
||||
* @param conn the SSLv3_Interpreter to which this SSLv3_Record should be deliverd
|
||||
*/
|
||||
virtual void Deliver(SSLv3_Interpreter* conn) =0;
|
||||
|
||||
/** Helper function that converts from 24-bit big endian integer
|
||||
* starting at offset to 32 bit integer in little endian format.
|
||||
*
|
||||
* @param data the ssl-record
|
||||
* @param len length of data (record)
|
||||
* @param offset where the 24 bit big endian starts
|
||||
* @return 32 bit little endian
|
||||
*/
|
||||
int ExtractInt24(const u_char* data, int len, int offset);
|
||||
|
||||
int recordLength; ///< total length of the SSL record <b>without</b> the record layer header
|
||||
const u_char* data; ///< pointer to the SSL record without the record layer header
|
||||
SSLv3_Endpoint const* endp; ///< pointer to the SSLv3_Endpoint that created this instance of SSLv3_Record
|
||||
uint16 sslVersion; ///< version information of the SSL record layer header
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/* This class represents a handshake record used in SSLv3.0/SSLv3.1.
|
||||
*
|
||||
* Handshake records in SSLv3.0/SSLv3.1 need a special treatment,
|
||||
* because it is possible that multiple handshake messages are coalesced into
|
||||
* a single SSLv3.0/SSLv3.1 record.
|
||||
* I think, this only can happen to
|
||||
* handshake records (even RFC2246 page 16 generally talks about all
|
||||
* messages of a same content type), because only handshake records
|
||||
* have got an own length descriptor within and thus make de-coalescing
|
||||
* possible.
|
||||
* So when generating a new instance of a SSLv3_HandshakeRecord, it is checked whether there
|
||||
* are more handshake records within data.
|
||||
* If so, they are put apart and linked together to a chain by using
|
||||
* the next-pointer.
|
||||
* The Deliver() method takes this into account and delivers every single
|
||||
* handshake record one by one to the SSLv3_Interpreter.
|
||||
*/
|
||||
class SSLv3_HandshakeRecord : public SSLv3_Record{
|
||||
public:
|
||||
SSLv3_HandshakeRecord(const u_char* data, int len, uint16 version,
|
||||
SSLv3_Endpoint const* e);
|
||||
~SSLv3_HandshakeRecord();
|
||||
|
||||
int GetType() const;
|
||||
int GetLength() const;
|
||||
|
||||
/** This method delivers the SSLv3_HandshakeRecord(s) to the
|
||||
* SSLv3_Interpreter passed as argument. The method follows
|
||||
* the next-pointer and delivers every SSLv3_HandshakeRecord
|
||||
* contained in this list to the SSLv3_Interpreter.
|
||||
*
|
||||
* @param conn the SSLv3_Interpreter to which this SSLv3_HandshakeRecord should be deliverd
|
||||
*/
|
||||
void Deliver(SSLv3_Interpreter* conn);
|
||||
|
||||
/* This method is invoked within the SSLv3_Interpreter and does lots of
|
||||
* weird and consistency checks on a client hello SSL handshake record.
|
||||
*
|
||||
* @return 0 if further processing of this client hello is not
|
||||
* possible due to inconsistency and 1 otherwise.
|
||||
*/
|
||||
int checkClientHello();
|
||||
|
||||
/* This method is invoked within the SSLv3_Interpreter and does lots of
|
||||
* weird and consistency checks on a server hello SSL handshake record.
|
||||
*
|
||||
* @return 0 if further processing of this server hello is not
|
||||
* possible due to inconsistency and 1 otherwise.
|
||||
*/
|
||||
int checkServerHello();
|
||||
|
||||
int type; ///< holds the handshake type of the handshake record (first byte)
|
||||
int length; ///< holds the length of this handshake record (which is needed due to coalesced handshake messages)
|
||||
|
||||
private:
|
||||
SSLv3_HandshakeRecord* next; ///< pointer to the next ssl handshake record if they are coalesced into a single record
|
||||
|
||||
SSLv3_HandshakeRecord* GetNext();
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** This class represents an alert record used in SSLv3.0/SSLv3.1.
|
||||
*
|
||||
* description holds the SSL alert description and level the alert level.
|
||||
*/
|
||||
class SSLv3_AlertRecord : public SSLv3_Record {
|
||||
public:
|
||||
SSLv3_AlertRecord(const u_char* data, int len, uint16 version,
|
||||
SSLv3_Endpoint const* e);
|
||||
~SSLv3_AlertRecord();
|
||||
|
||||
int GetDescription() const;
|
||||
int GetLevel() const;
|
||||
|
||||
/** This method delivers the SSLv3_AlertRecord to the SSLv3_Interpreter passed as
|
||||
* argument.
|
||||
*
|
||||
* @param conn the SSLv3_Interpreter to which this SSLv3_AlertRecord should be deliverd
|
||||
*/
|
||||
void Deliver(SSLv3_Interpreter* conn);
|
||||
|
||||
int description; ///< holds the alert description
|
||||
int level; ///< holds the alert level
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** This class represents a change cipher record used in SSLv3.0/SSLv3.1.
|
||||
*
|
||||
* type holds the change cipher type used (currently only 1 is valid (rfc 2246))
|
||||
*/
|
||||
class SSLv3_ChangeCipherRecord : public SSLv3_Record{
|
||||
public:
|
||||
SSLv3_ChangeCipherRecord(const u_char* data, int len, uint16 version,
|
||||
SSLv3_Endpoint const* e);
|
||||
~SSLv3_ChangeCipherRecord();
|
||||
int GetType() const;
|
||||
|
||||
/** This method delivers the SSLv3_ChangeCipherRecord to the
|
||||
* SSLv3_Interpreter passed as argument.
|
||||
*
|
||||
* @param conn the SSLv3_Interpreter to which this
|
||||
* SSLv3_ChangeCipherRecord should be delivered
|
||||
*/
|
||||
void Deliver(SSLv3_Interpreter* conn);
|
||||
|
||||
int type; ///< holds the change cipher type
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** This class represents an application record used in SSLv3.0/SSLv3.1.
|
||||
*/
|
||||
class SSLv3_ApplicationRecord : public SSLv3_Record {
|
||||
public:
|
||||
SSLv3_ApplicationRecord(const u_char* data, int len, uint16 version,
|
||||
SSLv3_Endpoint const* e);
|
||||
~SSLv3_ApplicationRecord();
|
||||
|
||||
/** This method delivers the SSLv3_ApplicationRecord to the
|
||||
* SSLv3_Interpreter passed as argument.
|
||||
*
|
||||
* @param conn the SSLv3_Interpreter to which this
|
||||
* SSLv3_ApplicationRecord should be deliverd
|
||||
*/
|
||||
void Deliver(SSLv3_Interpreter* conn);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,83 +0,0 @@
|
|||
// $Id: SSLv3Automaton.cc 80 2004-07-14 20:15:50Z jason $
|
||||
|
||||
// ---SSLv3_Automaton----------------------------------------------------------
|
||||
|
||||
#include "SSLv3Automaton.h"
|
||||
|
||||
SSLv3_Automaton::SSLv3_Automaton(int arg_num_states, int num_trans,
|
||||
int error_state)
|
||||
{
|
||||
num_states = arg_num_states;
|
||||
states = new SSLv3_State*[num_states];
|
||||
for ( int i = 0; i < num_states; ++i )
|
||||
states[i] = new SSLv3_State(num_trans, error_state);
|
||||
}
|
||||
|
||||
SSLv3_Automaton::~SSLv3_Automaton()
|
||||
{
|
||||
for ( int i = 0; i < num_states; ++i )
|
||||
delete states[i];
|
||||
delete [] states;
|
||||
}
|
||||
|
||||
void SSLv3_Automaton::Describe(ODesc* d) const
|
||||
{
|
||||
d->Add("sslAutomaton");
|
||||
}
|
||||
|
||||
void SSLv3_Automaton::setStartState(int state)
|
||||
{
|
||||
if ( state < num_states )
|
||||
startState = state;
|
||||
}
|
||||
|
||||
void SSLv3_Automaton::addTrans(int state1, int trans, int state2)
|
||||
{
|
||||
if ( state1 < num_states && state2 < num_states )
|
||||
states[state1]->addTrans(trans, state2);
|
||||
}
|
||||
|
||||
int SSLv3_Automaton::getNextState(int state, int trans)
|
||||
{
|
||||
if ( state < num_states )
|
||||
return states[state]->getNextState(trans);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SSLv3_Automaton::getStartState()
|
||||
{
|
||||
if (startState >= 0)
|
||||
return startState;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ---SSLv3_State--------------------------------------------------------------
|
||||
|
||||
SSLv3_State::SSLv3_State(int num_trans, int error_state)
|
||||
{
|
||||
this->num_trans = num_trans;
|
||||
transitions = new int[num_trans];
|
||||
for ( int i = 0; i < num_trans; ++i )
|
||||
transitions[i] = error_state;
|
||||
}
|
||||
|
||||
SSLv3_State::~SSLv3_State()
|
||||
{
|
||||
delete [] transitions;
|
||||
}
|
||||
|
||||
void SSLv3_State::addTrans(int trans, int state)
|
||||
{
|
||||
if ( trans < num_trans )
|
||||
transitions[trans] = state;
|
||||
}
|
||||
|
||||
int SSLv3_State::getNextState(int trans)
|
||||
{
|
||||
if ( trans < num_trans )
|
||||
return transitions[trans];
|
||||
else
|
||||
return 0;
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
// $Id: SSLv3Automaton.h 80 2004-07-14 20:15:50Z jason $
|
||||
|
||||
#ifndef ssl_v3_automaton_h
|
||||
#define ssl_v3_automaton_h
|
||||
|
||||
#include "Obj.h"
|
||||
#include "SSLDefines.h"
|
||||
|
||||
class SSLv3_State;
|
||||
|
||||
/** Class SSLv3_Automaton is there for holding the transitions of a state machine.
|
||||
* The States are simply Integer Constants >= 0. Same for the transitions.
|
||||
* The SSLv3_Automaton holds a pointer to an array of pointers to the
|
||||
* states of the automaton. The array is indexed by the integer that
|
||||
* represents the corresponding state.
|
||||
* By default, the automaton is initialized with every transition leading to
|
||||
* the error_state.
|
||||
* By calling addTrans() (done in the SSLv3_Interpreter's BuildAutomaton()-method)
|
||||
* the proper transitions for the SSL automaton are created.
|
||||
* When calling getNextState(state, trans), you get the next state of the
|
||||
* automaton, according to state and trans.
|
||||
* */
|
||||
class SSLv3_Automaton : public BroObj {
|
||||
public:
|
||||
/* The constructor initialises the states 2-dim. array
|
||||
* (which's size depends on num_states and num_trans).
|
||||
* By default, every transition from every state leads to the error_state.
|
||||
* @param num_states how many states the automaton has
|
||||
* @param num_trans how many different transitions the automaton has
|
||||
* @param error_state which Integer the error_state has
|
||||
*/
|
||||
SSLv3_Automaton(int num_states, int num_trans, int error_state);
|
||||
~SSLv3_Automaton();
|
||||
void Describe(ODesc* d) const;
|
||||
|
||||
/* Sets the start state of the automaton.
|
||||
* @param state the start state
|
||||
*/
|
||||
void setStartState(int state);
|
||||
|
||||
/* This method is used for building up the automaton and defining
|
||||
* from which state you get to which state which what transition.
|
||||
* @param state1 the state from which the transition starts
|
||||
* @param trans the transition itself
|
||||
* @param to which state the transition leads
|
||||
*/
|
||||
void addTrans(int state1, int trans, int state2);
|
||||
|
||||
/* Used for determinig into which state the automaton gets by using the
|
||||
* given transition in the given state.
|
||||
* @param state the state from which the transition starts
|
||||
* @param trans the transition itself
|
||||
* @return the state to which the transition leads
|
||||
*/
|
||||
int getNextState(int state, int trans);
|
||||
int getStartState();
|
||||
int OutRef()
|
||||
{
|
||||
return RefCnt();
|
||||
}
|
||||
|
||||
protected:
|
||||
int num_states; ///< how many states the automaton has
|
||||
SSLv3_State** states; ///< the pointer to the array of pointers that holds the states
|
||||
int startState; ///< the start state of the automaton
|
||||
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/** This class represents a state of the SSLv3_Automaton.
|
||||
* It holds a pointer to an array of integers, which corresponds to the
|
||||
* succeeding states of this state when "taking" a transition.
|
||||
* The transition array is indexed by the integer-values corresponding to
|
||||
* the transitions of the automaton.
|
||||
* */
|
||||
class SSLv3_State {
|
||||
public:
|
||||
/* The constructor initialises the state. By default, every transition
|
||||
* of the automaton leads to the error_state.
|
||||
* @param num_trans how many different transitions the automaton has
|
||||
* @param error_state how many different transitions the automaton has
|
||||
*/
|
||||
SSLv3_State(int num_trans, int error_state);
|
||||
~SSLv3_State();
|
||||
|
||||
/* This method is used for building up the automaton and is invoked by
|
||||
* the SSLv3_Automaton's addTrans()-method. It defines the successing state
|
||||
* of the automaton by taking the transition trans in this state.
|
||||
* @param trans the transition,
|
||||
* @param that leads to the state
|
||||
*/
|
||||
void addTrans(int trans, int state);
|
||||
|
||||
/* Used for determinig into which state the automaton gets by using the
|
||||
* given transition in the this state.
|
||||
* @param trans which transition is to be taken
|
||||
* @return the resulting state of the automaton
|
||||
*/
|
||||
int getNextState(int trans);
|
||||
|
||||
protected:
|
||||
int num_trans; ///< how many transitions the automaton has
|
||||
int* transitions; ///< the array of successing states of this state by taking the transition that indexes this array
|
||||
};
|
||||
|
||||
#endif
|
|
@ -21,6 +21,7 @@ void SerializationFormat::StartRead(char* data, uint32 arg_len)
|
|||
input = data;
|
||||
input_len = arg_len;
|
||||
input_pos = 0;
|
||||
bytes_read = 0;
|
||||
}
|
||||
|
||||
void SerializationFormat::EndRead()
|
||||
|
@ -44,7 +45,6 @@ void SerializationFormat::StartWrite()
|
|||
|
||||
output_pos = 0;
|
||||
bytes_written = 0;
|
||||
bytes_read = 0;
|
||||
}
|
||||
|
||||
uint32 SerializationFormat::EndWrite(char** data)
|
||||
|
|
90
src/Syslog-binpac.cc
Normal file
90
src/Syslog-binpac.cc
Normal file
|
@ -0,0 +1,90 @@
|
|||
#include "Syslog-binpac.h"
|
||||
#include "TCP_Reassembler.h"
|
||||
|
||||
Syslog_Analyzer_binpac::Syslog_Analyzer_binpac(Connection* conn)
|
||||
: Analyzer(AnalyzerTag::SYSLOG_BINPAC, conn)
|
||||
{
|
||||
interp = new binpac::Syslog::Syslog_Conn(this);
|
||||
did_session_done = 0;
|
||||
//ADD_ANALYZER_TIMER(&Syslog_Analyzer_binpac::ExpireTimer,
|
||||
// network_time + Syslog_session_timeout, 1, TIMER_Syslog_EXPIRE);
|
||||
}
|
||||
|
||||
Syslog_Analyzer_binpac::~Syslog_Analyzer_binpac()
|
||||
{
|
||||
delete interp;
|
||||
}
|
||||
|
||||
void Syslog_Analyzer_binpac::Done()
|
||||
{
|
||||
Analyzer::Done();
|
||||
|
||||
if ( ! did_session_done )
|
||||
Event(udp_session_done);
|
||||
}
|
||||
|
||||
void Syslog_Analyzer_binpac::DeliverPacket(int len, const u_char* data, bool orig, int seq, const IP_Hdr* ip, int caplen)
|
||||
{
|
||||
Analyzer::DeliverPacket(len, data, orig, seq, ip, caplen);
|
||||
interp->NewData(orig, data, data + len);
|
||||
}
|
||||
|
||||
//void Syslog_Analyzer_binpac::ExpireTimer(double t)
|
||||
// {
|
||||
// // The - 1.0 in the following is to allow 1 second for the
|
||||
// // common case of a single request followed by a single reply,
|
||||
// // so we don't needlessly set the timer twice in that case.
|
||||
// if ( t - Conn()->LastTime() >= Syslog_session_timeout - 1.0 || terminating )
|
||||
// {
|
||||
// Event(connection_timeout);
|
||||
// sessions->Remove(Conn());
|
||||
// }
|
||||
// else
|
||||
// ADD_ANALYZER_TIMER(&Syslog_Analyzer_binpac::ExpireTimer,
|
||||
// t + Syslog_session_timeout, 1, TIMER_Syslog_EXPIRE);
|
||||
// }
|
||||
|
||||
//Syslog_TCP_Analyzer_binpac::Syslog_TCP_Analyzer_binpac(Connection* conn)
|
||||
//: TCP_ApplicationAnalyzer(AnalyzerTag::Syslog_TCP_BINPAC, conn)
|
||||
// {
|
||||
// interp = new binpac::Syslog_on_TCP::Syslog_TCP_Conn(this);
|
||||
// }
|
||||
|
||||
//Syslog_TCP_Analyzer_binpac::~Syslog_TCP_Analyzer_binpac()
|
||||
// {
|
||||
// delete interp;
|
||||
// }
|
||||
|
||||
//void Syslog_TCP_Analyzer_binpac::Done()
|
||||
// {
|
||||
// TCP_ApplicationAnalyzer::Done();
|
||||
//
|
||||
// interp->FlowEOF(true);
|
||||
// interp->FlowEOF(false);
|
||||
// }
|
||||
|
||||
//void Syslog_TCP_Analyzer_binpac::EndpointEOF(TCP_Reassembler* endp)
|
||||
// {
|
||||
// TCP_ApplicationAnalyzer::EndpointEOF(endp);
|
||||
// interp->FlowEOF(endp->IsOrig());
|
||||
// }
|
||||
|
||||
//void Syslog_TCP_Analyzer_binpac::DeliverStream(int len, const u_char* data,
|
||||
// bool orig)
|
||||
// {
|
||||
// TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
|
||||
//
|
||||
// assert(TCP());
|
||||
//
|
||||
// if ( TCP()->IsPartial() || TCP()->HadGap(orig) )
|
||||
// // punt-on-partial or stop-on-gap.
|
||||
// return;
|
||||
//
|
||||
// interp->NewData(orig, data, data + len);
|
||||
// }
|
||||
|
||||
//void Syslog_TCP_Analyzer_binpac::Undelivered(int seq, int len, bool orig)
|
||||
// {
|
||||
// TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
|
||||
// interp->NewGap(orig, len);
|
||||
// }
|
55
src/Syslog-binpac.h
Normal file
55
src/Syslog-binpac.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#ifndef Syslog_binpac_h
|
||||
#define Syslog_binpac_h
|
||||
|
||||
#include "UDP.h"
|
||||
#include "TCP.h"
|
||||
|
||||
#include "syslog_pac.h"
|
||||
|
||||
class Syslog_Analyzer_binpac : public Analyzer {
|
||||
public:
|
||||
Syslog_Analyzer_binpac(Connection* conn);
|
||||
virtual ~Syslog_Analyzer_binpac();
|
||||
|
||||
virtual void Done();
|
||||
virtual void DeliverPacket(int len, const u_char* data, bool orig,
|
||||
int seq, const IP_Hdr* ip, int caplen);
|
||||
|
||||
static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
{ return new Syslog_Analyzer_binpac(conn); }
|
||||
|
||||
static bool Available()
|
||||
{ return syslog_message; }
|
||||
|
||||
protected:
|
||||
friend class AnalyzerTimer;
|
||||
void ExpireTimer(double t);
|
||||
|
||||
int did_session_done;
|
||||
|
||||
binpac::Syslog::Syslog_Conn* interp;
|
||||
};
|
||||
|
||||
// #include "Syslog_tcp_pac.h"
|
||||
//
|
||||
//class Syslog_TCP_Analyzer_binpac : public TCP_ApplicationAnalyzer {
|
||||
//public:
|
||||
// Syslog_TCP_Analyzer_binpac(Connection* conn);
|
||||
// virtual ~Syslog_TCP_Analyzer_binpac();
|
||||
//
|
||||
// virtual void Done();
|
||||
// virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||
// virtual void Undelivered(int seq, int len, bool orig);
|
||||
// virtual void EndpointEOF(TCP_Reassembler* endp);
|
||||
//
|
||||
// static Analyzer* InstantiateAnalyzer(Connection* conn)
|
||||
// { return new Syslog_TCP_Analyzer_binpac(conn); }
|
||||
//
|
||||
// static bool Available()
|
||||
// { return (Syslog_request || Syslog_full_request) && FLAGS_use_binpac; }
|
||||
//
|
||||
//protected:
|
||||
// binpac::Syslog_on_TCP::Syslog_TCP_Conn* interp;
|
||||
//};
|
||||
//
|
||||
#endif
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "NetVar.h"
|
||||
#include "PIA.h"
|
||||
#include "File.h"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// $Id: TCP_Reassembler.cc,v 1.1.2.8 2006/05/31 01:52:02 sommer Exp $
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Analyzer.h"
|
||||
#include "TCP_Reassembler.h"
|
||||
#include "TCP.h"
|
||||
|
@ -220,11 +222,9 @@ void TCP_Reassembler::Undelivered(int up_to_seq)
|
|||
// handshakes, but Oh Well.
|
||||
|
||||
if ( content_gap &&
|
||||
(BifConst::report_gaps_for_partial ||
|
||||
(endpoint->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
peer->state == TCP_ENDPOINT_ESTABLISHED )
|
||||
)
|
||||
)
|
||||
(BifConst::report_gaps_for_partial ||
|
||||
(endpoint->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
peer->state == TCP_ENDPOINT_ESTABLISHED ) ) )
|
||||
{
|
||||
val_list* vl = new val_list;
|
||||
vl->append(dst_analyzer->BuildConnVal());
|
||||
|
@ -492,11 +492,9 @@ void TCP_Reassembler::AckReceived(int seq)
|
|||
return;
|
||||
|
||||
bool test_active = ! skip_deliveries && ! tcp_analyzer->Skipping() &&
|
||||
( BifConst::report_gaps_for_partial ||
|
||||
(endp->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
endp->peer->state == TCP_ENDPOINT_ESTABLISHED
|
||||
)
|
||||
);
|
||||
( BifConst::report_gaps_for_partial ||
|
||||
(endp->state == TCP_ENDPOINT_ESTABLISHED &&
|
||||
endp->peer->state == TCP_ENDPOINT_ESTABLISHED ) );
|
||||
|
||||
int num_missing = TrimToSeq(seq);
|
||||
|
||||
|
|
|
@ -848,8 +848,8 @@ void TypeDecl::DescribeReST(ODesc* d) const
|
|||
}
|
||||
|
||||
CommentedTypeDecl::CommentedTypeDecl(BroType* t, const char* i,
|
||||
attr_list* attrs, std::list<std::string>* cmnt_list)
|
||||
: TypeDecl(t, i, attrs)
|
||||
attr_list* attrs, bool in_record, std::list<std::string>* cmnt_list)
|
||||
: TypeDecl(t, i, attrs, in_record)
|
||||
{
|
||||
comments = cmnt_list;
|
||||
}
|
||||
|
@ -1157,6 +1157,7 @@ void RecordType::DescribeFieldsReST(ODesc* d, bool func_args) const
|
|||
for ( int i = 0; i < num_fields; ++i )
|
||||
{
|
||||
if ( i > 0 )
|
||||
{
|
||||
if ( func_args )
|
||||
d->Add(", ");
|
||||
else
|
||||
|
@ -1164,6 +1165,7 @@ void RecordType::DescribeFieldsReST(ODesc* d, bool func_args) const
|
|||
d->NL();
|
||||
d->NL();
|
||||
}
|
||||
}
|
||||
|
||||
FieldDecl(i)->DescribeReST(d);
|
||||
}
|
||||
|
|
|
@ -420,7 +420,7 @@ public:
|
|||
class CommentedTypeDecl : public TypeDecl {
|
||||
public:
|
||||
CommentedTypeDecl(BroType* t, const char* i, attr_list* attrs = 0,
|
||||
std::list<std::string>* cmnt_list = 0);
|
||||
bool in_record = false, std::list<std::string>* cmnt_list = 0);
|
||||
virtual ~CommentedTypeDecl();
|
||||
|
||||
void DescribeReST(ODesc* d) const;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "Net.h"
|
||||
|
|
|
@ -2866,7 +2866,7 @@ RecordVal::RecordVal(RecordType* t) : MutableVal(t)
|
|||
else if ( tag == TYPE_TABLE )
|
||||
def = new TableVal(type->AsTableType(), a);
|
||||
|
||||
else if ( t->Tag() == TYPE_VECTOR )
|
||||
else if ( tag == TYPE_VECTOR )
|
||||
def = new VectorVal(type->AsVectorType());
|
||||
}
|
||||
|
||||
|
|
29
src/Var.cc
29
src/Var.cc
|
@ -242,15 +242,26 @@ void add_type(ID* id, BroType* t, attr_list* attr, int /* is_event */)
|
|||
// t->GetTypeID() is true.
|
||||
if ( generate_documentation )
|
||||
{
|
||||
if ( t->Tag() == TYPE_RECORD )
|
||||
{
|
||||
// Only "shallow" copy record types because we want to be able
|
||||
// to see additions to the original type's list of fields
|
||||
switch ( t->Tag() ) {
|
||||
// Only "shallow" copy types that may contain records because
|
||||
// we want to be able to see additions to the original record type's
|
||||
// list of fields
|
||||
case TYPE_RECORD:
|
||||
tnew = new RecordType(t->AsRecordType()->Types());
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
break;
|
||||
case TYPE_TABLE:
|
||||
tnew = new TableType(t->AsTableType()->Indices(),
|
||||
t->AsTableType()->YieldType());
|
||||
break;
|
||||
case TYPE_VECTOR:
|
||||
tnew = new VectorType(t->AsVectorType()->YieldType());
|
||||
break;
|
||||
case TYPE_FUNC:
|
||||
tnew = new FuncType(t->AsFuncType()->Args(),
|
||||
t->AsFuncType()->YieldType(),
|
||||
t->AsFuncType()->IsEvent());
|
||||
break;
|
||||
default:
|
||||
SerializationFormat* form = new BinarySerializationFormat();
|
||||
form->StartWrite();
|
||||
CloneSerializer ss(form);
|
||||
|
@ -267,7 +278,7 @@ void add_type(ID* id, BroType* t, attr_list* attr, int /* is_event */)
|
|||
tnew = t->Unserialize(&uinfo);
|
||||
|
||||
delete [] data;
|
||||
}
|
||||
}
|
||||
|
||||
tnew->SetTypeID(copy_string(id->Name()));
|
||||
}
|
||||
|
|
265
src/X509.cc
265
src/X509.cc
|
@ -1,265 +0,0 @@
|
|||
// $Id: X509.cc 6724 2009-06-07 09:23:03Z vern $
|
||||
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "X509.h"
|
||||
#include "config.h"
|
||||
|
||||
// ### NOTE: while d2i_X509 does not take a const u_char** pointer,
|
||||
// here we assume d2i_X509 does not write to <data>, so it is safe to
|
||||
// convert data to a non-const pointer. Could some X509 guru verify
|
||||
// this?
|
||||
|
||||
X509* d2i_X509_(X509** px, const u_char** in, int len)
|
||||
{
|
||||
#ifdef OPENSSL_D2I_X509_USES_CONST_CHAR
|
||||
return d2i_X509(px, in, len);
|
||||
#else
|
||||
return d2i_X509(px, (u_char**)in, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
X509_STORE* X509_Cert::ctx = 0;
|
||||
X509_LOOKUP* X509_Cert::lookup = 0;
|
||||
X509_STORE_CTX X509_Cert::csc;
|
||||
bool X509_Cert::bInited = false;
|
||||
|
||||
// TODO: Check if Key < 768 Bits => Weakness!
|
||||
// FIXME: Merge verify and verifyChain.
|
||||
|
||||
void X509_Cert::sslCertificateEvent(Contents_SSL* e, X509* pCert)
|
||||
{
|
||||
EventHandlerPtr event = ssl_certificate;
|
||||
if ( ! event )
|
||||
return;
|
||||
|
||||
char tmp[256];
|
||||
RecordVal* pX509Cert = new RecordVal(x509_type);
|
||||
|
||||
X509_NAME_oneline(X509_get_issuer_name(pCert), tmp, sizeof tmp);
|
||||
pX509Cert->Assign(0, new StringVal(tmp));
|
||||
X509_NAME_oneline(X509_get_subject_name(pCert), tmp, sizeof tmp);
|
||||
pX509Cert->Assign(1, new StringVal(tmp));
|
||||
pX509Cert->Assign(2, new AddrVal(e->Conn()->OrigAddr()));
|
||||
|
||||
val_list* vl = new val_list;
|
||||
vl->append(e->BuildConnVal());
|
||||
vl->append(pX509Cert);
|
||||
vl->append(new Val(e->IsOrig(), TYPE_BOOL));
|
||||
|
||||
e->Conn()->ConnectionEvent(event, e, vl);
|
||||
}
|
||||
|
||||
void X509_Cert::sslCertificateError(Contents_SSL* e, int error_numbe)
|
||||
{
|
||||
Val* err_str = new StringVal(X509_verify_cert_error_string(csc.error));
|
||||
val_list* vl = new val_list;
|
||||
|
||||
vl->append(e->BuildConnVal());
|
||||
vl->append(new Val(csc.error, TYPE_INT));
|
||||
vl->append(err_str);
|
||||
|
||||
e->Conn()->ConnectionEvent(ssl_X509_error, e, vl);
|
||||
}
|
||||
|
||||
int X509_Cert::init()
|
||||
{
|
||||
#if 0
|
||||
OpenSSL_add_all_algorithms();
|
||||
#endif
|
||||
|
||||
ctx = X509_STORE_new();
|
||||
int flag = 0;
|
||||
int ret = 0;
|
||||
|
||||
if ( x509_trusted_cert_path &&
|
||||
x509_trusted_cert_path->AsString()->Len() > 0 )
|
||||
{ // add the path(s) for the local CA's certificates
|
||||
const BroString* pString = x509_trusted_cert_path->AsString();
|
||||
|
||||
lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_hash_dir());
|
||||
if ( ! lookup )
|
||||
{
|
||||
fprintf(stderr, "X509_Cert::init(): initing lookup failed\n");
|
||||
flag = 1;
|
||||
}
|
||||
|
||||
int i = X509_LOOKUP_add_dir(lookup,
|
||||
(const char*) pString->Bytes(),
|
||||
X509_FILETYPE_PEM);
|
||||
if ( ! i )
|
||||
{
|
||||
fprintf( stderr, "X509_Cert::init(): error adding lookup directory\n" );
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("X509: Using the default trusted cert path.\n");
|
||||
X509_STORE_set_default_paths(ctx);
|
||||
}
|
||||
|
||||
// Add crl functionality - will only add if defined and
|
||||
// X509_STORE_add_lookup was successful.
|
||||
if ( ! flag && x509_crl_file && x509_crl_file->AsString()->Len() > 0 )
|
||||
{
|
||||
const BroString* rString = x509_crl_file->AsString();
|
||||
|
||||
if ( X509_load_crl_file(lookup, (const char*) rString->Bytes(),
|
||||
X509_FILETYPE_PEM) != 1 )
|
||||
{
|
||||
fprintf(stderr, "X509_Cert::init(): error reading CRL file\n");
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Note, openssl version must be > 0.9.7(a).
|
||||
X509_STORE_set_flags(ctx,
|
||||
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
|
||||
#endif
|
||||
}
|
||||
|
||||
bInited = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int X509_Cert::verify(Contents_SSL* e, const u_char* data, uint32 len)
|
||||
{
|
||||
if ( ! bInited )
|
||||
init();
|
||||
|
||||
X509* pCert = d2i_X509_(NULL, &data, len);
|
||||
if ( ! pCert )
|
||||
{
|
||||
// 5 = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
|
||||
sslCertificateError(e, 5);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sslCertificateEvent(e, pCert);
|
||||
|
||||
X509_STORE_CTX_init(&csc, ctx, pCert, 0);
|
||||
X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time);
|
||||
int i = X509_verify_cert(&csc);
|
||||
X509_STORE_CTX_cleanup(&csc);
|
||||
int ret = 0;
|
||||
|
||||
int ext = X509_get_ext_count(pCert);
|
||||
|
||||
if ( ext > 0 )
|
||||
{
|
||||
TableVal* x509ex = new TableVal(x509_extension);
|
||||
val_list* vl = new val_list;
|
||||
char buf[256];
|
||||
|
||||
for ( int k = 0; k < ext; ++k )
|
||||
{
|
||||
X509_EXTENSION* ex = X509_get_ext(pCert, k);
|
||||
ASN1_OBJECT* obj = X509_EXTENSION_get_object(ex);
|
||||
i2t_ASN1_OBJECT(buf, sizeof(buf), obj);
|
||||
|
||||
Val* index = new Val(k+1, TYPE_COUNT);
|
||||
Val* value = new StringVal(strlen(buf), buf);
|
||||
x509ex->Assign(index, value);
|
||||
Unref(index);
|
||||
// later we can do critical extensions like:
|
||||
// X509_EXTENSION_get_critical(ex);
|
||||
}
|
||||
|
||||
vl->append(e->BuildConnVal());
|
||||
vl->append(x509ex);
|
||||
e->Conn()->ConnectionEvent(process_X509_extensions, e, vl);
|
||||
}
|
||||
|
||||
if ( ! i )
|
||||
{
|
||||
sslCertificateError(e, csc.error);
|
||||
ret = csc.error;
|
||||
}
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
delete pCert;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int X509_Cert::verifyChain(Contents_SSL* e, const u_char* data, uint32 len)
|
||||
{
|
||||
if ( ! bInited )
|
||||
init();
|
||||
|
||||
// Gets an ssl3x cert chain (could be one single cert, too,
|
||||
// but in chain format).
|
||||
|
||||
// Init the stack.
|
||||
STACK_OF(X509)* untrustedCerts = sk_X509_new_null();
|
||||
if ( ! untrustedCerts )
|
||||
{
|
||||
// Internal error allocating stack of untrusted certs.
|
||||
// 11 = X509_V_ERR_OUT_OF_MEM
|
||||
sslCertificateError(e, 11);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOT AGAIN!!!
|
||||
// Extract certificates and put them into an OpenSSL Stack.
|
||||
uint tempLength = 0;
|
||||
int certCount = 0;
|
||||
X509* pCert = 0; // base cert, this one is to be verified
|
||||
|
||||
while ( tempLength < len )
|
||||
{
|
||||
++certCount;
|
||||
uint32 certLength =
|
||||
uint32((data[tempLength + 0] << 16) |
|
||||
data[tempLength + 1] << 8) |
|
||||
data[tempLength + 2];
|
||||
|
||||
// Points to current cert.
|
||||
const u_char* pCurrentCert = &data[tempLength+3];
|
||||
|
||||
X509* pTemp = d2i_X509_(0, &pCurrentCert, certLength);
|
||||
if ( ! pTemp )
|
||||
{ // error is somewhat of a misnomer
|
||||
// 5 = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
|
||||
sslCertificateError(e, 5);
|
||||
//FIXME: free ptrs
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( certCount == 1 )
|
||||
// The first certificate goes directly into the ctx.
|
||||
pCert = pTemp;
|
||||
else
|
||||
// The remaining certificates (if any) are put into
|
||||
// the list of untrusted certificates
|
||||
sk_X509_push(untrustedCerts, pTemp);
|
||||
|
||||
tempLength += certLength + 3;
|
||||
}
|
||||
|
||||
sslCertificateEvent(e, pCert);
|
||||
|
||||
X509_STORE_CTX_init(&csc, ctx, pCert, untrustedCerts);
|
||||
X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time);
|
||||
int i = X509_verify_cert(&csc);
|
||||
X509_STORE_CTX_cleanup(&csc);
|
||||
//X509_STORE_CTX_free(&csc);
|
||||
int ret = 0;
|
||||
|
||||
if ( ! i )
|
||||
{
|
||||
sslCertificateError(e, csc.error);
|
||||
ret = csc.error;
|
||||
}
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
delete pCert;
|
||||
// Free the stack, incuding. contents.
|
||||
|
||||
// FIXME: could this break Bro's memory tracking?
|
||||
sk_X509_pop_free(untrustedCerts, X509_free);
|
||||
|
||||
return ret;
|
||||
}
|
33
src/X509.h
33
src/X509.h
|
@ -1,33 +0,0 @@
|
|||
// $Id: X509.h 3526 2006-09-12 07:32:21Z vern $
|
||||
|
||||
#ifndef X509_H
|
||||
#define X509_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
|
||||
#include "SSLProxy.h"
|
||||
|
||||
class X509_Cert {
|
||||
public:
|
||||
static X509_STORE* ctx;
|
||||
static X509_LOOKUP* lookup;
|
||||
static X509_STORE_CTX csc;
|
||||
static bool bInited;
|
||||
|
||||
// Initializes the OpenSSL library, which is used for verify().
|
||||
static int init();
|
||||
|
||||
// Wrapper for X.509 error event.
|
||||
static void sslCertificateError(Contents_SSL* e, int error_numbe);
|
||||
|
||||
// Retrieves a DER-encoded X.509 certificate. Returns 0 on failure.
|
||||
static int verify(Contents_SSL* e, const u_char* data, uint32 len);
|
||||
static int verifyChain(Contents_SSL* e, const u_char* data, uint32 len);
|
||||
|
||||
// Wrapper for the ssl_certificate event.
|
||||
static void sslCertificateEvent(Contents_SSL* e, X509* pCert);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "XDR.h"
|
||||
|
@ -33,7 +35,7 @@ uint64 extract_XDR_uint64(const u_char*& buf, int& len)
|
|||
if ( ! buf || len < 8 )
|
||||
{
|
||||
buf = 0;
|
||||
return 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64 uhi = extract_XDR_uint32(buf, len);
|
||||
|
@ -64,6 +66,9 @@ const u_char* extract_XDR_opaque(const u_char*& buf, int& len, int& n, int max_l
|
|||
if (short_buf_ok)
|
||||
n = std::min(n, len);
|
||||
|
||||
if ( short_buf_ok )
|
||||
n = std::min(n, len);
|
||||
|
||||
if ( n < 0 || n > len || n > max_len )
|
||||
{ // ### Should really flag this as a different sort of error.
|
||||
buf = 0;
|
||||
|
@ -79,7 +84,6 @@ const u_char* extract_XDR_opaque(const u_char*& buf, int& len, int& n, int max_l
|
|||
return opaque;
|
||||
}
|
||||
|
||||
|
||||
const u_char* extract_XDR_opaque_fixed(const u_char*& buf, int& len, int n)
|
||||
{
|
||||
if ( ! buf )
|
||||
|
|
162
src/bro.bif
162
src/bro.bif
|
@ -486,15 +486,15 @@ function to_count%(str: string%): count
|
|||
%{
|
||||
const char* s = str->CheckString();
|
||||
char* end_s;
|
||||
|
||||
|
||||
uint64 u = (uint64) strtoll(s, &end_s, 10);
|
||||
|
||||
|
||||
if ( s[0] == '\0' || end_s[0] != '\0' )
|
||||
{
|
||||
builtin_run_time("bad conversion to count", @ARG@[0]);
|
||||
u = 0;
|
||||
}
|
||||
|
||||
|
||||
return new Val(u, TYPE_COUNT);
|
||||
%}
|
||||
|
||||
|
@ -556,7 +556,7 @@ function to_addr%(ip: string%): addr
|
|||
delete [] s;
|
||||
return ret;
|
||||
%}
|
||||
|
||||
|
||||
function count_to_v4_addr%(ip: count%): addr
|
||||
%{
|
||||
if ( ip > 4294967295 )
|
||||
|
@ -564,7 +564,7 @@ function count_to_v4_addr%(ip: count%): addr
|
|||
builtin_run_time("conversion of non-IPv4 count to addr", @ARG@[0]);
|
||||
return new AddrVal(uint32(0));
|
||||
}
|
||||
|
||||
|
||||
return new AddrVal(htonl(uint32(ip)));
|
||||
%}
|
||||
|
||||
|
@ -1348,7 +1348,7 @@ function fmt_ftp_port%(a: addr, p: port%): string
|
|||
function decode_netbios_name%(name: string%): string
|
||||
%{
|
||||
char buf[16];
|
||||
char result[32];
|
||||
char result[16];
|
||||
const u_char* s = name->Bytes();
|
||||
int i, j;
|
||||
|
||||
|
@ -1370,7 +1370,7 @@ function decode_netbios_name%(name: string%): string
|
|||
break;
|
||||
}
|
||||
|
||||
return new StringVal(result);
|
||||
return new StringVal(i, result);
|
||||
%}
|
||||
|
||||
function decode_netbios_name_type%(name: string%): count
|
||||
|
@ -3106,7 +3106,13 @@ function lookup_location%(a: addr%) : geo_location
|
|||
}
|
||||
|
||||
#else
|
||||
builtin_run_time("Bro was not configured for GeoIP support");
|
||||
static int missing_geoip_reported = 0;
|
||||
|
||||
if ( ! missing_geoip_reported )
|
||||
{
|
||||
builtin_run_time("Bro was not configured for GeoIP support");
|
||||
missing_geoip_reported = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We can get here even if we have GeoIP support if we weren't
|
||||
|
@ -3164,7 +3170,13 @@ function lookup_asn%(a: addr%) : count
|
|||
return new Val(atoi(gir+2), TYPE_COUNT);
|
||||
}
|
||||
#else
|
||||
builtin_run_time("Bro was not configured for GeoIP ASN support");
|
||||
static int missing_geoip_reported = 0;
|
||||
|
||||
if ( ! missing_geoip_reported )
|
||||
{
|
||||
builtin_run_time("Bro was not configured for GeoIP ASN support");
|
||||
missing_geoip_reported = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We can get here even if we have GeoIP support, if we weren't
|
||||
|
@ -3328,21 +3340,146 @@ function entropy_test_finish%(index: any%): entropy_test_result
|
|||
return ent_result;
|
||||
%}
|
||||
|
||||
function bro_has_ipv6%(%) : bool
|
||||
%{
|
||||
#ifdef BROv6
|
||||
return new Val(1, TYPE_BOOL);
|
||||
#else
|
||||
return new Val(0, TYPE_BOOL);
|
||||
#endif
|
||||
%}
|
||||
|
||||
function unique_id%(prefix: string%) : string
|
||||
%{
|
||||
char tmp[20];
|
||||
uint64 uid = calculate_unique_id();
|
||||
return new StringVal(uitoa_n(uid, tmp, sizeof(tmp), 62, prefix->CheckString()));
|
||||
%}
|
||||
|
||||
%%{
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
|
||||
// This is the indexed map of X509 certificate stores.
|
||||
static map<BroString, X509_STORE*> x509_stores;
|
||||
|
||||
// ### NOTE: while d2i_X509 does not take a const u_char** pointer,
|
||||
// here we assume d2i_X509 does not write to <data>, so it is safe to
|
||||
// convert data to a non-const pointer. Could some X509 guru verify
|
||||
// this?
|
||||
|
||||
X509* d2i_X509_(X509** px, const u_char** in, int len)
|
||||
{
|
||||
#ifdef OPENSSL_D2I_X509_USES_CONST_CHAR
|
||||
return d2i_X509(px, in, len);
|
||||
#else
|
||||
return d2i_X509(px, (u_char**)in, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
%%}
|
||||
|
||||
|
||||
function x509_verify%(der_cert: string, cert_stack: string_vec, root_certs: table_string_of_string%): count
|
||||
%{
|
||||
X509_STORE* ctx = 0;
|
||||
int i = 0;
|
||||
|
||||
// If this certificate store was built previously, just reuse the old one.
|
||||
BroString* s = convert_index_to_string(root_certs);
|
||||
if ( x509_stores.count(*s) > 0 )
|
||||
ctx = x509_stores[*s];
|
||||
|
||||
if ( ! ctx ) // lookup to see if we have this one built already!
|
||||
{
|
||||
ctx = X509_STORE_new();
|
||||
TableVal* root_certs2 = root_certs->AsTableVal();
|
||||
ListVal* idxs = root_certs2->ConvertToPureList();
|
||||
|
||||
// Build the validation store
|
||||
for ( i = 0; i < idxs->Length(); ++i )
|
||||
{
|
||||
Val* key = idxs->Index(i);
|
||||
StringVal *sv = root_certs2->Lookup(key)->AsStringVal();
|
||||
const uint8* data = sv->Bytes();
|
||||
X509* x = d2i_X509_(NULL, &data, sv->Len());
|
||||
if ( ! x )
|
||||
{
|
||||
builtin_run_time(fmt("Root CA error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
|
||||
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
|
||||
}
|
||||
X509_STORE_add_cert(ctx, x);
|
||||
}
|
||||
|
||||
// Save the newly constructed certificate store into the cacheing map.
|
||||
x509_stores[*s] = ctx;
|
||||
}
|
||||
|
||||
STACK_OF(X509)* untrusted_certs = sk_X509_new_null();
|
||||
if ( ! untrusted_certs )
|
||||
{
|
||||
builtin_run_time(fmt("Untrusted certificate stack initialization error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
|
||||
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
|
||||
}
|
||||
|
||||
VectorVal *cert_stack_vec = cert_stack->AsVectorVal();
|
||||
for ( i = 0; i < (int) cert_stack_vec->Size(); ++i )
|
||||
{
|
||||
StringVal *sv = cert_stack_vec->Lookup(i)->AsStringVal();
|
||||
const uint8 *data = sv->Bytes();
|
||||
X509* x = d2i_X509_(NULL, &data, sv->Len());
|
||||
if ( ! x )
|
||||
{
|
||||
builtin_run_time(fmt("Untrusted certificate stack creation error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
|
||||
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
|
||||
}
|
||||
sk_X509_push(untrusted_certs, x);
|
||||
}
|
||||
|
||||
const uint8 *cert_data = der_cert->Bytes();
|
||||
|
||||
X509_STORE_CTX csc;
|
||||
X509* cert = d2i_X509_(NULL, &cert_data, der_cert->Len());
|
||||
if ( ! cert )
|
||||
{
|
||||
builtin_run_time(fmt("Certificate error: %s", ERR_error_string(ERR_peek_last_error(),NULL)));
|
||||
return new Val((uint64) ERR_get_error(), TYPE_COUNT);
|
||||
}
|
||||
|
||||
X509_STORE_CTX_init(&csc, ctx, cert, untrusted_certs);
|
||||
X509_STORE_CTX_set_time(&csc, 0, (time_t) network_time);
|
||||
|
||||
int result = X509_verify_cert(&csc);
|
||||
X509_STORE_CTX_cleanup(&csc);
|
||||
|
||||
if ( untrusted_certs )
|
||||
sk_X509_pop_free(untrusted_certs, X509_free);
|
||||
|
||||
return new Val((uint64) csc.error, TYPE_COUNT);
|
||||
%}
|
||||
|
||||
function x509_err2str%(err_num: count%): string
|
||||
%{
|
||||
return new StringVal(X509_verify_cert_error_string(err_num));
|
||||
%}
|
||||
|
||||
function NFS3::mode2string%(mode: count%): string
|
||||
%{
|
||||
char str[12];
|
||||
char *p = str;
|
||||
|
||||
|
||||
/* usr */
|
||||
if (mode & S_IRUSR)
|
||||
*p++ = 'r';
|
||||
else
|
||||
*p++ = '-';
|
||||
|
||||
if (mode & S_IWUSR)
|
||||
*p++ = 'w';
|
||||
else
|
||||
*p++ = '-';
|
||||
|
||||
switch (mode & (S_IXUSR | S_ISUID)) {
|
||||
case 0:
|
||||
*p++ = '-';
|
||||
|
@ -3357,6 +3494,7 @@ function NFS3::mode2string%(mode: count%): string
|
|||
*p++ = 's';
|
||||
break;
|
||||
}
|
||||
|
||||
/* group */
|
||||
if (mode & S_IRGRP)
|
||||
*p++ = 'r';
|
||||
|
@ -3366,6 +3504,7 @@ function NFS3::mode2string%(mode: count%): string
|
|||
*p++ = 'w';
|
||||
else
|
||||
*p++ = '-';
|
||||
|
||||
switch (mode & (S_IXGRP | S_ISGID)) {
|
||||
case 0:
|
||||
*p++ = '-';
|
||||
|
@ -3380,6 +3519,7 @@ function NFS3::mode2string%(mode: count%): string
|
|||
*p++ = 's';
|
||||
break;
|
||||
}
|
||||
|
||||
/* other */
|
||||
if (mode & S_IROTH)
|
||||
*p++ = 'r';
|
||||
|
@ -3389,6 +3529,7 @@ function NFS3::mode2string%(mode: count%): string
|
|||
*p++ = 'w';
|
||||
else
|
||||
*p++ = '-';
|
||||
|
||||
switch (mode & (S_IXOTH | S_ISVTX)) {
|
||||
case 0:
|
||||
*p++ = '-';
|
||||
|
@ -3403,6 +3544,7 @@ function NFS3::mode2string%(mode: count%): string
|
|||
*p++ = 't';
|
||||
break;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
return new StringVal(str);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# $Id: const.bif 3929 2007-01-14 00:37:59Z vern $
|
||||
|
||||
# Documentation and default values for these are located in policy/bro.init
|
||||
# Documentation and default values for these are located in policy/bro.init.
|
||||
|
||||
const ignore_keep_alive_rexmit: bool;
|
||||
const skip_http_data: bool;
|
||||
|
@ -8,7 +8,6 @@ const parse_udp_tunnels: bool;
|
|||
const use_conn_size_analyzer: bool;
|
||||
const report_gaps_for_partial: bool;
|
||||
|
||||
# NFS analyzer settings. See bro.init for documentation
|
||||
const NFS3::return_data: bool;
|
||||
const NFS3::return_data_max: count;
|
||||
const NFS3::return_data_first_only: bool;
|
||||
|
|
|
@ -52,6 +52,7 @@ event icmp_echo_request%(c: connection, icmp: icmp_conn, id: count, seq: count,
|
|||
event icmp_echo_reply%(c: connection, icmp: icmp_conn, id: count, seq: count, payload: string%);
|
||||
event icmp_unreachable%(c: connection, icmp: icmp_conn, code: count, context: icmp_context%);
|
||||
event icmp_time_exceeded%(c: connection, icmp: icmp_conn, code: count, context: icmp_context%);
|
||||
event icmp_redirect%(c: connection, icmp: icmp_conn, a: addr%);
|
||||
event net_stats_update%(t: time, ns: net_stats%);
|
||||
event conn_stats%(c: connection, os: endpoint_stats, rs: endpoint_stats%);
|
||||
event conn_weird%(name: string, c: connection%);
|
||||
|
@ -156,6 +157,13 @@ event mime_all_data%(c: connection, length: count, data: string%);
|
|||
event mime_event%(c: connection, event_type: string, detail: string%);
|
||||
event mime_content_hash%(c: connection, content_len: count, hash_value: string%);
|
||||
|
||||
# Generated for each RPC request / reply *pair* (if there is no reply, the event
|
||||
# will be generated on timeout).
|
||||
event rpc_dialogue%(c: connection, prog: count, ver: count, proc: count, status: rpc_status, start_time: time, call_len: count, reply_len: count%);
|
||||
# Generated for each (correctly formed) RPC_CALL message received.
|
||||
event rpc_call%(c: connection, xid: count, prog: count, ver: count, proc: count, call_len: count%);
|
||||
# Generated for each (correctly formed) RPC_REPLY message received.
|
||||
event rpc_reply%(c: connection, xid: count, status: rpc_status, reply_len: count%);
|
||||
|
||||
# Generated for each RPC request / reply *pair* (if there is no reply, the event
|
||||
# will be generated on timeout).
|
||||
|
@ -179,12 +187,12 @@ event pm_attempt_dump%(r: connection, status: rpc_status%);
|
|||
event pm_attempt_callit%(r: connection, status: rpc_status, call: pm_callit_request%);
|
||||
event pm_bad_port%(r: connection, bad_p: count%);
|
||||
|
||||
# Event for the NFS analyzer. An event is generated if we have received a
|
||||
# Call (request) / Response pair (or in case of a time out).
|
||||
# info$rpc_stat and info$nfs_stat show whether the request was successful.
|
||||
# The request record is always filled out, however, the reply record(s) might not be set or
|
||||
# might only be partially set. See the comments for the record types in
|
||||
# bro.init to see which reply fields are set when.
|
||||
# Events for the NFS analyzer. An event is generated if we have received a
|
||||
# Call (request) / Response pair (or in case of a time out). info$rpc_stat and
|
||||
# info$nfs_stat show whether the request was successful. The request record is
|
||||
# always filled out, however, the reply record(s) might not be set or might only
|
||||
# be partially set. See the comments for the record types in bro.init to see which
|
||||
# reply fields are set when.
|
||||
event nfs_proc_null%(c: connection, info: NFS3::info_t%);
|
||||
event nfs_proc_not_implemented%(c: connection, info: NFS3::info_t, proc: NFS3::proc_t%);
|
||||
|
||||
|
@ -199,7 +207,7 @@ event nfs_proc_remove%(c: connection, info: NFS3::info_t, req: NFS3::diropargs_t
|
|||
event nfs_proc_rmdir%(c: connection, info: NFS3::info_t, req: NFS3::diropargs_t, rep: NFS3::delobj_reply_t%);
|
||||
event nfs_proc_readdir%(c: connection, info: NFS3::info_t, req: NFS3::readdirargs_t, rep: NFS3::readdir_reply_t%);
|
||||
|
||||
# Generated for each NFS reply message we receive. Just gives the status.
|
||||
# Generated for each NFS reply message we receive, giving just gives the status.
|
||||
event nfs_reply_status%(n: connection, info: NFS3::info_t%);
|
||||
|
||||
event ntp_message%(u: connection, msg: ntp_msg, excess: string%);
|
||||
|
@ -287,19 +295,15 @@ event http_stats%(c: connection, stats: http_stats_rec%);
|
|||
event ssh_client_version%(c: connection, version: string%);
|
||||
event ssh_server_version%(c: connection, version: string%);
|
||||
|
||||
event ssl_certificate_seen%(c: connection, is_server: bool%);
|
||||
event ssl_certificate%(c: connection, cert: X509, is_server: bool%);
|
||||
event ssl_conn_attempt%(c: connection, version: count, ciphers: cipher_suites_list%);
|
||||
event ssl_conn_server_reply%(c: connection, version: count, ciphers: cipher_suites_list%);
|
||||
event ssl_conn_established%(c: connection, version: count, cipher_suite: count%);
|
||||
event ssl_conn_reused%(c: connection, session_id: SSL_sessionID%);
|
||||
event ssl_conn_alert%(c: connection, version: count, level: count,
|
||||
description: count%);
|
||||
event ssl_conn_weak%(name: string, c: connection%);
|
||||
event ssl_client_hello%(c: connection, version: count, possible_ts: time, session_id: string, ciphers: count_set%);
|
||||
event ssl_server_hello%(c: connection, version: count, possible_ts: time, session_id: string, cipher: count, comp_method: count%);
|
||||
event ssl_extension%(c: connection, code: count, val: string%);
|
||||
event ssl_established%(c: connection%);
|
||||
event ssl_alert%(c: connection, level: count, desc: count%);
|
||||
|
||||
event ssl_session_insertion%(c: connection, id: SSL_sessionID%);
|
||||
event process_X509_extensions%(c: connection, ex: X509_extension%);
|
||||
event ssl_X509_error%(c: connection, err: int, err_string: string%);
|
||||
event x509_certificate%(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string%);
|
||||
event x509_extension%(c: connection, data: string%);
|
||||
event x509_error%(c: connection, err: count%);
|
||||
|
||||
event stp_create_endp%(c: connection, e: int, is_orig: bool%);
|
||||
event stp_resume_endp%(e: int%);
|
||||
|
@ -410,6 +414,8 @@ event irc_password_message%(c: connection, password: string%);
|
|||
event file_transferred%(c: connection, prefix: string, descr: string, mime_type: string%);
|
||||
event file_virus%(c: connection, virname: string%);
|
||||
|
||||
event syslog_message%(c: connection, facility: count, severity: count, msg: string%);
|
||||
|
||||
event signature_match%(state: signature_state, msg: string, data: string%);
|
||||
|
||||
# Generated if a handler finds an identification of the software
|
||||
|
|
28
src/main.cc
28
src/main.cc
|
@ -740,6 +740,20 @@ int main(int argc, char** argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
if ( generate_documentation )
|
||||
{
|
||||
std::list<BroDoc*>::iterator it;
|
||||
|
||||
for ( it = docs_generated.begin(); it != docs_generated.end(); ++it )
|
||||
(*it)->WriteDocFile();
|
||||
|
||||
for ( it = docs_generated.begin(); it != docs_generated.end(); ++it )
|
||||
delete *it;
|
||||
|
||||
terminate_bro();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( nerr > 0 )
|
||||
{
|
||||
delete dns_mgr;
|
||||
|
@ -975,20 +989,6 @@ int main(int argc, char** argv)
|
|||
|
||||
mgr.Drain();
|
||||
|
||||
if ( generate_documentation )
|
||||
{
|
||||
std::list<BroDoc*>::iterator it;
|
||||
|
||||
for ( it = docs_generated.begin(); it != docs_generated.end(); ++it )
|
||||
(*it)->WriteDocFile();
|
||||
|
||||
for ( it = docs_generated.begin(); it != docs_generated.end(); ++it )
|
||||
delete *it;
|
||||
|
||||
terminate_bro();
|
||||
return 0;
|
||||
}
|
||||
|
||||
have_pending_timers = ! reading_traces && timer_mgr->Size() > 0;
|
||||
|
||||
if ( io_sources.Size() > 0 || have_pending_timers )
|
||||
|
|
|
@ -936,6 +936,7 @@ type_decl:
|
|||
|
||||
if ( generate_documentation )
|
||||
{
|
||||
// TypeDecl ctor deletes the attr list, so make a copy
|
||||
attr_list* a = $5;
|
||||
attr_list* a_copy = 0;
|
||||
|
||||
|
@ -947,7 +948,7 @@ type_decl:
|
|||
}
|
||||
|
||||
last_fake_type_decl = new CommentedTypeDecl(
|
||||
$4, $2, a_copy, concat_opt_docs($1, $7));
|
||||
$4, $2, a_copy, (in_record > 0), concat_opt_docs($1, $7));
|
||||
}
|
||||
|
||||
$$ = new TypeDecl($4, $2, $5, (in_record > 0));
|
||||
|
@ -1091,8 +1092,10 @@ decl:
|
|||
new RecordType(fake_type_decl_list);
|
||||
ID* fake = create_dummy_id($3, fake_record);
|
||||
fake_type_decl_list = 0;
|
||||
current_reST_doc->AddRedef(
|
||||
new BroDocObj(fake, reST_doc_comments, true));
|
||||
BroDocObj* o =
|
||||
new BroDocObj(fake, reST_doc_comments, true);
|
||||
o->SetRole(true);
|
||||
current_reST_doc->AddRedef(o);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
12
src/scan.l
12
src/scan.l
|
@ -4,6 +4,11 @@
|
|||
|
||||
#include <errno.h>
|
||||
|
||||
#include <stack>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "input.h"
|
||||
#include "util.h"
|
||||
#include "Scope.h"
|
||||
|
@ -19,11 +24,6 @@
|
|||
#include "Analyzer.h"
|
||||
#include "AnalyzerTags.h"
|
||||
|
||||
#include <stack>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
extern YYLTYPE yylloc; // holds start line and column of token
|
||||
extern int print_loaded_scripts;
|
||||
extern int generate_documentation;
|
||||
|
@ -558,7 +558,7 @@ static int load_files_with_prefix(const char* orig_file)
|
|||
else
|
||||
strcpy(new_filename, file);
|
||||
|
||||
f = search_for_file(new_filename, "bro", &full_filename);
|
||||
f = search_for_file(new_filename, "bro", &full_filename, true);
|
||||
delete [] new_filename;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# $Id:$
|
||||
|
||||
# Analyzer for SSL (Bro-specific part).
|
||||
|
||||
%extern{
|
||||
|
@ -11,8 +9,7 @@
|
|||
#include "util.h"
|
||||
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509_vfy.h>
|
||||
#include "X509.h"
|
||||
#include <openssl/asn1.h>
|
||||
%}
|
||||
|
||||
|
||||
|
@ -46,22 +43,20 @@
|
|||
%}
|
||||
|
||||
|
||||
function to_table_val(data : uint8[]) : TableVal
|
||||
function to_string_val(data : uint8[]) : StringVal
|
||||
%{
|
||||
TableVal* tv = new TableVal(SSL_sessionID);
|
||||
for ( unsigned int i = 0; i < data->size(); i += 4 )
|
||||
{
|
||||
uint32 temp = 0;
|
||||
for ( unsigned int j = 0; j < 4; ++j )
|
||||
if ( i + j < data->size() )
|
||||
temp |= (*data)[i + j] << (24 - 8 * j);
|
||||
assert(data->size() <= 32);
|
||||
|
||||
Val* idx = new Val(i / 4, TYPE_COUNT);
|
||||
tv->Assign(idx, new Val((*data)[i], TYPE_COUNT));
|
||||
Unref(idx);
|
||||
char tmp[32];
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
|
||||
if ( data )
|
||||
{
|
||||
for ( unsigned int i = data->size(); i > 0; --i )
|
||||
tmp[i-1] = (*data)[i-1];
|
||||
}
|
||||
|
||||
return tv;
|
||||
return new StringVal(32, tmp);
|
||||
%}
|
||||
|
||||
function version_ok(vers : uint16) : bool
|
||||
|
@ -83,7 +78,7 @@ function convert_ciphers_uint24(ciph : uint24[]) : int[]
|
|||
vector<int>* newciph = new vector<int>();
|
||||
|
||||
std::transform(ciph->begin(), ciph->end(),
|
||||
std::back_inserter(*newciph), to_int());
|
||||
std::back_inserter(*newciph), to_int());
|
||||
|
||||
return newciph;
|
||||
%}
|
||||
|
@ -93,7 +88,7 @@ function convert_ciphers_uint16(ciph : uint16[]) : int[]
|
|||
vector<int>* newciph = new vector<int>();
|
||||
|
||||
std::copy(ciph->begin(), ciph->end(),
|
||||
std::back_inserter(*newciph));
|
||||
std::back_inserter(*newciph));
|
||||
|
||||
return newciph;
|
||||
%}
|
||||
|
@ -101,23 +96,10 @@ function convert_ciphers_uint16(ciph : uint16[]) : int[]
|
|||
refine analyzer SSLAnalyzer += {
|
||||
%member{
|
||||
Analyzer* bro_analyzer_;
|
||||
|
||||
vector<uint8>* client_session_id_;
|
||||
vector<int>* advertised_ciphers_;
|
||||
int version_;
|
||||
int cipher_;
|
||||
%}
|
||||
|
||||
%init{
|
||||
bro_analyzer_ = 0;
|
||||
|
||||
client_session_id_ = 0;
|
||||
advertised_ciphers_ = new vector<int>;
|
||||
version_ = -1;
|
||||
cipher_ = -1;
|
||||
|
||||
if ( ! X509_Cert::bInited )
|
||||
X509_Cert::init();
|
||||
%}
|
||||
|
||||
%eof{
|
||||
|
@ -128,11 +110,6 @@ refine analyzer SSLAnalyzer += {
|
|||
%}
|
||||
|
||||
%cleanup{
|
||||
delete client_session_id_;
|
||||
client_session_id_ = 0;
|
||||
|
||||
delete advertised_ciphers_;
|
||||
advertised_ciphers_ = 0;
|
||||
%}
|
||||
|
||||
function bro_analyzer() : Analyzer
|
||||
|
@ -145,282 +122,203 @@ refine analyzer SSLAnalyzer += {
|
|||
bro_analyzer_ = a;
|
||||
%}
|
||||
|
||||
function check_cipher(cipher : int) : bool
|
||||
%{
|
||||
if ( ! ssl_compare_cipherspecs )
|
||||
return true;
|
||||
|
||||
if ( std::find(advertised_ciphers_->begin(),
|
||||
advertised_ciphers_->end(), cipher) ==
|
||||
advertised_ciphers_->end() )
|
||||
{
|
||||
bro_analyzer()->ProtocolViolation("chosen cipher not advertised before");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
function certificate_error(err_num : int) : void
|
||||
%{
|
||||
StringVal* err_str =
|
||||
new StringVal(X509_verify_cert_error_string(err_num));
|
||||
BifEvent::generate_ssl_X509_error(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
err_num, err_str);
|
||||
%}
|
||||
|
||||
function proc_change_cipher_spec(msg : ChangeCipherSpec) : bool
|
||||
function proc_change_cipher_spec(rec: SSLRecord) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected ChangeCipherSpec from %s at state %s",
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
orig_label(${rec.is_orig}).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_application_data(msg : ApplicationData) : bool
|
||||
function proc_application_data(rec: SSLRecord) : bool
|
||||
%{
|
||||
if ( state_ != STATE_CONN_ESTABLISHED )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected ApplicationData from %s at state %s",
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
orig_label(${rec.is_orig}).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_alert(level : int, description : int) : bool
|
||||
function proc_alert(rec: SSLRecord, level : int, desc : int) : bool
|
||||
%{
|
||||
BifEvent::generate_ssl_conn_alert(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
current_record_version_, level,
|
||||
description);
|
||||
BifEvent::generate_ssl_alert(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
level, desc);
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_client_hello(version : uint16, session_id : uint8[],
|
||||
csuits : int[]) : bool
|
||||
function proc_client_hello(rec: SSLRecord,
|
||||
version : uint16, ts : double,
|
||||
session_id : uint8[],
|
||||
cipher_suites : int[]) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected client hello message from %s in state %s",
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
orig_label(${rec.is_orig}).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
|
||||
if ( ! version_ok(version) )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unsupported client SSL version 0x%04x", version));
|
||||
|
||||
delete client_session_id_;
|
||||
client_session_id_ = new vector<uint8>(*session_id);
|
||||
|
||||
TableVal* cipher_table = new TableVal(cipher_suites_list);
|
||||
for ( unsigned int i = 0; i < csuits->size(); ++i )
|
||||
if ( ssl_client_hello )
|
||||
{
|
||||
Val* ciph = new Val((*csuits)[i], TYPE_COUNT);
|
||||
cipher_table->Assign(ciph, 0);
|
||||
Unref(ciph);
|
||||
}
|
||||
BroType* count_t = base_type(TYPE_COUNT);
|
||||
TypeList* set_index = new TypeList(count_t);
|
||||
set_index->Append(count_t);
|
||||
SetType* s = new SetType(set_index, 0);
|
||||
TableVal* cipher_set = new TableVal(s);
|
||||
for ( unsigned int i = 0; i < cipher_suites->size(); ++i )
|
||||
{
|
||||
Val* ciph = new Val((*cipher_suites)[i], TYPE_COUNT);
|
||||
cipher_set->Assign(ciph, 0);
|
||||
Unref(ciph);
|
||||
}
|
||||
|
||||
BifEvent::generate_ssl_conn_attempt(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
version, cipher_table);
|
||||
|
||||
if ( ssl_compare_cipherspecs )
|
||||
{
|
||||
delete advertised_ciphers_;
|
||||
advertised_ciphers_ = csuits;
|
||||
BifEvent::generate_ssl_client_hello(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
version, ts,
|
||||
to_string_val(session_id),
|
||||
cipher_set);
|
||||
}
|
||||
else
|
||||
delete csuits;
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_server_hello(version : uint16, session_id : uint8[],
|
||||
ciphers : int[], v2_sess_hit : int) : bool
|
||||
function proc_server_hello(rec: SSLRecord,
|
||||
version : uint16, ts : double,
|
||||
session_id : uint8[],
|
||||
cipher_suite : uint16,
|
||||
comp_method : uint8) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected server hello message from %s in state %s",
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
orig_label(${rec.is_orig}).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
|
||||
if ( ! version_ok(version) )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unsupported server SSL version 0x%04x", version));
|
||||
|
||||
version_ = version;
|
||||
|
||||
TableVal* chosen_ciphers = new TableVal(cipher_suites_list);
|
||||
for ( unsigned int i = 0; i < ciphers->size(); ++i )
|
||||
if ( ssl_server_hello )
|
||||
{
|
||||
Val* ciph = new Val((*ciphers)[i], TYPE_COUNT);
|
||||
chosen_ciphers->Assign(ciph, 0);
|
||||
Unref(ciph);
|
||||
}
|
||||
|
||||
BifEvent::generate_ssl_conn_server_reply(bro_analyzer_,
|
||||
bro_analyzer_->Conn(),
|
||||
version_, chosen_ciphers);
|
||||
|
||||
if ( v2_sess_hit < 0 )
|
||||
{ // this is SSLv3
|
||||
cipher_ = (*ciphers)[0];
|
||||
check_cipher(cipher_);
|
||||
TableVal* tv = to_table_val(session_id);
|
||||
if ( client_session_id_ &&
|
||||
*client_session_id_ == *session_id )
|
||||
BifEvent::generate_ssl_conn_reused(bro_analyzer_,
|
||||
bro_analyzer_->Conn(), tv);
|
||||
else
|
||||
BifEvent::generate_ssl_session_insertion(bro_analyzer_,
|
||||
bro_analyzer_->Conn(), tv);
|
||||
|
||||
delete ciphers;
|
||||
}
|
||||
|
||||
else if ( v2_sess_hit > 0 )
|
||||
{ // this is SSLv2 and a session hit
|
||||
if ( client_session_id_ )
|
||||
{
|
||||
TableVal* tv = to_table_val(client_session_id_);
|
||||
BifEvent::generate_ssl_conn_reused(bro_analyzer_,
|
||||
bro_analyzer_->Conn(), tv);
|
||||
}
|
||||
|
||||
// We don't know the chosen cipher, as there is
|
||||
// no session storage.
|
||||
BifEvent::generate_ssl_conn_established(bro_analyzer_,
|
||||
BifEvent::generate_ssl_server_hello(bro_analyzer_,
|
||||
bro_analyzer_->Conn(),
|
||||
version_, 0xffffffff);
|
||||
delete ciphers;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// This is SSLv2; we have to set advertised
|
||||
// ciphers to server ciphers.
|
||||
if ( ssl_compare_cipherspecs )
|
||||
{
|
||||
delete advertised_ciphers_;
|
||||
advertised_ciphers_ = ciphers;
|
||||
}
|
||||
version, ts,
|
||||
to_string_val(session_id),
|
||||
cipher_suite, comp_method);
|
||||
}
|
||||
|
||||
bro_analyzer()->ProtocolConfirmation();
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_certificate(certificates : bytestring[]) : bool
|
||||
function proc_ssl_extension(type: int, data: bytestring) : bool
|
||||
%{
|
||||
if ( ssl_extension )
|
||||
BifEvent::generate_ssl_extension(bro_analyzer_,
|
||||
bro_analyzer_->Conn(), type,
|
||||
new StringVal(data.length(), (const char*) data.data()));
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_certificate(rec: SSLRecord, certificates : bytestring[]) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected certificate message from %s in state %s",
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
orig_label(${rec.is_orig}).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
|
||||
if ( ! ssl_analyze_certificates )
|
||||
return true;
|
||||
if ( certificates->size() == 0 )
|
||||
return true;
|
||||
|
||||
BifEvent::generate_ssl_certificate_seen(bro_analyzer_,
|
||||
bro_analyzer_->Conn(),
|
||||
! current_record_is_orig_);
|
||||
STACK_OF(X509)* untrusted_certs = 0;
|
||||
|
||||
const bytestring& cert = (*certificates)[0];
|
||||
const uint8* data = cert.data();
|
||||
|
||||
X509* pCert = d2i_X509_binpac(NULL, &data, cert.length());
|
||||
if ( ! pCert )
|
||||
if ( x509_certificate )
|
||||
{
|
||||
// X509_V_UNABLE_TO_DECRYPT_CERT_SIGNATURE
|
||||
certificate_error(4);
|
||||
return false;
|
||||
}
|
||||
|
||||
RecordVal* pX509Cert = new RecordVal(x509_type);
|
||||
|
||||
char tmp[256];
|
||||
X509_NAME_oneline(X509_get_issuer_name(pCert), tmp, sizeof tmp);
|
||||
pX509Cert->Assign(0, new StringVal(tmp));
|
||||
X509_NAME_oneline(X509_get_subject_name(pCert), tmp, sizeof tmp);
|
||||
|
||||
pX509Cert->Assign(1, new StringVal(tmp));
|
||||
pX509Cert->Assign(2, new AddrVal(bro_analyzer_->Conn()->OrigAddr()));
|
||||
|
||||
BifEvent::generate_ssl_certificate(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
pX509Cert, current_record_is_orig_);
|
||||
|
||||
if ( X509_get_ext_count(pCert) > 0 )
|
||||
{
|
||||
TableVal* x509ex = new TableVal(x509_extension);
|
||||
|
||||
for ( int k = 0; k < X509_get_ext_count(pCert); ++k )
|
||||
X509* pCert = 0;
|
||||
for ( unsigned int i = 0; i < certificates->size(); ++i )
|
||||
{
|
||||
X509_EXTENSION* ex = X509_get_ext(pCert, k);
|
||||
ASN1_OBJECT* obj = X509_EXTENSION_get_object(ex);
|
||||
|
||||
char buf[256];
|
||||
i2t_ASN1_OBJECT(buf, sizeof(buf), obj);
|
||||
Val* index = new Val(k+1, TYPE_COUNT);
|
||||
Val* value = new StringVal(strlen(buf), buf);
|
||||
x509ex->Assign(index, value);
|
||||
Unref(index);
|
||||
}
|
||||
|
||||
BifEvent::generate_process_X509_extensions(bro_analyzer_,
|
||||
bro_analyzer_->Conn(), x509ex);
|
||||
}
|
||||
|
||||
if ( ssl_verify_certificates )
|
||||
{
|
||||
STACK_OF(X509)* untrusted_certs = 0;
|
||||
if ( certificates->size() > 1 )
|
||||
{
|
||||
untrusted_certs = sk_X509_new_null();
|
||||
if ( ! untrusted_certs )
|
||||
const bytestring& cert = (*certificates)[i];
|
||||
const uint8* data = cert.data();
|
||||
X509* pTemp = d2i_X509_binpac(NULL, &data, cert.length());
|
||||
if ( ! pTemp )
|
||||
{
|
||||
// X509_V_ERR_OUT_OF_MEM;
|
||||
certificate_error(17);
|
||||
BifEvent::generate_x509_error(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
ERR_get_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( unsigned int i = 1;
|
||||
i < certificates->size(); ++i )
|
||||
{
|
||||
const bytestring& temp =
|
||||
(*certificates)[i];
|
||||
const uint8* tdata = temp.data();
|
||||
X509* pTemp = d2i_X509_binpac(NULL,
|
||||
&tdata, temp.length());
|
||||
if ( ! pTemp )
|
||||
{
|
||||
// X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
|
||||
certificate_error(2);
|
||||
return false;
|
||||
}
|
||||
RecordVal* pX509Cert = new RecordVal(x509_type);
|
||||
char tmp[256];
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
|
||||
sk_X509_push(untrusted_certs, pTemp);
|
||||
pX509Cert->Assign(0, new Val((uint64) X509_get_version(pTemp), TYPE_COUNT));
|
||||
i2a_ASN1_INTEGER(bio, X509_get_serialNumber(pTemp));
|
||||
int len = BIO_read(bio, &(*tmp), sizeof tmp);
|
||||
pX509Cert->Assign(1, new StringVal(len, tmp));
|
||||
|
||||
X509_NAME_print_ex(bio, X509_get_subject_name(pTemp), 0, XN_FLAG_RFC2253);
|
||||
len = BIO_gets(bio, &(*tmp), sizeof tmp);
|
||||
pX509Cert->Assign(2, new StringVal(len, tmp));
|
||||
X509_NAME_print_ex(bio, X509_get_issuer_name(pTemp), 0, XN_FLAG_RFC2253);
|
||||
len = BIO_gets(bio, &(*tmp), sizeof tmp);
|
||||
pX509Cert->Assign(3, new StringVal(len, tmp));
|
||||
BIO_free(bio);
|
||||
|
||||
pX509Cert->Assign(4, new Val(get_time_from_asn1(X509_get_notBefore(pTemp)), TYPE_TIME));
|
||||
pX509Cert->Assign(5, new Val(get_time_from_asn1(X509_get_notAfter(pTemp)), TYPE_TIME));
|
||||
StringVal* der_cert = new StringVal(cert.length(), (const char*) cert.data());
|
||||
|
||||
BifEvent::generate_x509_certificate(bro_analyzer_, bro_analyzer_->Conn(),
|
||||
pX509Cert,
|
||||
! ${rec.is_orig},
|
||||
i, certificates->size(),
|
||||
der_cert);
|
||||
|
||||
// Are there any X509 extensions?
|
||||
if ( x509_extension && X509_get_ext_count(pTemp) > 0 )
|
||||
{
|
||||
BroType* count_t = base_type(TYPE_COUNT);
|
||||
TypeList* set_index = new TypeList(count_t);
|
||||
set_index->Append(count_t);
|
||||
SetType* s = new SetType(set_index, 0);
|
||||
TableVal* x509ex = new TableVal(s);
|
||||
int num_ext = X509_get_ext_count(pTemp);
|
||||
for ( int k = 0; k < num_ext; ++k )
|
||||
{
|
||||
char *pBuffer = 0;
|
||||
int length = 0;
|
||||
|
||||
X509_EXTENSION* ex = X509_get_ext(pTemp, k);
|
||||
if (ex)
|
||||
{
|
||||
ASN1_STRING *pString = X509_EXTENSION_get_data(ex);
|
||||
length = ASN1_STRING_to_UTF8((unsigned char**)&pBuffer, pString);
|
||||
//i2t_ASN1_OBJECT(&pBuffer, length, obj)
|
||||
|
||||
// -1 indicates an error.
|
||||
if ( length < 0 )
|
||||
continue;
|
||||
|
||||
StringVal* value = new StringVal(length, pBuffer);
|
||||
BifEvent::generate_x509_extension(bro_analyzer_,
|
||||
bro_analyzer_->Conn(), value);
|
||||
OPENSSL_free(pBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
X509_STORE_CTX csc;
|
||||
X509_STORE_CTX_init(&csc, X509_Cert::ctx,
|
||||
pCert, untrusted_certs);
|
||||
X509_STORE_CTX_set_time(&csc, 0, time_t(network_time()));
|
||||
if (! X509_verify_cert(&csc))
|
||||
certificate_error(csc.error);
|
||||
X509_STORE_CTX_cleanup(&csc);
|
||||
|
||||
sk_X509_pop_free(untrusted_certs, X509_free);
|
||||
}
|
||||
|
||||
X509_free(pCert);
|
||||
return true;
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_v2_certificate(cert : bytestring) : bool
|
||||
function proc_v2_certificate(rec: SSLRecord, cert : bytestring) : bool
|
||||
%{
|
||||
vector<bytestring>* cert_list = new vector<bytestring>(1,cert);
|
||||
bool ret = proc_certificate(cert_list);
|
||||
bool ret = proc_certificate(rec, cert_list);
|
||||
delete cert_list;
|
||||
return ret;
|
||||
%}
|
||||
|
||||
function proc_v3_certificate(cl : CertificateList) : bool
|
||||
function proc_v3_certificate(rec: SSLRecord, cl : CertificateList) : bool
|
||||
%{
|
||||
vector<X509Certificate*>* certs = cl->val();
|
||||
vector<bytestring>* cert_list = new vector<bytestring>();
|
||||
|
@ -428,65 +326,61 @@ refine analyzer SSLAnalyzer += {
|
|||
std::transform(certs->begin(), certs->end(),
|
||||
std::back_inserter(*cert_list), extract_certs());
|
||||
|
||||
bool ret = proc_certificate(cert_list);
|
||||
bool ret = proc_certificate(rec, cert_list);
|
||||
delete cert_list;
|
||||
|
||||
return ret;
|
||||
%}
|
||||
|
||||
function proc_v2_client_master_key(cipher : int) : bool
|
||||
function proc_v2_client_master_key(rec: SSLRecord, cipher_kind: int) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected v2 client master key message from %s in state %s",
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
orig_label(${rec.is_orig}).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
|
||||
check_cipher(cipher);
|
||||
BifEvent::generate_ssl_conn_established(bro_analyzer_,
|
||||
bro_analyzer_->Conn(), version_, cipher);
|
||||
BifEvent::generate_ssl_established(bro_analyzer_,
|
||||
bro_analyzer_->Conn());
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_unknown_handshake(msg_type : int) : bool
|
||||
function proc_unknown_handshake(hs: Handshake, is_orig: bool) : bool
|
||||
%{
|
||||
bro_analyzer()->ProtocolViolation(fmt("unknown handshake message (%d) from %s",
|
||||
msg_type, orig_label(current_record_is_orig_).c_str()));
|
||||
${hs.msg_type}, orig_label(is_orig).c_str()));
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_handshake(msg : Handshake) : bool
|
||||
function proc_handshake(hs: Handshake, is_orig: bool) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected Handshake message %s from %s in state %s",
|
||||
handshake_type_label(msg->msg_type()).c_str(),
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
handshake_type_label(${hs.msg_type}).c_str(),
|
||||
orig_label(is_orig).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_unknown_record(msg : UnknownRecord) : bool
|
||||
function proc_unknown_record(rec: SSLRecord) : bool
|
||||
%{
|
||||
bro_analyzer()->ProtocolViolation(fmt("unknown SSL record type (%d) from %s",
|
||||
current_record_type_,
|
||||
orig_label(current_record_is_orig_).c_str()));
|
||||
${rec.content_type},
|
||||
orig_label(${rec.is_orig}).c_str()));
|
||||
return true;
|
||||
%}
|
||||
|
||||
function proc_ciphertext_record(msg : CiphertextRecord) : bool
|
||||
function proc_ciphertext_record(rec : SSLRecord) : bool
|
||||
%{
|
||||
if ( state_ == STATE_TRACK_LOST )
|
||||
bro_analyzer()->ProtocolViolation(fmt("unexpected ciphertext record from %s in state %s",
|
||||
orig_label(current_record_is_orig_).c_str(),
|
||||
orig_label(${rec.is_orig}).c_str(),
|
||||
state_label(old_state_).c_str()));
|
||||
|
||||
if ( state_ == STATE_CONN_ESTABLISHED &&
|
||||
old_state_ == STATE_COMM_ENCRYPTED )
|
||||
{
|
||||
BifEvent::generate_ssl_conn_established(bro_analyzer_,
|
||||
bro_analyzer_->Conn(),
|
||||
version_, cipher_);
|
||||
}
|
||||
else if ( state_ == STATE_CONN_ESTABLISHED &&
|
||||
old_state_ == STATE_COMM_ENCRYPTED )
|
||||
BifEvent::generate_ssl_established(bro_analyzer_,
|
||||
bro_analyzer_->Conn());
|
||||
|
||||
return true;
|
||||
%}
|
||||
|
@ -494,72 +388,80 @@ refine analyzer SSLAnalyzer += {
|
|||
};
|
||||
|
||||
refine typeattr ChangeCipherSpec += &let {
|
||||
proc : bool = $context.analyzer.proc_change_cipher_spec(this)
|
||||
proc : bool = $context.analyzer.proc_change_cipher_spec(rec)
|
||||
&requires(state_changed);
|
||||
};
|
||||
|
||||
refine typeattr Alert += &let {
|
||||
proc : bool = $context.analyzer.proc_alert(level, description);
|
||||
proc : bool = $context.analyzer.proc_alert(rec, level, description);
|
||||
};
|
||||
|
||||
refine typeattr V2Error += &let {
|
||||
proc : bool = $context.analyzer.proc_alert(-1, error_code);
|
||||
proc : bool = $context.analyzer.proc_alert(rec, -1, error_code);
|
||||
};
|
||||
|
||||
refine typeattr ApplicationData += &let {
|
||||
proc : bool = $context.analyzer.proc_application_data(this);
|
||||
proc : bool = $context.analyzer.proc_application_data(rec);
|
||||
};
|
||||
|
||||
refine typeattr ClientHello += &let {
|
||||
proc : bool = $context.analyzer.proc_client_hello(client_version,
|
||||
proc : bool = $context.analyzer.proc_client_hello(rec, client_version,
|
||||
gmt_unix_time,
|
||||
session_id, convert_ciphers_uint16(csuits))
|
||||
&requires(state_changed);
|
||||
};
|
||||
|
||||
refine typeattr V2ClientHello += &let {
|
||||
proc : bool = $context.analyzer.proc_client_hello(client_version,
|
||||
proc : bool = $context.analyzer.proc_client_hello(rec, client_version, 0,
|
||||
session_id, convert_ciphers_uint24(ciphers))
|
||||
&requires(state_changed);
|
||||
};
|
||||
|
||||
refine typeattr ServerHello += &let {
|
||||
proc : bool = $context.analyzer.proc_server_hello(server_version,
|
||||
session_id, convert_ciphers_uint16(cipher_suite), -1)
|
||||
proc : bool = $context.analyzer.proc_server_hello(rec, server_version,
|
||||
gmt_unix_time, session_id, cipher_suite,
|
||||
compression_method)
|
||||
&requires(state_changed);
|
||||
};
|
||||
|
||||
refine typeattr V2ServerHello += &let {
|
||||
proc : bool = $context.analyzer.proc_server_hello(server_version, 0,
|
||||
convert_ciphers_uint24(ciphers), session_id_hit)
|
||||
proc : bool = $context.analyzer.proc_server_hello(rec, server_version, 0, 0,
|
||||
convert_ciphers_uint24(ciphers)[0], 0)
|
||||
&requires(state_changed);
|
||||
|
||||
cert : bool = $context.analyzer.proc_v2_certificate(cert_data)
|
||||
cert : bool = $context.analyzer.proc_v2_certificate(rec, cert_data)
|
||||
&requires(proc);
|
||||
};
|
||||
|
||||
refine typeattr Certificate += &let {
|
||||
proc : bool = $context.analyzer.proc_v3_certificate(certificates)
|
||||
proc : bool = $context.analyzer.proc_v3_certificate(rec, certificates)
|
||||
&requires(state_changed);
|
||||
};
|
||||
|
||||
refine typeattr V2ClientMasterKey += &let {
|
||||
proc : bool = $context.analyzer.proc_v2_client_master_key(to_int()(cipher_kind))
|
||||
proc : bool = $context.analyzer.proc_v2_client_master_key(rec, to_int()(cipher_kind))
|
||||
&requires(state_changed);
|
||||
};
|
||||
|
||||
refine typeattr UnknownHandshake += &let {
|
||||
proc : bool = $context.analyzer.proc_unknown_handshake(msg_type);
|
||||
proc : bool = $context.analyzer.proc_unknown_handshake(hs, is_orig);
|
||||
};
|
||||
|
||||
refine typeattr Handshake += &let {
|
||||
proc : bool = $context.analyzer.proc_handshake(this);
|
||||
proc : bool = $context.analyzer.proc_handshake(this, rec.is_orig);
|
||||
};
|
||||
|
||||
refine typeattr UnknownRecord += &let {
|
||||
proc : bool = $context.analyzer.proc_unknown_record(this);
|
||||
proc : bool = $context.analyzer.proc_unknown_record(rec);
|
||||
};
|
||||
|
||||
refine typeattr CiphertextRecord += &let {
|
||||
proc : bool = $context.analyzer.proc_ciphertext_record(this)
|
||||
&requires(state_changed);
|
||||
proc : bool = $context.analyzer.proc_ciphertext_record(rec);
|
||||
}
|
||||
|
||||
refine typeattr SSLExtension += &let {
|
||||
proc : bool = $context.analyzer.proc_ssl_extension(type, data);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# $Id:$
|
||||
|
||||
# Analyzer for SSL messages (general part).
|
||||
# To be used in conjunction with an SSL record-layer analyzer.
|
||||
# Separation is necessary due to possible fragmentation of SSL records.
|
||||
|
@ -26,6 +24,57 @@ type uint24 = record {
|
|||
|
||||
extern type to_int;
|
||||
|
||||
type SSLRecord(is_orig: bool) = record {
|
||||
head0 : uint8;
|
||||
head1 : uint8;
|
||||
head2 : uint8;
|
||||
head3 : uint8;
|
||||
head4 : uint8;
|
||||
rec : RecordText(this, is_orig) &requires(content_type), &restofdata;
|
||||
} &length = length+5, &byteorder=bigendian,
|
||||
&let {
|
||||
version : int =
|
||||
$context.analyzer.determine_ssl_version(head0, head1, head2);
|
||||
|
||||
content_type : int = case version of {
|
||||
UNKNOWN_VERSION -> 0;
|
||||
SSLv20 -> head2+300;
|
||||
default -> head0;
|
||||
};
|
||||
|
||||
length : int = case version of {
|
||||
UNKNOWN_VERSION -> 0;
|
||||
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
|
||||
default -> (head3 << 8) | head4;
|
||||
};
|
||||
};
|
||||
|
||||
type RecordText(rec: SSLRecord, is_orig: bool) = case $context.analyzer.state() of {
|
||||
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED,
|
||||
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED
|
||||
-> ciphertext : CiphertextRecord(rec, is_orig);
|
||||
default
|
||||
-> plaintext : PlaintextRecord(rec, is_orig);
|
||||
};
|
||||
|
||||
type PlaintextRecord(rec: SSLRecord, is_orig: bool) = case rec.content_type of {
|
||||
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec);
|
||||
ALERT -> alert : Alert(rec);
|
||||
HANDSHAKE -> handshake : Handshake(rec)[];
|
||||
APPLICATION_DATA -> app_data : ApplicationData(rec);
|
||||
V2_ERROR -> v2_error : V2Error(rec);
|
||||
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec);
|
||||
V2_CLIENT_MASTER_KEY -> v2_client_master_key : V2ClientMasterKey(rec);
|
||||
V2_SERVER_HELLO -> v2_server_hello : V2ServerHello(rec);
|
||||
default -> unknown_record : UnknownRecord(rec);
|
||||
};
|
||||
|
||||
type SSLExtension = record {
|
||||
type: uint16;
|
||||
data_len: uint16;
|
||||
data: bytestring &length=data_len;
|
||||
};
|
||||
|
||||
######################################################################
|
||||
# state management according to Section 7.3. in spec
|
||||
######################################################################
|
||||
|
@ -99,6 +148,104 @@ enum AnalyzerState {
|
|||
{
|
||||
return string(is_orig ? "originator" :"responder");
|
||||
}
|
||||
|
||||
double get_time_from_asn1(const ASN1_TIME * atime)
|
||||
{
|
||||
time_t lResult = 0;
|
||||
|
||||
char lBuffer[24];
|
||||
char * pBuffer = lBuffer;
|
||||
|
||||
size_t lTimeLength = atime->length;
|
||||
char * pString = (char *) atime->data;
|
||||
|
||||
if ( atime->type == V_ASN1_UTCTIME )
|
||||
{
|
||||
if ( lTimeLength < 11 || lTimeLength > 17 )
|
||||
return 0;
|
||||
|
||||
memcpy(pBuffer, pString, 10);
|
||||
pBuffer += 10;
|
||||
pString += 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( lTimeLength < 13 )
|
||||
return 0;
|
||||
|
||||
memcpy(pBuffer, pString, 12);
|
||||
pBuffer += 12;
|
||||
pString += 12;
|
||||
}
|
||||
|
||||
if ((*pString == 'Z') || (*pString == '-') || (*pString == '+'))
|
||||
{
|
||||
*(pBuffer++) = '0';
|
||||
*(pBuffer++) = '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
*(pBuffer++) = *(pString++);
|
||||
*(pBuffer++) = *(pString++);
|
||||
|
||||
// Skip any fractional seconds...
|
||||
if (*pString == '.')
|
||||
{
|
||||
pString++;
|
||||
while ((*pString >= '0') && (*pString <= '9'))
|
||||
pString++;
|
||||
}
|
||||
}
|
||||
|
||||
*(pBuffer++) = 'Z';
|
||||
*(pBuffer++) = '\0';
|
||||
|
||||
time_t lSecondsFromUTC;
|
||||
|
||||
if ( *pString == 'Z' )
|
||||
lSecondsFromUTC = 0;
|
||||
|
||||
else
|
||||
{
|
||||
if ((*pString != '+') && (pString[5] != '-'))
|
||||
return 0;
|
||||
|
||||
lSecondsFromUTC = ((pString[1]-'0') * 10 + (pString[2]-'0')) * 60;
|
||||
lSecondsFromUTC += (pString[3]-'0') * 10 + (pString[4]-'0');
|
||||
|
||||
if (*pString == '-')
|
||||
lSecondsFromUTC = -lSecondsFromUTC;
|
||||
}
|
||||
|
||||
tm lTime;
|
||||
lTime.tm_sec = ((lBuffer[10] - '0') * 10) + (lBuffer[11] - '0');
|
||||
lTime.tm_min = ((lBuffer[8] - '0') * 10) + (lBuffer[9] - '0');
|
||||
lTime.tm_hour = ((lBuffer[6] - '0') * 10) + (lBuffer[7] - '0');
|
||||
lTime.tm_mday = ((lBuffer[4] - '0') * 10) + (lBuffer[5] - '0');
|
||||
lTime.tm_mon = (((lBuffer[2] - '0') * 10) + (lBuffer[3] - '0')) - 1;
|
||||
lTime.tm_year = ((lBuffer[0] - '0') * 10) + (lBuffer[1] - '0');
|
||||
|
||||
if ( lTime.tm_year < 50 )
|
||||
lTime.tm_year += 100; // RFC 2459
|
||||
|
||||
lTime.tm_wday = 0;
|
||||
lTime.tm_yday = 0;
|
||||
lTime.tm_isdst = 0; // No DST adjustment requested
|
||||
|
||||
lResult = mktime(&lTime);
|
||||
|
||||
if ( lResult )
|
||||
{
|
||||
if ( 0 != lTime.tm_isdst )
|
||||
lResult -= 3600; // mktime may adjust for DST (OS dependent)
|
||||
|
||||
lResult += lSecondsFromUTC;
|
||||
}
|
||||
else
|
||||
lResult = 0;
|
||||
|
||||
return lResult;
|
||||
}
|
||||
%}
|
||||
|
||||
######################################################################
|
||||
|
@ -115,7 +262,9 @@ enum HandshakeType {
|
|||
SERVER_HELLO_DONE = 14,
|
||||
CERTIFICATE_VERIFY = 15,
|
||||
CLIENT_KEY_EXCHANGE = 16,
|
||||
FINISHED = 20
|
||||
FINISHED = 20,
|
||||
CERTIFICATE_URL = 21, # RFC 3546
|
||||
CERTIFICATE_STATUS = 22, # RFC 3546
|
||||
};
|
||||
|
||||
%code{
|
||||
|
@ -132,6 +281,8 @@ enum HandshakeType {
|
|||
case CERTIFICATE_VERIFY: return string("CERTIFICATE_VERIFY");
|
||||
case CLIENT_KEY_EXCHANGE: return string("CLIENT_KEY_EXCHANGE");
|
||||
case FINISHED: return string("FINISHED");
|
||||
case CERTIFICATE_URL: return string("CERTIFICATE_URL");
|
||||
case CERTIFICATE_STATUS: return string("CERTIFICATE_STATUS");
|
||||
default: return string(fmt("UNKNOWN (%d)", type));
|
||||
}
|
||||
}
|
||||
|
@ -142,23 +293,25 @@ enum HandshakeType {
|
|||
# V3 Change Cipher Spec Protocol (7.1.)
|
||||
######################################################################
|
||||
|
||||
type ChangeCipherSpec = record {
|
||||
type ChangeCipherSpec(rec: SSLRecord) = record {
|
||||
type : uint8;
|
||||
} &length = 1, &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_CLIENT_FINISHED,
|
||||
STATE_COMM_ENCRYPTED, false) ||
|
||||
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_ABBREV_SERVER_ENCRYPTED, false) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_KEY_NO_CERT,
|
||||
STATE_CLIENT_ENCRYPTED, true) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_CERT_VERIFIED,
|
||||
STATE_CLIENT_ENCRYPTED, true) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
|
||||
STATE_CLIENT_ENCRYPTED, true) ||
|
||||
$context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED,
|
||||
STATE_COMM_ENCRYPTED, true) ||
|
||||
$context.analyzer.lost_track();
|
||||
$context.analyzer.transition(STATE_CLIENT_FINISHED,
|
||||
STATE_COMM_ENCRYPTED, rec.is_orig, false) ||
|
||||
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_ABBREV_SERVER_ENCRYPTED, rec.is_orig, false) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_KEY_NO_CERT,
|
||||
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_CERT_VERIFIED,
|
||||
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
#$context.analyzer.transition(STATE_CLIENT_CERT,
|
||||
# STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
|
||||
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.analyzer.transition(STATE_ABBREV_SERVER_FINISHED,
|
||||
STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
||||
|
@ -166,19 +319,19 @@ type ChangeCipherSpec = record {
|
|||
# V3 Alert Protocol (7.2.)
|
||||
######################################################################
|
||||
|
||||
type Alert = record {
|
||||
type Alert(rec: SSLRecord) = record {
|
||||
level : uint8;
|
||||
description: uint8;
|
||||
} &length = 2;
|
||||
};
|
||||
|
||||
|
||||
######################################################################
|
||||
# V2 Error Records (SSLv2 2.7.)
|
||||
######################################################################
|
||||
|
||||
type V2Error = record {
|
||||
type V2Error(rec: SSLRecord) = record {
|
||||
error_code : uint16;
|
||||
} &length = 2;
|
||||
};
|
||||
|
||||
|
||||
######################################################################
|
||||
|
@ -187,9 +340,7 @@ type V2Error = record {
|
|||
|
||||
# Application data should always be encrypted, so we should not
|
||||
# reach this point.
|
||||
type ApplicationData = empty &let {
|
||||
discard: bool = $context.flow.discard_data();
|
||||
};
|
||||
type ApplicationData(rec: SSLRecord) = empty;
|
||||
|
||||
######################################################################
|
||||
# Handshake Protocol (7.4.)
|
||||
|
@ -200,7 +351,7 @@ type ApplicationData = empty &let {
|
|||
######################################################################
|
||||
|
||||
# Hello Request is empty
|
||||
type HelloRequest = empty &let {
|
||||
type HelloRequest(rec: SSLRecord) = empty &let {
|
||||
hr: bool = $context.analyzer.set_hello_requested(true);
|
||||
};
|
||||
|
||||
|
@ -209,7 +360,7 @@ type HelloRequest = empty &let {
|
|||
# V3 Client Hello (7.4.1.2.)
|
||||
######################################################################
|
||||
|
||||
type ClientHello = record {
|
||||
type ClientHello(rec: SSLRecord) = record {
|
||||
client_version : uint16;
|
||||
gmt_unix_time : uint32;
|
||||
random_bytes : bytestring &length = 28 &transient;
|
||||
|
@ -219,12 +370,16 @@ type ClientHello = record {
|
|||
csuits : uint16[csuit_len/2];
|
||||
cmeth_len : uint8 &check(cmeth_len > 0);
|
||||
cmeths : uint8[cmeth_len];
|
||||
# This weirdness is to deal with the possible existence or absence
|
||||
# of the following fields.
|
||||
ext_len: uint16[] &until($element == 0 || $element != 0);
|
||||
extensions : SSLExtension[] &until($input.length() == 0);
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_INITIAL,
|
||||
STATE_CLIENT_HELLO_RCVD, true) ||
|
||||
STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
|
||||
($context.analyzer.hello_requested() &&
|
||||
$context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, true)) ||
|
||||
$context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -233,7 +388,7 @@ type ClientHello = record {
|
|||
# V2 Client Hello (SSLv2 2.5.)
|
||||
######################################################################
|
||||
|
||||
type V2ClientHello = record {
|
||||
type V2ClientHello(rec: SSLRecord) = record {
|
||||
client_version : uint16;
|
||||
csuit_len : uint16;
|
||||
session_len : uint16;
|
||||
|
@ -244,9 +399,9 @@ type V2ClientHello = record {
|
|||
} &length = 8 + csuit_len + session_len + chal_len, &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_INITIAL,
|
||||
STATE_CLIENT_HELLO_RCVD, true) ||
|
||||
STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
|
||||
($context.analyzer.hello_requested() &&
|
||||
$context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, true)) ||
|
||||
$context.analyzer.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -255,18 +410,18 @@ type V2ClientHello = record {
|
|||
# V3 Server Hello (7.4.1.3.)
|
||||
######################################################################
|
||||
|
||||
type ServerHello = record {
|
||||
type ServerHello(rec: SSLRecord) = record {
|
||||
server_version : uint16;
|
||||
gmt_unix_time : uint32;
|
||||
random_bytes : bytestring &length = 28 &transient;
|
||||
session_len : uint8;
|
||||
session_id : uint8[session_len];
|
||||
cipher_suite : uint16[1];
|
||||
cipher_suite : uint16;
|
||||
compression_method : uint8;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_CLIENT_HELLO_RCVD,
|
||||
STATE_IN_SERVER_HELLO, false) ||
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -275,7 +430,7 @@ type ServerHello = record {
|
|||
# V2 Server Hello (SSLv2 2.6.)
|
||||
######################################################################
|
||||
|
||||
type V2ServerHello = record {
|
||||
type V2ServerHello(rec: SSLRecord) = record {
|
||||
session_id_hit : uint8;
|
||||
cert_type : uint8;
|
||||
server_version : uint16;
|
||||
|
@ -289,9 +444,9 @@ type V2ServerHello = record {
|
|||
state_changed : bool =
|
||||
(session_id_hit > 0 ?
|
||||
$context.analyzer.transition(STATE_CLIENT_HELLO_RCVD,
|
||||
STATE_CONN_ESTABLISHED, false) :
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, false) :
|
||||
$context.analyzer.transition(STATE_CLIENT_HELLO_RCVD,
|
||||
STATE_V2_CL_MASTER_KEY_EXPECTED, false)) ||
|
||||
STATE_V2_CL_MASTER_KEY_EXPECTED, rec.is_orig, false)) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -307,15 +462,15 @@ type X509Certificate = record {
|
|||
|
||||
type CertificateList = X509Certificate[] &until($input.length() == 0);
|
||||
|
||||
type Certificate = record {
|
||||
type Certificate(rec: SSLRecord) = record {
|
||||
length : uint24;
|
||||
certificates : CertificateList &length = to_int()(length);
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_IN_SERVER_HELLO, false) ||
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.analyzer.transition(STATE_SERVER_HELLO_DONE,
|
||||
STATE_CLIENT_CERT, true) ||
|
||||
STATE_CLIENT_CERT, rec.is_orig, true) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -325,12 +480,12 @@ type Certificate = record {
|
|||
######################################################################
|
||||
|
||||
# For now ignore details; just eat up complete message
|
||||
type ServerKeyExchange = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
type ServerKeyExchange(rec: SSLRecord) = record {
|
||||
key : bytestring &restofdata;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_IN_SERVER_HELLO, false) ||
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -340,12 +495,12 @@ type ServerKeyExchange = record {
|
|||
######################################################################
|
||||
|
||||
# For now, ignore Certificate Request Details; just eat up message.
|
||||
type CertificateRequest = record {
|
||||
type CertificateRequest(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_IN_SERVER_HELLO, false) ||
|
||||
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -355,10 +510,10 @@ type CertificateRequest = record {
|
|||
######################################################################
|
||||
|
||||
# Server Hello Done is empty
|
||||
type ServerHelloDone = empty &let {
|
||||
type ServerHelloDone(rec: SSLRecord) = empty &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_IN_SERVER_HELLO,
|
||||
STATE_SERVER_HELLO_DONE, false) ||
|
||||
STATE_SERVER_HELLO_DONE, rec.is_orig, false) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -377,14 +532,16 @@ type ServerHelloDone = empty &let {
|
|||
|
||||
# For now ignore details of ClientKeyExchange (most of it is
|
||||
# encrypted anyway); just eat up message.
|
||||
type ClientKeyExchange = record {
|
||||
type ClientKeyExchange(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_SERVER_HELLO_DONE,
|
||||
STATE_CLIENT_KEY_NO_CERT, true) ||
|
||||
STATE_CLIENT_KEY_NO_CERT, rec.is_orig, true) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_CERT,
|
||||
STATE_CLIENT_KEY_WITH_CERT, true) ||
|
||||
STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_CERT,
|
||||
STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -392,7 +549,7 @@ type ClientKeyExchange = record {
|
|||
# V2 Client Master Key (SSLv2 2.5.)
|
||||
######################################################################
|
||||
|
||||
type V2ClientMasterKey = record {
|
||||
type V2ClientMasterKey(rec: SSLRecord) = record {
|
||||
cipher_kind : uint24;
|
||||
cl_key_len : uint16;
|
||||
en_key_len : uint16;
|
||||
|
@ -403,7 +560,7 @@ type V2ClientMasterKey = record {
|
|||
} &length = 9 + cl_key_len + en_key_len + key_arg_len, &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_V2_CL_MASTER_KEY_EXPECTED,
|
||||
STATE_CONN_ESTABLISHED, true) ||
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -413,12 +570,12 @@ type V2ClientMasterKey = record {
|
|||
######################################################################
|
||||
|
||||
# For now, ignore Certificate Verify; just eat up the message.
|
||||
type CertificateVerify = record {
|
||||
type CertificateVerify(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_CLIENT_KEY_WITH_CERT,
|
||||
STATE_CLIENT_CERT_VERIFIED, true) ||
|
||||
STATE_CLIENT_CERT_VERIFIED, rec.is_orig, true) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -427,35 +584,40 @@ type CertificateVerify = record {
|
|||
# V3 Finished (7.4.9.)
|
||||
######################################################################
|
||||
|
||||
# The Finished messages are always sent after encryption is in effect,
|
||||
# so we will not be able to read those message
|
||||
# The finished messages are always sent after encryption is in effect,
|
||||
# so we will not be able to read those message.
|
||||
|
||||
|
||||
######################################################################
|
||||
# V3 Handshake Protocol (7.)
|
||||
######################################################################
|
||||
|
||||
type UnknownHandshake(msg_type : uint8) = record {
|
||||
type UnknownHandshake(hs: Handshake, is_orig: bool) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
state_changed : bool = $context.analyzer.lost_track();
|
||||
# TODO: an unknown handshake could just be an encrypted handshake
|
||||
# before a server sends the change cipher spec message.
|
||||
# I have no clue why this happens, but it does seem to happen.
|
||||
# This should be solved in a different way eventually.
|
||||
#state_changed : bool = $context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
type Handshake = record {
|
||||
|
||||
type Handshake(rec: SSLRecord) = record {
|
||||
msg_type : uint8;
|
||||
length : uint24;
|
||||
|
||||
body : case msg_type of {
|
||||
HELLO_REQUEST -> hello_request : HelloRequest;
|
||||
CLIENT_HELLO -> client_hello : ClientHello;
|
||||
SERVER_HELLO -> server_hello : ServerHello;
|
||||
CERTIFICATE -> certificate : Certificate;
|
||||
SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange;
|
||||
CERTIFICATE_REQUEST -> certificate_request : CertificateRequest;
|
||||
SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone;
|
||||
CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify;
|
||||
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange;
|
||||
default -> unknown_handshake : UnknownHandshake(msg_type);
|
||||
HELLO_REQUEST -> hello_request : HelloRequest(rec);
|
||||
CLIENT_HELLO -> client_hello : ClientHello(rec);
|
||||
SERVER_HELLO -> server_hello : ServerHello(rec);
|
||||
CERTIFICATE -> certificate : Certificate(rec);
|
||||
SERVER_KEY_EXCHANGE -> server_key_exchange : ServerKeyExchange(rec);
|
||||
CERTIFICATE_REQUEST -> certificate_request : CertificateRequest(rec);
|
||||
SERVER_HELLO_DONE -> server_hello_done : ServerHelloDone(rec);
|
||||
CERTIFICATE_VERIFY -> certificate_verify : CertificateVerify(rec);
|
||||
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec);
|
||||
default -> unknown_handshake : UnknownHandshake(this, rec.is_orig);
|
||||
};
|
||||
} &length = 4 + to_int()(length);
|
||||
|
||||
|
@ -464,40 +626,26 @@ type Handshake = record {
|
|||
# Fragmentation (6.2.1.)
|
||||
######################################################################
|
||||
|
||||
type UnknownRecord = record {
|
||||
cont : empty;
|
||||
type UnknownRecord(rec: SSLRecord) = record {
|
||||
cont : bytestring &restofdata &transient;
|
||||
} &let {
|
||||
discard : bool = $context.flow.discard_data();
|
||||
state_changed : bool = $context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
type PlaintextRecord = case $context.analyzer.current_record_type() of {
|
||||
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec;
|
||||
ALERT -> alert : Alert;
|
||||
HANDSHAKE -> handshakes : Handshake;
|
||||
APPLICATION_DATA -> app_data : ApplicationData;
|
||||
V2_ERROR -> v2_error : V2Error;
|
||||
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello;
|
||||
V2_CLIENT_MASTER_KEY -> v2_client_master_key : V2ClientMasterKey;
|
||||
V2_SERVER_HELLO -> v2_server_hello : V2ServerHello;
|
||||
UNKNOWN_OR_V2_ENCRYPTED -> unknown_record : UnknownRecord;
|
||||
};
|
||||
|
||||
type CiphertextRecord = empty &let {
|
||||
discard : bool = $context.flow.discard_data();
|
||||
type CiphertextRecord(rec: SSLRecord, is_orig: bool) = empty &let {
|
||||
state_changed : bool =
|
||||
$context.analyzer.transition(STATE_ABBREV_SERVER_ENCRYPTED,
|
||||
STATE_ABBREV_SERVER_FINISHED, false) ||
|
||||
STATE_ABBREV_SERVER_FINISHED, rec.is_orig, false) ||
|
||||
$context.analyzer.transition(STATE_CLIENT_ENCRYPTED,
|
||||
STATE_CLIENT_FINISHED, true) ||
|
||||
STATE_CLIENT_FINISHED, rec.is_orig, true) ||
|
||||
$context.analyzer.transition(STATE_COMM_ENCRYPTED,
|
||||
STATE_CONN_ESTABLISHED, false) ||
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
|
||||
$context.analyzer.transition(STATE_COMM_ENCRYPTED,
|
||||
STATE_CONN_ESTABLISHED, true) ||
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
|
||||
$context.analyzer.transition(STATE_CONN_ESTABLISHED,
|
||||
STATE_CONN_ESTABLISHED, false) ||
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
|
||||
$context.analyzer.transition(STATE_CONN_ESTABLISHED,
|
||||
STATE_CONN_ESTABLISHED, true) ||
|
||||
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
|
||||
$context.analyzer.lost_track();
|
||||
};
|
||||
|
||||
|
@ -506,15 +654,9 @@ type CiphertextRecord = empty &let {
|
|||
# initial datatype for binpac
|
||||
######################################################################
|
||||
|
||||
type SSLPDU = case $context.analyzer.state() of {
|
||||
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED,
|
||||
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED
|
||||
-> ciphertext : CiphertextRecord;
|
||||
default
|
||||
-> plaintext : PlaintextRecord;
|
||||
} &byteorder = bigendian, &let {
|
||||
consumed : bool = $context.flow.consume_data();
|
||||
};
|
||||
type SSLPDU(is_orig: bool) = record {
|
||||
records : SSLRecord(is_orig)[] &until($element == 0);
|
||||
} &byteorder = bigendian;
|
||||
|
||||
|
||||
######################################################################
|
||||
|
@ -526,60 +668,48 @@ analyzer SSLAnalyzer {
|
|||
downflow = SSLFlow(false);
|
||||
|
||||
%member{
|
||||
int current_record_type_;
|
||||
int current_record_version_;
|
||||
int current_record_length_;
|
||||
bool current_record_is_orig_;
|
||||
int state_;
|
||||
int old_state_;
|
||||
bool hello_requested_;
|
||||
%}
|
||||
|
||||
%init{
|
||||
current_record_type_ = -1;
|
||||
current_record_version_ = -1;
|
||||
current_record_length_ = -1;
|
||||
current_record_is_orig_ = false;
|
||||
state_ = STATE_INITIAL;
|
||||
old_state_ = STATE_INITIAL;
|
||||
hello_requested_ = false;
|
||||
%}
|
||||
|
||||
function current_record_type() : int
|
||||
%{ return current_record_type_; %}
|
||||
function current_record_version() : int
|
||||
%{ return current_record_version_; %}
|
||||
function current_record_length() : int
|
||||
%{ return current_record_length_; %}
|
||||
function current_record_is_orig() : bool
|
||||
%{ return current_record_is_orig_; %}
|
||||
|
||||
function next_record(rec : const_bytestring, type : int,
|
||||
version : int, is_orig : bool) : bool
|
||||
function determine_ssl_version(head0 : uint8, head1 : uint8,
|
||||
head2 : uint8) : int
|
||||
%{
|
||||
current_record_type_ = type;
|
||||
current_record_version_ = version;
|
||||
current_record_length_ = rec.length();
|
||||
current_record_is_orig_ = is_orig;
|
||||
if ( head0 >= 20 && head0 <= 23 &&
|
||||
head1 == 0x03 && head2 < 0x03 )
|
||||
// This is most probably SSL version 3.
|
||||
return (head1 << 8) | head2;
|
||||
|
||||
NewData(is_orig, rec.begin(), rec.end());
|
||||
else if ( head0 >= 128 && head2 < 5 && head2 != 3 )
|
||||
// Not very strong evidence, but we suspect
|
||||
// this to be SSLv2.
|
||||
return SSLv20;
|
||||
|
||||
return true;
|
||||
else
|
||||
return UNKNOWN_VERSION;
|
||||
%}
|
||||
|
||||
function state() : int %{ return state_; %}
|
||||
function old_state() : int %{ return old_state_; %}
|
||||
|
||||
function transition(olds : AnalyzerState, news : AnalyzerState,
|
||||
is_orig : bool) : bool
|
||||
current_record_is_orig : bool, is_orig : bool) : bool
|
||||
%{
|
||||
if ( (olds != STATE_ANY && olds != state_) ||
|
||||
current_record_is_orig_ != is_orig )
|
||||
current_record_is_orig != is_orig )
|
||||
return false;
|
||||
|
||||
old_state_ = state_;
|
||||
state_ = news;
|
||||
|
||||
//printf("transitioning from %s to %s\n", state_label(old_state()).c_str(), state_label(state()).c_str());
|
||||
return true;
|
||||
%}
|
||||
|
||||
|
@ -602,29 +732,3 @@ analyzer SSLAnalyzer {
|
|||
return val;
|
||||
%}
|
||||
};
|
||||
|
||||
|
||||
######################################################################
|
||||
# binpac flow for SSL
|
||||
######################################################################
|
||||
|
||||
flow SSLFlow(is_orig : bool) {
|
||||
flowunit = SSLPDU withcontext(connection, this);
|
||||
|
||||
function discard_data() : bool
|
||||
%{
|
||||
flow_buffer_->DiscardData();
|
||||
return true;
|
||||
%}
|
||||
|
||||
function data_available() : bool
|
||||
%{
|
||||
return flow_buffer_->data_available();
|
||||
%}
|
||||
|
||||
function consume_data() : bool
|
||||
%{
|
||||
flow_buffer_->NewFrame(0, false);
|
||||
return true;
|
||||
%}
|
||||
};
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
# $Id:$
|
||||
|
||||
# binpac analyzer representing the SSLv3 record layer
|
||||
#
|
||||
# This additional layering in the analyzer hierarchy is necessary due to
|
||||
# fragmentation that can be introduced in the SSL record layer.
|
||||
|
||||
%include binpac.pac
|
||||
%include bro.pac
|
||||
|
||||
analyzer SSLRecordLayer withcontext {
|
||||
analyzer : SSLRecordLayerAnalyzer;
|
||||
flow : SSLRecordLayerFlow;
|
||||
};
|
||||
|
||||
%include ssl-defs.pac
|
||||
|
||||
%extern{
|
||||
#include "ssl_pac.h"
|
||||
using binpac::SSL::SSLAnalyzer;
|
||||
%}
|
||||
|
||||
extern type const_bytestring;
|
||||
|
||||
|
||||
type SSLPDU = record {
|
||||
head0 : uint8;
|
||||
head1 : uint8;
|
||||
head2 : uint8;
|
||||
head3 : uint8;
|
||||
head4 : uint8;
|
||||
fragment : bytestring &restofdata;
|
||||
} &length = 5 + length, &byteorder = bigendian, &let {
|
||||
version : int =
|
||||
$context.analyzer.determine_ssl_version(head0, head1, head2);
|
||||
|
||||
length : int = case version of {
|
||||
UNKNOWN_VERSION -> 0;
|
||||
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
|
||||
default -> (head3 << 8) | head4;
|
||||
};
|
||||
|
||||
fw : bool = case version of {
|
||||
UNKNOWN_VERSION ->
|
||||
$context.analyzer.forward_record(const_bytestring(),
|
||||
UNKNOWN_OR_V2_ENCRYPTED, UNKNOWN_VERSION,
|
||||
$context.flow.is_orig)
|
||||
&& $context.flow.discard_data();
|
||||
|
||||
SSLv20 -> $context.analyzer.forward_v2_record(head2, head3, head4,
|
||||
fragment, $context.flow.is_orig);
|
||||
default -> $context.analyzer.forward_record(fragment, head0,
|
||||
(head1 << 8) | head2, $context.flow.is_orig);
|
||||
};
|
||||
};
|
||||
|
||||
# binpac-specific definitions
|
||||
|
||||
analyzer SSLRecordLayerAnalyzer {
|
||||
upflow = SSLRecordLayerFlow(true);
|
||||
downflow = SSLRecordLayerFlow(false);
|
||||
|
||||
%member{
|
||||
SSLAnalyzer* ssl_analyzer_;
|
||||
|
||||
int ssl_version_;
|
||||
int record_length_;
|
||||
%}
|
||||
|
||||
%init{
|
||||
ssl_analyzer_ = 0;
|
||||
ssl_version_ = UNKNOWN_VERSION;
|
||||
record_length_ = 0;
|
||||
%}
|
||||
|
||||
%eof{
|
||||
ssl_analyzer_->FlowEOF(true);
|
||||
ssl_analyzer_->FlowEOF(false);
|
||||
%}
|
||||
|
||||
function set_ssl_analyzer(a : SSLAnalyzer) : void
|
||||
%{ ssl_analyzer_ = a; %}
|
||||
|
||||
function ssl_version() : int %{ return ssl_version_; %}
|
||||
function record_length() : int %{ return record_length_; %}
|
||||
|
||||
function determine_ssl_version(head0 : uint8, head1 : uint8,
|
||||
head2 : uint8) : int
|
||||
%{
|
||||
if ( head0 >= 20 && head0 <= 23 &&
|
||||
head1 == 0x03 && head2 < 0x03 )
|
||||
// This is most probably SSL version 3.
|
||||
ssl_version_ = (head1 << 8) | head2;
|
||||
|
||||
else if ( head0 >= 128 && head2 < 5 && head2 != 3 )
|
||||
// Not very strong evidence, but we suspect
|
||||
// this to be SSLv2.
|
||||
ssl_version_ = SSLv20;
|
||||
|
||||
else
|
||||
ssl_version_ = UNKNOWN_VERSION;
|
||||
|
||||
return ssl_version_;
|
||||
%}
|
||||
|
||||
function forward_record(fragment : const_bytestring, type : int,
|
||||
version : uint16, is_orig : bool) : bool
|
||||
%{
|
||||
return ssl_analyzer_->next_record(fragment, type,
|
||||
version, is_orig);
|
||||
%}
|
||||
|
||||
function forward_v2_record(b1 : uint8, b2 : uint8, b3 : uint8,
|
||||
fragment : const_bytestring,
|
||||
is_orig : bool) : bool
|
||||
%{
|
||||
uint8* buffer = new uint8[2 + fragment.length()];
|
||||
|
||||
// Byte 1 is the record type.
|
||||
buffer[0] = b2;
|
||||
buffer[1] = b3;
|
||||
|
||||
memcpy(buffer + 2, fragment.begin(), fragment.length());
|
||||
const_bytestring bs(buffer, 2 + fragment.length());
|
||||
|
||||
bool ret = ssl_analyzer_->next_record(bs, 300 + b1, SSLv20,
|
||||
is_orig);
|
||||
delete [] buffer;
|
||||
return ret;
|
||||
%}
|
||||
};
|
||||
|
||||
flow SSLRecordLayerFlow(is_orig : bool) {
|
||||
flowunit = SSLPDU withcontext(connection, this);
|
||||
|
||||
function discard_data() : bool
|
||||
%{
|
||||
flow_buffer_->DiscardData();
|
||||
return true;
|
||||
%}
|
||||
};
|
|
@ -15,8 +15,10 @@ analyzer SSL withcontext {
|
|||
flow : SSLFlow;
|
||||
};
|
||||
|
||||
|
||||
%include ssl-defs.pac
|
||||
|
||||
%include ssl-protocol.pac
|
||||
%include ssl-analyzer.pac
|
||||
%include ssl-defs.pac
|
||||
|
||||
flow SSLFlow(is_orig : bool) {
|
||||
flowunit = SSLPDU(is_orig) withcontext(connection, this);
|
||||
};
|
||||
|
|
27
src/syslog-analyzer.pac
Normal file
27
src/syslog-analyzer.pac
Normal file
|
@ -0,0 +1,27 @@
|
|||
|
||||
connection Syslog_Conn(bro_analyzer: BroAnalyzer)
|
||||
{
|
||||
upflow = Syslog_Flow;
|
||||
downflow = Syslog_Flow;
|
||||
};
|
||||
|
||||
flow Syslog_Flow
|
||||
{
|
||||
datagram = Syslog_Message withcontext(connection, this);
|
||||
|
||||
function process_syslog_message(m: Syslog_Message): bool
|
||||
%{
|
||||
BifEvent::generate_syslog_message(connection()->bro_analyzer(),
|
||||
connection()->bro_analyzer()->Conn(),
|
||||
${m.PRI.facility},
|
||||
${m.PRI.severity},
|
||||
new StringVal(${m.msg}.length(), (const char*) ${m.msg}.begin())
|
||||
);
|
||||
return true;
|
||||
%}
|
||||
|
||||
};
|
||||
|
||||
refine typeattr Syslog_Message += &let {
|
||||
proc_syslog_message = $context.flow.process_syslog_message(this);
|
||||
};
|
15
src/syslog-protocol.pac
Normal file
15
src/syslog-protocol.pac
Normal file
|
@ -0,0 +1,15 @@
|
|||
type Syslog_Message = record {
|
||||
PRI: Syslog_Priority;
|
||||
msg: bytestring &restofdata;
|
||||
} &byteorder = littleendian;
|
||||
|
||||
type Syslog_Priority = record {
|
||||
lt : uint8 &check(lt == "<");
|
||||
val : RE/[[:digit:]]+/;
|
||||
gt : uint8 &check(gt == ">");
|
||||
} &let {
|
||||
val_length: int = sizeof(val) - 1;
|
||||
int_val: int = bytestring_to_int(val, 10);
|
||||
severity: int = (int_val & 0x07);
|
||||
facility: int = (int_val & 0x03f8) >> 3;
|
||||
};
|
10
src/syslog.pac
Normal file
10
src/syslog.pac
Normal file
|
@ -0,0 +1,10 @@
|
|||
%include binpac.pac
|
||||
%include bro.pac
|
||||
|
||||
analyzer Syslog withcontext {
|
||||
connection: Syslog_Conn;
|
||||
flow: Syslog_Flow;
|
||||
};
|
||||
|
||||
%include syslog-protocol.pac
|
||||
%include syslog-analyzer.pac
|
|
@ -52,33 +52,34 @@ enum rpc_status %{
|
|||
%}
|
||||
|
||||
module NFS3;
|
||||
enum proc_t %{ # NFSv3 procedures
|
||||
PROC_NULL = 0, # done
|
||||
PROC_GETATTR = 1, # done
|
||||
PROC_SETATTR = 2,
|
||||
PROC_LOOKUP = 3, # done
|
||||
PROC_ACCESS = 4,
|
||||
PROC_READLINK = 5, # done
|
||||
PROC_READ = 6, # done
|
||||
PROC_WRITE = 7, # done
|
||||
PROC_CREATE = 8, # partial
|
||||
PROC_MKDIR = 9, # partial
|
||||
PROC_SYMLINK = 10,
|
||||
PROC_MKNOD = 11,
|
||||
PROC_REMOVE = 12, # done
|
||||
PROC_RMDIR = 13, # done
|
||||
PROC_RENAME = 14,
|
||||
PROC_LINK = 15,
|
||||
PROC_READDIR = 16, # done
|
||||
PROC_READDIRPLUS = 17, # done
|
||||
PROC_FSSTAT = 18,
|
||||
PROC_FSINFO = 19,
|
||||
PROC_PATHCONF = 20,
|
||||
PROC_COMMIT = 21,
|
||||
PROC_END_OF_PROCS = 22,
|
||||
|
||||
enum proc_t %{ # NFSv3 procedures
|
||||
PROC_NULL = 0, # done
|
||||
PROC_GETATTR = 1, # done
|
||||
PROC_SETATTR = 2, # not implemented
|
||||
PROC_LOOKUP = 3, # done
|
||||
PROC_ACCESS = 4, # not implemented
|
||||
PROC_READLINK = 5, # done
|
||||
PROC_READ = 6, # done
|
||||
PROC_WRITE = 7, # done
|
||||
PROC_CREATE = 8, # partial
|
||||
PROC_MKDIR = 9, # partial
|
||||
PROC_SYMLINK = 10, # not implemented
|
||||
PROC_MKNOD = 11, # not implemented
|
||||
PROC_REMOVE = 12, # done
|
||||
PROC_RMDIR = 13, # done
|
||||
PROC_RENAME = 14, # not implemented
|
||||
PROC_LINK = 15, # not implemented
|
||||
PROC_READDIR = 16, # done
|
||||
PROC_READDIRPLUS = 17, # done
|
||||
PROC_FSSTAT = 18, # not implemented
|
||||
PROC_FSINFO = 19, # not implemented
|
||||
PROC_PATHCONF = 20, # not implemented
|
||||
PROC_COMMIT = 21, # not implemented
|
||||
PROC_END_OF_PROCS = 22, # not implemented
|
||||
%}
|
||||
|
||||
enum status_t %{ #NFSv3 return status
|
||||
enum status_t %{ # NFSv3 return status
|
||||
NFS3ERR_OK = 0,
|
||||
NFS3ERR_PERM = 1,
|
||||
NFS3ERR_NOENT = 2,
|
||||
|
@ -111,14 +112,13 @@ enum status_t %{ #NFSv3 return status
|
|||
NFS3ERR_UNKNOWN = 0xffffffff,
|
||||
%}
|
||||
|
||||
|
||||
enum file_type_t %{
|
||||
FTYPE_REG = 1,
|
||||
FTYPE_DIR = 2,
|
||||
FTYPE_BLK = 3,
|
||||
FTYPE_CHR = 4,
|
||||
FTYPE_LNK = 5,
|
||||
FTYPE_SOCK = 6,
|
||||
FTYPE_REG = 1,
|
||||
FTYPE_DIR = 2,
|
||||
FTYPE_BLK = 3,
|
||||
FTYPE_CHR = 4,
|
||||
FTYPE_LNK = 5,
|
||||
FTYPE_SOCK = 6,
|
||||
FTYPE_FIFO = 7,
|
||||
%}
|
||||
|
||||
|
@ -134,8 +134,8 @@ enum createmode_t %{
|
|||
EXCLUSIVE = 2,
|
||||
%}
|
||||
|
||||
# Decleare record types that we want to access from the C++
|
||||
# These are defined in bro.init
|
||||
# Decleare record types that we want to access from the even engine. These are
|
||||
# defined in bro.init.
|
||||
type info_t: record;
|
||||
type fattr_t: record;
|
||||
type diropargs_t: record;
|
||||
|
@ -153,7 +153,6 @@ type direntry_t: record;
|
|||
type direntry_vec_t: vector;
|
||||
type readdir_reply_t: record;
|
||||
|
||||
|
||||
type fsstat_t: record;
|
||||
|
||||
module Log;
|
||||
|
|
101
src/util.cc
101
src/util.cc
|
@ -344,15 +344,27 @@ template<class T> int atoi_n(int len, const char* s, const char** end, int base,
|
|||
template int atoi_n<int>(int len, const char* s, const char** end, int base, int& result);
|
||||
template int atoi_n<int64_t>(int len, const char* s, const char** end, int base, int64_t& result);
|
||||
|
||||
char* uitoa_n(uint64 value, char* str, int n, int base)
|
||||
char* uitoa_n(uint64 value, char* str, int n, int base, const char* prefix)
|
||||
{
|
||||
static char dig[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
assert(n);
|
||||
|
||||
int i = 0;
|
||||
uint64 v;
|
||||
char* p, *q;
|
||||
char c;
|
||||
|
||||
if ( prefix )
|
||||
{
|
||||
strncpy(str, prefix, n);
|
||||
str[n-1] = '\0';
|
||||
i += strlen(prefix);
|
||||
}
|
||||
|
||||
if ( i >= n )
|
||||
return str;
|
||||
|
||||
v = value;
|
||||
|
||||
do {
|
||||
|
@ -868,21 +880,45 @@ const char* bro_prefixes()
|
|||
return p;
|
||||
}
|
||||
|
||||
FILE* open_file(const char* filename, const char** full_filename)
|
||||
static const char* PACKAGE_LOADER = "__load__.bro";
|
||||
|
||||
// If filename is pointing to a directory that contains a file called
|
||||
// PACKAGE_LOADER, returns the files path. Otherwise returns filename itself.
|
||||
// In both cases, the returned string is newly allocated.
|
||||
static const char* check_for_dir(const char* filename, bool load_pkgs)
|
||||
{
|
||||
if ( load_pkgs && is_dir(filename) )
|
||||
{
|
||||
char init_filename_buf[1024];
|
||||
safe_snprintf(init_filename_buf, sizeof(init_filename_buf),
|
||||
"%s/%s", filename, PACKAGE_LOADER);
|
||||
|
||||
if ( access(init_filename_buf, R_OK) == 0 )
|
||||
return copy_string(init_filename_buf);
|
||||
}
|
||||
|
||||
return copy_string(filename);
|
||||
}
|
||||
|
||||
FILE* open_file(const char* filename, const char** full_filename, bool load_pkgs)
|
||||
{
|
||||
filename = check_for_dir(filename, load_pkgs);
|
||||
|
||||
if ( full_filename )
|
||||
*full_filename = copy_string(filename);
|
||||
|
||||
FILE* f = fopen(filename, "r");
|
||||
|
||||
delete [] filename;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
FILE* search_for_file(const char* filename, const char* ext,
|
||||
const char** full_filename)
|
||||
const char** full_filename, bool load_pkgs)
|
||||
{
|
||||
if ( filename[0] == '/' || filename[0] == '.' )
|
||||
return open_file(filename, full_filename);
|
||||
return open_file(filename, full_filename, load_pkgs);
|
||||
|
||||
char path[1024], full_filename_buf[1024];
|
||||
safe_strncpy(path, bro_path(), sizeof(path));
|
||||
|
@ -905,13 +941,12 @@ FILE* search_for_file(const char* filename, const char* ext,
|
|||
"%s/%s.%s", dir_beginning, filename, ext);
|
||||
if ( access(full_filename_buf, R_OK) == 0 &&
|
||||
! is_dir(full_filename_buf) )
|
||||
return open_file(full_filename_buf, full_filename);
|
||||
return open_file(full_filename_buf, full_filename, load_pkgs);
|
||||
|
||||
safe_snprintf(full_filename_buf, sizeof(full_filename_buf),
|
||||
"%s/%s", dir_beginning, filename);
|
||||
if ( access(full_filename_buf, R_OK) == 0 &&
|
||||
! is_dir(full_filename_buf) )
|
||||
return open_file(full_filename_buf, full_filename);
|
||||
if ( access(full_filename_buf, R_OK) == 0 )
|
||||
return open_file(full_filename_buf, full_filename, load_pkgs);
|
||||
|
||||
dir_beginning = ++dir_ending;
|
||||
}
|
||||
|
@ -1099,6 +1134,56 @@ int time_compare(struct timeval* tv_a, struct timeval* tv_b)
|
|||
return tv_a->tv_sec - tv_b->tv_sec;
|
||||
}
|
||||
|
||||
static uint64 uid_counter; // Counter for unique IDs.
|
||||
static uint64 uid_instance; // Instance ID, computed once.
|
||||
|
||||
uint64 calculate_unique_id()
|
||||
{
|
||||
if ( uid_instance == 0 )
|
||||
{
|
||||
// This is the first time we need a UID.
|
||||
|
||||
if ( ! have_random_seed() )
|
||||
{
|
||||
// If we don't need deterministic output (as
|
||||
// indicated by a set seed), we calculate the
|
||||
// instance ID by hashing something likely to be
|
||||
// globally unique.
|
||||
struct {
|
||||
char hostname[128];
|
||||
struct timeval time;
|
||||
pid_t pid;
|
||||
int rnd;
|
||||
} unique;
|
||||
|
||||
gethostname(unique.hostname, 128);
|
||||
unique.hostname[sizeof(unique.hostname)-1] = '\0';
|
||||
gettimeofday(&unique.time, 0);
|
||||
unique.pid = getpid();
|
||||
unique.rnd = bro_random();
|
||||
|
||||
uid_instance = HashKey::HashBytes(&unique, sizeof(unique));
|
||||
++uid_instance; // Now it's larger than zero.
|
||||
}
|
||||
|
||||
else
|
||||
// Generate determistic UIDs.
|
||||
uid_instance = 1;
|
||||
}
|
||||
|
||||
// Now calculate the unique ID.
|
||||
struct {
|
||||
uint64 counter;
|
||||
hash_t instance;
|
||||
} key;
|
||||
|
||||
key.counter = ++uid_counter;
|
||||
key.instance = uid_instance;
|
||||
|
||||
uint64_t h = HashKey::HashBytes(&key, sizeof(key));
|
||||
return h;
|
||||
}
|
||||
|
||||
void out_of_memory(const char* where)
|
||||
{
|
||||
fprintf( stderr, "bro: out of memory in %s.\n", where );
|
||||
|
|
|
@ -116,7 +116,7 @@ extern char* strcasestr(const char* s, const char* find);
|
|||
#endif
|
||||
extern const char* strpbrk_n(size_t len, const char* s, const char* charset);
|
||||
template<class T> int atoi_n(int len, const char* s, const char** end, int base, T& result);
|
||||
extern char* uitoa_n(uint64 value, char* str, int n, int base);
|
||||
extern char* uitoa_n(uint64 value, char* str, int n, int base, const char* prefix=0);
|
||||
int strstr_n(const int big_len, const unsigned char* big,
|
||||
const int little_len, const unsigned char* little);
|
||||
extern int fputs(int len, const char* s, FILE* fp);
|
||||
|
@ -195,7 +195,7 @@ extern int int_list_cmp(const void* v1, const void* v2);
|
|||
extern const char* bro_path();
|
||||
extern const char* bro_prefixes();
|
||||
extern FILE* search_for_file(const char* filename, const char* ext,
|
||||
const char** full_filename);
|
||||
const char** full_filename, bool load_pkgs);
|
||||
|
||||
// Renames the given file to a new temporary name, and opens a new file with
|
||||
// the original name. Returns new file or NULL on error. Inits rotate_info if
|
||||
|
@ -238,6 +238,10 @@ extern struct timeval double_to_timeval(double t);
|
|||
// Return > 0 if tv_a > tv_b, 0 if equal, < 0 if tv_a < tv_b.
|
||||
extern int time_compare(struct timeval* tv_a, struct timeval* tv_b);
|
||||
|
||||
// Returns an integer that's very likely to be unique, even across Bro
|
||||
// instances.
|
||||
extern uint64 calculate_unique_id();
|
||||
|
||||
// For now, don't use hash_maps - they're not fully portable.
|
||||
#if 0
|
||||
// Use for hash_map's string keys.
|
||||
|
|
1
testing/btest/Baseline/bifs.unique_id-rnd/count
Normal file
1
testing/btest/Baseline/bifs.unique_id-rnd/count
Normal file
|
@ -0,0 +1 @@
|
|||
6
|
3
testing/btest/Baseline/bifs.unique_id/out
Normal file
3
testing/btest/Baseline/bifs.unique_id/out
Normal file
|
@ -0,0 +1,3 @@
|
|||
A-UWkUyAuUGXf
|
||||
B-56gKBmhBBB6
|
||||
C-50da4BEzauh
|
|
@ -1 +1 @@
|
|||
18
|
||||
62
|
||||
|
|
|
@ -1,34 +1,78 @@
|
|||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.202, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.202, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.50, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.50, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=35634/tcp, resp_h=208.80.152.2, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=35634/tcp, resp_h=208.80.152.2, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=43927/udp, resp_h=141.142.2.2, resp_p=53/udp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=43927/udp, resp_h=141.142.2.2, resp_p=53/udp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=37676/udp, resp_h=141.142.2.2, resp_p=53/udp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=37676/udp, resp_h=141.142.2.2, resp_p=53/udp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=40526/udp, resp_h=141.142.2.2, resp_p=53/udp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=40526/udp, resp_h=141.142.2.2, resp_p=53/udp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=32902/udp, resp_h=141.142.2.2, resp_p=53/udp], nSEQzFk1LZc
|
||||
[orig_h=141.142.220.118, orig_p=32902/udp, resp_h=141.142.2.2, resp_p=53/udp], nSEQzFk1LZc
|
||||
[orig_h=141.142.220.118, orig_p=59816/udp, resp_h=141.142.2.2, resp_p=53/udp], rmXOq6wncn1
|
||||
[orig_h=141.142.220.118, orig_p=59816/udp, resp_h=141.142.2.2, resp_p=53/udp], rmXOq6wncn1
|
||||
[orig_h=141.142.220.118, orig_p=59714/udp, resp_h=141.142.2.2, resp_p=53/udp], 4YYJTjETe1i
|
||||
[orig_h=141.142.220.118, orig_p=59714/udp, resp_h=141.142.2.2, resp_p=53/udp], 4YYJTjETe1i
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=58206/udp, resp_h=141.142.2.2, resp_p=53/udp], R8BqVlcp23e
|
||||
[orig_h=141.142.220.118, orig_p=58206/udp, resp_h=141.142.2.2, resp_p=53/udp], R8BqVlcp23e
|
||||
[orig_h=141.142.220.118, orig_p=38911/udp, resp_h=141.142.2.2, resp_p=53/udp], duYdXg7bTa3
|
||||
[orig_h=141.142.220.118, orig_p=38911/udp, resp_h=141.142.2.2, resp_p=53/udp], duYdXg7bTa3
|
||||
[orig_h=141.142.220.118, orig_p=59746/udp, resp_h=141.142.2.2, resp_p=53/udp], yzqaQTU9DXe
|
||||
[orig_h=141.142.220.118, orig_p=59746/udp, resp_h=141.142.2.2, resp_p=53/udp], yzqaQTU9DXe
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=45000/udp, resp_h=141.142.2.2, resp_p=53/udp], N6rbUGwigQ7
|
||||
[orig_h=141.142.220.118, orig_p=45000/udp, resp_h=141.142.2.2, resp_p=53/udp], N6rbUGwigQ7
|
||||
[orig_h=141.142.220.118, orig_p=48479/udp, resp_h=141.142.2.2, resp_p=53/udp], 8b9q7qPtzhd
|
||||
[orig_h=141.142.220.118, orig_p=48479/udp, resp_h=141.142.2.2, resp_p=53/udp], 8b9q7qPtzhd
|
||||
[orig_h=141.142.220.118, orig_p=48128/udp, resp_h=141.142.2.2, resp_p=53/udp], KOdlL7sC9z2
|
||||
[orig_h=141.142.220.118, orig_p=48128/udp, resp_h=141.142.2.2, resp_p=53/udp], KOdlL7sC9z2
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=56056/udp, resp_h=141.142.2.2, resp_p=53/udp], FHu81uYujA9
|
||||
[orig_h=141.142.220.118, orig_p=56056/udp, resp_h=141.142.2.2, resp_p=53/udp], FHu81uYujA9
|
||||
[orig_h=141.142.220.118, orig_p=55092/udp, resp_h=141.142.2.2, resp_p=53/udp], 2M1wDTa0C7a
|
||||
[orig_h=141.142.220.118, orig_p=55092/udp, resp_h=141.142.2.2, resp_p=53/udp], 2M1wDTa0C7a
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], tpUWfNdSLE
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], tpUWfNdSLE
|
||||
[orig_h=141.142.220.44, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], ra1C6ZLut4b
|
||||
[orig_h=141.142.220.44, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], ra1C6ZLut4b
|
||||
[orig_h=141.142.220.226, orig_p=137/udp, resp_h=141.142.220.255, resp_p=137/udp], UElDH5b9qA5
|
||||
[orig_h=141.142.220.226, orig_p=137/udp, resp_h=141.142.220.255, resp_p=137/udp], UElDH5b9qA5
|
||||
[orig_h=141.142.220.226, orig_p=55131/udp, resp_h=224.0.0.252, resp_p=5355/udp], sO3mBXBav1h
|
||||
[orig_h=141.142.220.226, orig_p=55131/udp, resp_h=224.0.0.252, resp_p=5355/udp], sO3mBXBav1h
|
||||
[orig_h=141.142.220.226, orig_p=55671/udp, resp_h=224.0.0.252, resp_p=5355/udp], xAQqZE8Wdp4
|
||||
[orig_h=141.142.220.226, orig_p=55671/udp, resp_h=224.0.0.252, resp_p=5355/udp], xAQqZE8Wdp4
|
||||
[orig_h=141.142.220.238, orig_p=56641/udp, resp_h=141.142.220.255, resp_p=137/udp], zVecVnfOlsf
|
||||
[orig_h=141.142.220.238, orig_p=56641/udp, resp_h=141.142.220.255, resp_p=137/udp], zVecVnfOlsf
|
||||
|
|
|
@ -1,36 +1,80 @@
|
|||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.202, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.202, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], UWkUyAuUGXf
|
||||
[orig_h=141.142.220.50, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.50, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], 56gKBmhBBB6
|
||||
[orig_h=141.142.220.118, orig_p=35634/tcp, resp_h=208.80.152.2, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=35634/tcp, resp_h=208.80.152.2, resp_p=80/tcp], 50da4BEzauh
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=48649/tcp, resp_h=208.80.152.118, resp_p=80/tcp], WUjEZFOdSS
|
||||
[orig_h=141.142.220.118, orig_p=43927/udp, resp_h=141.142.2.2, resp_p=53/udp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=43927/udp, resp_h=141.142.2.2, resp_p=53/udp], ecqdozAET6c
|
||||
[orig_h=141.142.220.118, orig_p=37676/udp, resp_h=141.142.2.2, resp_p=53/udp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=37676/udp, resp_h=141.142.2.2, resp_p=53/udp], tdkrEYpj5ja
|
||||
[orig_h=141.142.220.118, orig_p=40526/udp, resp_h=141.142.2.2, resp_p=53/udp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=40526/udp, resp_h=141.142.2.2, resp_p=53/udp], F5XgctwO3Vl
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=32902/udp, resp_h=141.142.2.2, resp_p=53/udp], nSEQzFk1LZc
|
||||
[orig_h=141.142.220.118, orig_p=32902/udp, resp_h=141.142.2.2, resp_p=53/udp], nSEQzFk1LZc
|
||||
[orig_h=141.142.220.118, orig_p=59816/udp, resp_h=141.142.2.2, resp_p=53/udp], rmXOq6wncn1
|
||||
[orig_h=141.142.220.118, orig_p=59816/udp, resp_h=141.142.2.2, resp_p=53/udp], rmXOq6wncn1
|
||||
[orig_h=141.142.220.118, orig_p=59714/udp, resp_h=141.142.2.2, resp_p=53/udp], 4YYJTjETe1i
|
||||
[orig_h=141.142.220.118, orig_p=59714/udp, resp_h=141.142.2.2, resp_p=53/udp], 4YYJTjETe1i
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=58206/udp, resp_h=141.142.2.2, resp_p=53/udp], R8BqVlcp23e
|
||||
[orig_h=141.142.220.118, orig_p=58206/udp, resp_h=141.142.2.2, resp_p=53/udp], R8BqVlcp23e
|
||||
[orig_h=141.142.220.118, orig_p=38911/udp, resp_h=141.142.2.2, resp_p=53/udp], duYdXg7bTa3
|
||||
[orig_h=141.142.220.118, orig_p=38911/udp, resp_h=141.142.2.2, resp_p=53/udp], duYdXg7bTa3
|
||||
[orig_h=141.142.220.118, orig_p=59746/udp, resp_h=141.142.2.2, resp_p=53/udp], yzqaQTU9DXe
|
||||
[orig_h=141.142.220.118, orig_p=59746/udp, resp_h=141.142.2.2, resp_p=53/udp], yzqaQTU9DXe
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=45000/udp, resp_h=141.142.2.2, resp_p=53/udp], N6rbUGwigQ7
|
||||
[orig_h=141.142.220.118, orig_p=45000/udp, resp_h=141.142.2.2, resp_p=53/udp], N6rbUGwigQ7
|
||||
[orig_h=141.142.220.118, orig_p=48479/udp, resp_h=141.142.2.2, resp_p=53/udp], 8b9q7qPtzhd
|
||||
[orig_h=141.142.220.118, orig_p=48479/udp, resp_h=141.142.2.2, resp_p=53/udp], 8b9q7qPtzhd
|
||||
[orig_h=141.142.220.118, orig_p=48128/udp, resp_h=141.142.2.2, resp_p=53/udp], KOdlL7sC9z2
|
||||
[orig_h=141.142.220.118, orig_p=48128/udp, resp_h=141.142.2.2, resp_p=53/udp], KOdlL7sC9z2
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=56056/udp, resp_h=141.142.2.2, resp_p=53/udp], FHu81uYujA9
|
||||
[orig_h=141.142.220.118, orig_p=56056/udp, resp_h=141.142.2.2, resp_p=53/udp], FHu81uYujA9
|
||||
[orig_h=141.142.220.118, orig_p=55092/udp, resp_h=141.142.2.2, resp_p=53/udp], 2M1wDTa0C7a
|
||||
[orig_h=141.142.220.118, orig_p=55092/udp, resp_h=141.142.2.2, resp_p=53/udp], 2M1wDTa0C7a
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=49997/tcp, resp_h=208.80.152.3, resp_p=80/tcp], UZkBBvjF0r8
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49996/tcp, resp_h=208.80.152.3, resp_p=80/tcp], svqqNKN9CFj
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=49998/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OldlyspNIr7
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=50000/tcp, resp_h=208.80.152.3, resp_p=80/tcp], j5w2LueK8Ti
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=49999/tcp, resp_h=208.80.152.3, resp_p=80/tcp], OPM7xFSDNw3
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=50001/tcp, resp_h=208.80.152.3, resp_p=80/tcp], hvOo97vj60k
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.118, orig_p=35642/tcp, resp_h=208.80.152.2, resp_p=80/tcp], OKiJdtzKWPk
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], tpUWfNdSLE
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], tpUWfNdSLE
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], tpUWfNdSLE
|
||||
[orig_h=141.142.220.235, orig_p=6705/tcp, resp_h=173.192.163.128, resp_p=80/tcp], tpUWfNdSLE
|
||||
[orig_h=141.142.220.44, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], ra1C6ZLut4b
|
||||
[orig_h=141.142.220.44, orig_p=5353/udp, resp_h=224.0.0.251, resp_p=5353/udp], ra1C6ZLut4b
|
||||
[orig_h=141.142.220.226, orig_p=137/udp, resp_h=141.142.220.255, resp_p=137/udp], UElDH5b9qA5
|
||||
[orig_h=141.142.220.226, orig_p=137/udp, resp_h=141.142.220.255, resp_p=137/udp], UElDH5b9qA5
|
||||
[orig_h=141.142.220.226, orig_p=55131/udp, resp_h=224.0.0.252, resp_p=5355/udp], sO3mBXBav1h
|
||||
[orig_h=141.142.220.226, orig_p=55131/udp, resp_h=224.0.0.252, resp_p=5355/udp], sO3mBXBav1h
|
||||
[orig_h=141.142.220.226, orig_p=55671/udp, resp_h=224.0.0.252, resp_p=5355/udp], xAQqZE8Wdp4
|
||||
[orig_h=141.142.220.226, orig_p=55671/udp, resp_h=224.0.0.252, resp_p=5355/udp], xAQqZE8Wdp4
|
||||
[orig_h=141.142.220.238, orig_p=56641/udp, resp_h=141.142.220.255, resp_p=137/udp], zVecVnfOlsf
|
||||
[orig_h=141.142.220.238, orig_p=56641/udp, resp_h=141.142.220.255, resp_p=137/udp], zVecVnfOlsf
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue