diff options
author | Guy Harris <guy@alum.mit.edu> | 2019-01-06 16:08:00 -0800 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2019-01-06 16:08:00 -0800 |
commit | 9956745b7df4e9ffa6bc97f411779167bde69e70 (patch) | |
tree | a26b8b4d0d843957ac9d51419f2ab4069debfc40 | |
parent | 9ece72846ad2a5d493703e6be20e21ccf88e9460 (diff) | |
parent | e6796ccd1c07e806a49f8821480a566dee85c1ee (diff) |
Merge branch 'master' of https://github.com/rixed/libpcap into rixed-master
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | aclocal.m4 | 278 | ||||
-rw-r--r-- | config.h.in | 3 | ||||
-rwxr-xr-x | configure | 247 | ||||
-rw-r--r-- | configure.ac | 36 | ||||
-rw-r--r-- | pcap-int.h | 7 | ||||
-rw-r--r-- | pcap-rpcap.c | 391 | ||||
-rw-r--r-- | pcap.c | 30 | ||||
-rw-r--r-- | pcap/pcap.h | 5 | ||||
-rw-r--r-- | rpcap-protocol.c | 6 | ||||
-rw-r--r-- | rpcap-protocol.h | 3 | ||||
-rw-r--r-- | rpcapd/Makefile.in | 2 | ||||
-rw-r--r-- | rpcapd/daemon.c | 269 | ||||
-rw-r--r-- | rpcapd/daemon.h | 10 | ||||
-rw-r--r-- | rpcapd/rpcapd.c | 188 | ||||
-rw-r--r-- | sockutils.c | 38 | ||||
-rw-r--r-- | sockutils.h | 10 | ||||
-rw-r--r-- | sslutils.c | 227 | ||||
-rw-r--r-- | sslutils.h | 70 |
19 files changed, 1543 insertions, 279 deletions
diff --git a/Makefile.in b/Makefile.in index 9f4683e4..031a7790 100644 --- a/Makefile.in +++ b/Makefile.in @@ -366,6 +366,8 @@ EXTRA_DIST = \ rpcapd/win32-svc.h \ sockutils.c \ sockutils.h \ + sslutils.c \ + sslutils.h \ scanner.l \ testprogs/CMakeLists.txt \ testprogs/Makefile.in \ @@ -977,3 +977,281 @@ AC_DEFUN(AC_LBL_LIBRARY_NET, [ # DLPI needs putmsg under HPUX so test for -lstr while we're at it AC_SEARCH_LIBS(putmsg, str) ]) + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +dnl serial 11 (pkg-config-0.29) +dnl +dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>. +dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com> +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +dnl 02111-1307, USA. +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a +dnl configuration script generated by Autoconf, you may include it under +dnl the same distribution terms that you use for the rest of that +dnl program. + +dnl PKG_PREREQ(MIN-VERSION) +dnl ----------------------- +dnl Since: 0.29 +dnl +dnl Verify that the version of the pkg-config macros are at least +dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's +dnl installed version of pkg-config, this checks the developer's version +dnl of pkg.m4 when generating configure. +dnl +dnl To ensure that this macro is defined, also add: +dnl m4_ifndef([PKG_PREREQ], +dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) +dnl +dnl See the "Since" comment for each macro you use to see what version +dnl of the macros you require. +m4_defun([PKG_PREREQ], +[m4_define([PKG_MACROS_VERSION], [0.29]) +m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) +])dnl PKG_PREREQ + +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) +dnl ---------------------------------- +dnl Since: 0.16 +dnl +dnl Search for the pkg-config tool and set the PKG_CONFIG variable to +dnl first found in the path. Checks that the version of pkg-config found +dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is +dnl used since that's the first version where most current features of +dnl pkg-config existed. +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])dnl PKG_PROG_PKG_CONFIG + +dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------------------------------- +dnl Since: 0.18 +dnl +dnl Check to see whether a particular set of modules exists. Similar to +dnl PKG_CHECK_MODULES(), but does not set variables or print errors. +dnl +dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +dnl only at the first occurence in configure.ac, so if the first place +dnl it's called might be skipped (such as if it is within an "if", you +dnl have to call PKG_CHECK_EXISTS manually +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +dnl --------------------------------------------- +dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting +dnl pkg_failed based on the result. +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])dnl _PKG_CONFIG + +dnl _PKG_SHORT_ERRORS_SUPPORTED +dnl --------------------------- +dnl Internal check to see if pkg-config supports short errors. +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])dnl _PKG_SHORT_ERRORS_SUPPORTED + + +dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl -------------------------------------------------------------- +dnl Since: 0.4.0 +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES might not happen, you should be sure to include an +dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])dnl PKG_CHECK_MODULES + + +dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------------------- +dnl Since: 0.29 +dnl +dnl Checks for existence of MODULES and gathers its build flags with +dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags +dnl and VARIABLE-PREFIX_LIBS from --libs. +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to +dnl include an explicit call to PKG_PROG_PKG_CONFIG in your +dnl configure.ac. +AC_DEFUN([PKG_CHECK_MODULES_STATIC], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +_save_PKG_CONFIG=$PKG_CONFIG +PKG_CONFIG="$PKG_CONFIG --static" +PKG_CHECK_MODULES($@) +PKG_CONFIG=$_save_PKG_CONFIG[]dnl +])dnl PKG_CHECK_MODULES_STATIC + + +dnl PKG_INSTALLDIR([DIRECTORY]) +dnl ------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable pkgconfigdir as the location where a module +dnl should install pkg-config .pc files. By default the directory is +dnl $libdir/pkgconfig, but the default can be changed by passing +dnl DIRECTORY. The user can override through the --with-pkgconfigdir +dnl parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_INSTALLDIR + + +dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) +dnl -------------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable noarch_pkgconfigdir as the location where a +dnl module should install arch-independent pkg-config .pc files. By +dnl default the directory is $datadir/pkgconfig, but the default can be +dnl changed by passing DIRECTORY. The user can override through the +dnl --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_NOARCH_INSTALLDIR + + +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR + diff --git a/config.h.in b/config.h.in index c554d25d..e09bc607 100644 --- a/config.h.in +++ b/config.h.in @@ -141,6 +141,9 @@ /* Define to 1 if you have the <net/raw.h> header file. */ #undef HAVE_NET_RAW_H +/* Use OpenSSL */ +#undef HAVE_OPENSSL + /* if there's an os_proto.h for this platform, to use additional prototypes */ #undef HAVE_OS_PROTO_H @@ -671,6 +671,11 @@ YACC LEXLIB LEX_OUTPUT_ROOT LEX +OPENSSL_LIBS +OPENSSL_CFLAGS +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG PCAP_SUPPORT_PACKET_RING VALGRINDTEST_SRC LIBOBJS @@ -774,6 +779,11 @@ LDFLAGS LIBS CPPFLAGS CPP +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +OPENSSL_CFLAGS +OPENSSL_LIBS YACC YFLAGS' @@ -1448,6 +1458,15 @@ Some influential environment variables: CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if you have headers in a nonstandard directory <include dir> CPP C preprocessor + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + OPENSSL_CFLAGS + C compiler flags for OPENSSL, overriding pkg-config + OPENSSL_LIBS + linker flags for OPENSSL, overriding pkg-config YACC The `Yet Another Compiler Compiler' implementation to use. Defaults to the first program found out of: `bison -y', `byacc', `yacc'. @@ -7727,10 +7746,212 @@ _ACEOF fi + # + # Optionally, we may want to forward packets over SSL: + # + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for OPENSSL" >&5 +$as_echo_n "checking for OPENSSL... " >&6; } + +if test -n "$OPENSSL_CFLAGS"; then + pkg_cv_OPENSSL_CFLAGS="$OPENSSL_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_CFLAGS=`$PKG_CONFIG --cflags "openssl" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$OPENSSL_LIBS"; then + pkg_cv_OPENSSL_LIBS="$OPENSSL_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"openssl\""; } >&5 + ($PKG_CONFIG --exists --print-errors "openssl") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_OPENSSL_LIBS=`$PKG_CONFIG --libs "openssl" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "openssl" 2>&1` + else + OPENSSL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "openssl" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$OPENSSL_PKG_ERRORS" >&5 + + { $as_echo "$as_me:${as_lineno-$LINENO}: No openssl detected" >&5 +$as_echo "$as_me: No openssl detected" >&6;} +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: No openssl detected" >&5 +$as_echo "$as_me: No openssl detected" >&6;} +else + OPENSSL_CFLAGS=$pkg_cv_OPENSSL_CFLAGS + OPENSSL_LIBS=$pkg_cv_OPENSSL_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define HAVE_OPENSSL 1" >>confdefs.h + + CFLAGS="$CFLAGS $OPENSSL_CFLAGS" + LIBS="$LIBS $OPENSSL_LIBS" + HAVE_OPENSSL="yes" +fi + $as_echo "#define ENABLE_REMOTE /**/" >>confdefs.h - MSRC="$MSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c" + MSRC="$MSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c sslutils.c" BUILD_RPCAPD=build-rpcapd INSTALL_RPCAPD=install-rpcapd ;; @@ -8225,8 +8446,20 @@ if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - V_LIB_CCOPT_FAT="-arch x86_64 -arch i386" - V_LIB_LDFLAGS_FAT="-arch x86_64 -arch i386" + V_LIB_CCOPT_FAT="-arch x86_64" + V_LIB_LDFLAGS_FAT="-arch x86_64" + + # + # OpenSSL installation on macOS seems + # to install only the libs for 64-bit + # x86 - at least that's what Brew does: + # only configure 32-bit builds if we + # don't have OpenSSL. + # + if test "$HAVE_OPENSSL" != yes; then + V_LIB_CCOPT_FAT="$V_LIB_CCOPT_FAT -arch i386" + V_LIB_LDFLAGS_FAT="$V_LIB_LDFLAGS_FAT -arch i386" + fi else @@ -8260,6 +8493,14 @@ $as_echo "$as_me: WARNING: Compiling for 32-bit x86 gives an error; try installi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$save_CFLAGS" + if test "$HAVE_OPENSSL" = yes; then + # + # If all else fails, look for OpenSSL in + # /usr/local/opt. + # + CFLAGS="$CFLAGS -I/usr/local/opt/openssl/include" + LIBS="$LIBS -L/usr/local/opt/openssl/lib" + fi ;; esac fi diff --git a/configure.ac b/configure.ac index 55a79474..069d9f8a 100644 --- a/configure.ac +++ b/configure.ac @@ -1487,9 +1487,19 @@ yes) AC_MSG_RESULT(yes) #include <sys/socket.h> ]) + # + # Optionally, we may want to forward packets over SSL: + # + PKG_CHECK_MODULES([OPENSSL], [openssl], + [AC_DEFINE([HAVE_OPENSSL], [1], [Use OpenSSL]) + CFLAGS="$CFLAGS $OPENSSL_CFLAGS" + LIBS="$LIBS $OPENSSL_LIBS" + HAVE_OPENSSL="yes"], + AC_MSG_NOTICE(No openssl detected)) + AC_DEFINE(ENABLE_REMOTE,, [Define to 1 if remote packet capture is to be supported]) - MSRC="$MSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c" + MSRC="$MSRC pcap-new.c pcap-rpcap.c rpcap-protocol.c sockutils.c sslutils.c" BUILD_RPCAPD=build-rpcapd INSTALL_RPCAPD=install-rpcapd ;; @@ -1738,8 +1748,20 @@ darwin*) [return 0;], [ AC_MSG_RESULT(yes) - V_LIB_CCOPT_FAT="-arch x86_64 -arch i386" - V_LIB_LDFLAGS_FAT="-arch x86_64 -arch i386" + V_LIB_CCOPT_FAT="-arch x86_64" + V_LIB_LDFLAGS_FAT="-arch x86_64" + + # + # OpenSSL installation on macOS seems + # to install only the libs for 64-bit + # x86 - at least that's what Brew does: + # only configure 32-bit builds if we + # don't have OpenSSL. + # + if test "$HAVE_OPENSSL" != yes; then + V_LIB_CCOPT_FAT="$V_LIB_CCOPT_FAT -arch i386" + V_LIB_LDFLAGS_FAT="$V_LIB_LDFLAGS_FAT -arch i386" + fi ], [ AC_MSG_RESULT(no) @@ -1767,6 +1789,14 @@ darwin*) esac ]) CFLAGS="$save_CFLAGS" + if test "$HAVE_OPENSSL" = yes; then + # + # If all else fails, look for OpenSSL in + # /usr/local/opt. + # + CFLAGS="$CFLAGS -I/usr/local/opt/openssl/include" + LIBS="$LIBS -L/usr/local/opt/openssl/lib" + fi ;; esac fi @@ -529,6 +529,13 @@ int install_bpf_program(pcap_t *, struct bpf_program *); int pcap_strcasecmp(const char *, const char *); +/* + * Internal interface for pcap_parsesrcstr with the additional bit of + * information regarding SSL support (rpcap:// vs rpcaps://) + */ +int pcap_parsesrcstr_ex(const char *source, int *type, char *host, char *port, + char *name, unsigned char *uses_ssl, char *errbuf); + #ifdef YYDEBUG extern int pcap_debug; #endif diff --git a/pcap-rpcap.c b/pcap-rpcap.c index 16dcbf8f..6e45c433 100644 --- a/pcap-rpcap.c +++ b/pcap-rpcap.c @@ -46,6 +46,10 @@ #include "rpcap-protocol.h" #include "pcap-rpcap.h" +#ifdef HAVE_OPENSSL +#include "sslutils.h" +#endif + /* * This file contains the pcap module for capturing from a remote machine's * interfaces using the RPCAP protocol. @@ -83,6 +87,7 @@ struct activehosts { struct sockaddr_storage host; SOCKET sockctrl; + SSL *ssl; uint8 protocol_version; struct activehosts *next; }; @@ -97,6 +102,7 @@ static struct activehosts *activeHosts; * pcap_remoteact_cleanup() for more details. */ static SOCKET sockmain; +static SSL *ssl_main; /* * Private data for capturing remotely using the rpcap protocol. @@ -111,11 +117,13 @@ struct pcap_rpcap { SOCKET rmt_sockctrl; /* socket ID of the socket used for the control connection */ SOCKET rmt_sockdata; /* socket ID of the socket used for the data connection */ + SSL *ctrl_ssl, *data_ssl; /* optional transport of rmt_sockctrl and rmt_sockdata via TLS */ int rmt_flags; /* we have to save flags, since they are passed by the pcap_open_live(), but they are used by the pcap_startcapture() */ int rmt_capstarted; /* 'true' if the capture is already started (needed to knoe if we have to call the pcap_startcapture() */ char *currentfilter; /* Pointer to a buffer (allocated at run-time) that stores the current filter. Needed when flag PCAP_OPENFLAG_NOCAPTURE_RPCAP is turned on. */ uint8 protocol_version; /* negotiated protocol version */ + uint8 uses_ssl; /* User asked for rpcaps scheme */ unsigned int TotNetDrops; /* keeps the number of packets that have been dropped by the network */ @@ -155,15 +163,15 @@ static void pcap_save_current_filter_rpcap(pcap_t *fp, const char *filter); static int pcap_setfilter_rpcap(pcap_t *fp, struct bpf_program *prog); static int pcap_setsampling_remote(pcap_t *fp); static int pcap_startcapture_remote(pcap_t *fp); -static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf); -static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char *errbuf); -static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_header *header, char *errbuf); -static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf); -static int rpcap_process_msg_header(SOCKET sock, uint8 ver, uint8 request_type, struct rpcap_header *header, char *errbuf); -static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, char *errbuf); -static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf); -static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf); -static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size); +static int rpcap_sendauth(SOCKET sock, SSL *, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf); +static int rpcap_recv_msg_header(SOCKET sock, SSL *, struct rpcap_header *header, char *errbuf); +static int rpcap_check_msg_ver(SOCKET sock, SSL *, uint8 expected_ver, struct rpcap_header *header, char *errbuf); +static int rpcap_check_msg_type(SOCKET sock, SSL *, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf); +static int rpcap_process_msg_header(SOCKET sock, SSL *, uint8 ver, uint8 request_type, struct rpcap_header *header, char *errbuf); +static int rpcap_recv(SOCKET sock, SSL *, void *buffer, size_t toread, uint32 *plen, char *errbuf); +static void rpcap_msg_err(SOCKET sockctrl, SSL *, uint32 plen, char *remote_errbuf); +static int rpcap_discard(SOCKET sock, SSL *, uint32 len, char *errbuf); +static int rpcap_read_packet_msg(struct pcap_rpcap const *, pcap_t *p, size_t size); /**************************************************** * * @@ -376,7 +384,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch struct rpcap_pkthdr *net_pkt_header; /* header of the packet, from the message */ u_char *net_pkt_data; /* packet data from the message */ uint32 plen; - int retval; /* generic return value */ + int retval = 0; /* generic return value */ int msglen; /* Structures needed for the select() call */ @@ -390,27 +398,36 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch tv.tv_sec = p->opt.timeout / 1000; tv.tv_usec = (suseconds_t)((p->opt.timeout - tv.tv_sec * 1000) * 1000); - /* Watch out sockdata to see if it has input */ - FD_ZERO(&rfds); +#ifdef HAVE_OPENSSL + /* Check if we still have bytes available in the last decoded TLS record. + * If that's the case, we know SSL_read will not block. */ + retval = pr->data_ssl && SSL_pending(pr->data_ssl) > 0; +#endif + if (! retval) + { + /* Watch out sockdata to see if it has input */ + FD_ZERO(&rfds); - /* - * 'fp->rmt_sockdata' has always to be set before calling the select(), - * since it is cleared by the select() - */ - FD_SET(pr->rmt_sockdata, &rfds); + /* + * 'fp->rmt_sockdata' has always to be set before calling the select(), + * since it is cleared by the select() + */ + FD_SET(pr->rmt_sockdata, &rfds); - retval = select((int) pr->rmt_sockdata + 1, &rfds, NULL, NULL, &tv); - if (retval == -1) - { -#ifndef _WIN32 - if (errno == EINTR) + retval = select((int) pr->rmt_sockdata + 1, &rfds, NULL, NULL, &tv); + + if (retval == -1) { - /* Interrupted. */ - return 0; - } +#ifndef _WIN32 + if (errno == EINTR) + { + /* Interrupted. */ + return 0; + } #endif - sock_geterror("select(): ", p->errbuf, PCAP_ERRBUF_SIZE); - return -1; + sock_geterror("select(): ", p->errbuf, PCAP_ERRBUF_SIZE); + return -1; + } } /* There is no data waiting, so return '0' */ @@ -428,7 +445,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch if (pr->rmt_flags & PCAP_OPENFLAG_DATATX_UDP) { /* Read the entire message from the network */ - msglen = sock_recv_dgram(pr->rmt_sockdata, p->buffer, + msglen = sock_recv_dgram(pr->rmt_sockdata, pr->data_ssl, p->buffer, p->bufsize, p->errbuf, PCAP_ERRBUF_SIZE); if (msglen == -1) { @@ -472,8 +489,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch * The size we should get is the size of the * packet header. */ - status = rpcap_read_packet_msg(pr->rmt_sockdata, p, - sizeof(struct rpcap_header)); + status = rpcap_read_packet_msg(pr, p, sizeof(struct rpcap_header)); if (status == -1) { /* Network error. */ @@ -505,8 +521,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch "Server sent us a message larger than the largest expected packet message"); return -1; } - status = rpcap_read_packet_msg(pr->rmt_sockdata, p, - sizeof(struct rpcap_header) + plen); + status = rpcap_read_packet_msg(pr, p, sizeof(struct rpcap_header) + plen); if (status == -1) { /* Network error. */ @@ -535,7 +550,7 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch /* * Did the server specify the version we negotiated? */ - if (rpcap_check_msg_ver(pr->rmt_sockdata, pr->protocol_version, + if (rpcap_check_msg_ver(pr->rmt_sockdata, pr->data_ssl, pr->protocol_version, header, p->errbuf) == -1) { return 0; /* Return 'no packets received' */ @@ -715,7 +730,7 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * we're closing this pcap_t, and have no place to report * the error. No reply is sent to this message. */ - (void)sock_send(pr->rmt_sockctrl, (char *)&header, + (void)sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), NULL, 0); } else @@ -728,7 +743,7 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * as we're closing this pcap_t, and have no place to * report the error. */ - if (sock_send(pr->rmt_sockctrl, (char *)&header, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), NULL, 0) == 0) { /* @@ -736,11 +751,11 @@ static void pcap_cleanup_rpcap(pcap_t *fp) * as we're closing this pcap_t, and have no * place to report the error. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_ENDCAP_REQ, &header, NULL) == 0) { - (void)rpcap_discard(pr->rmt_sockctrl, + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, NULL); } } @@ -748,14 +763,31 @@ static void pcap_cleanup_rpcap(pcap_t *fp) if (pr->rmt_sockdata) { +#ifdef HAVE_OPENSSL + if (pr->data_ssl) + { + SSL_free(pr->data_ssl); // Has to be done before the socket is closed + pr->data_ssl = NULL; + } +#endif sock_close(pr->rmt_sockdata, NULL, 0); pr->rmt_sockdata = 0; } if ((!active) && (pr->rmt_sockctrl)) + { +#ifdef HAVE_OPENSSL + if (pr->ctrl_ssl) + { + SSL_free(pr->ctrl_ssl); + pr->ctrl_ssl = NULL; + } +#endif sock_close(pr->rmt_sockctrl, NULL, 0); + } pr->rmt_sockctrl = 0; + pr->ctrl_ssl = NULL; if (pr->currentfilter) { @@ -880,19 +912,19 @@ static struct pcap_stat *rpcap_stats_rpcap(pcap_t *p, struct pcap_stat *ps, int RPCAP_MSG_STATS_REQ, 0, 0); /* Send the PCAP_STATS command */ - if (sock_send(pr->rmt_sockctrl, (char *)&header, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&header, sizeof(struct rpcap_header), p->errbuf, PCAP_ERRBUF_SIZE) < 0) return NULL; /* Unrecoverable network error */ /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_STATS_REQ, &header, p->errbuf) == -1) return NULL; /* Error */ plen = header.plen; /* Read the reply body */ - if (rpcap_recv(pr->rmt_sockctrl, (char *)&netstats, + if (rpcap_recv(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&netstats, sizeof(struct rpcap_stats), &plen, p->errbuf) == -1) goto error; @@ -909,7 +941,7 @@ static struct pcap_stat *rpcap_stats_rpcap(pcap_t *p, struct pcap_stat *ps, int #endif /* _WIN32 */ /* Discard the rest of the message. */ - if (rpcap_discard(pr->rmt_sockctrl, plen, p->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, p->errbuf) == -1) goto error_nodiscard; return ps; @@ -920,7 +952,7 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(pr->rmt_sockctrl, plen, NULL); + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: return NULL; @@ -1040,6 +1072,10 @@ static int pcap_startcapture_remote(pcap_t *fp) int sockbufsize = 0; uint32 server_sockbufsize; + // Take the opportunity to clear pr->data_ssl before any goto error, + // as it seems pr->priv is not zeroed after its malloced. + pr->data_ssl = NULL; + /* * Let's check if sampling has been required. * If so, let's set it first @@ -1179,18 +1215,18 @@ static int pcap_startcapture_remote(pcap_t *fp) if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, &fp->fcode)) goto error_nodiscard; - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_STARTCAP_REQ, &header, fp->errbuf) == -1) goto error_nodiscard; plen = header.plen; - if (rpcap_recv(pr->rmt_sockctrl, (char *)&startcapreply, + if (rpcap_recv(pr->rmt_sockctrl, pr->ctrl_ssl, (char *)&startcapreply, sizeof(struct rpcap_startcapreply), &plen, fp->errbuf) == -1) goto error; @@ -1249,6 +1285,14 @@ static int pcap_startcapture_remote(pcap_t *fp) /* Let's save the socket of the data connection */ pr->rmt_sockdata = sockdata; +#ifdef HAVE_OPENSSL + if (pr->uses_ssl) + { + pr->data_ssl = ssl_promotion(0, sockdata, fp->errbuf, PCAP_ERRBUF_SIZE); + if (! pr->data_ssl) goto error; + } +#endif + /* * Set the size of the socket buffer for the data socket. * It has the same size as the local capture buffer used @@ -1336,7 +1380,7 @@ static int pcap_startcapture_remote(pcap_t *fp) fp->cc = 0; /* Discard the rest of the message. */ - if (rpcap_discard(pr->rmt_sockctrl, plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, fp->errbuf) == -1) goto error_nodiscard; /* @@ -1376,14 +1420,31 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(pr->rmt_sockctrl, plen, NULL); + (void)rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: +#ifdef HAVE_OPENSSL + if (pr->data_ssl) + { + SSL_free(pr->data_ssl); // Have to be done before the socket is closed + pr->data_ssl = NULL; + } +#endif + if ((sockdata) && (sockdata != -1)) /* we can be here because sockdata said 'error' */ sock_close(sockdata, NULL, 0); if (!active) + { +#ifdef HAVE_OPENSSL + if (pr->ctrl_ssl) + { + SSL_free(pr->ctrl_ssl); + pr->ctrl_ssl = NULL; + } +#endif sock_close(pr->rmt_sockctrl, NULL, 0); + } if (addrinfo != NULL) freeaddrinfo(addrinfo); @@ -1516,19 +1577,19 @@ static int pcap_updatefilter_remote(pcap_t *fp, struct bpf_program *prog) if (pcap_pack_bpffilter(fp, &sendbuf[sendbufidx], &sendbufidx, prog)) return -1; - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_UPDATEFILTER_REQ, &header, fp->errbuf) == -1) return -1; /* * It shouldn't have any contents; discard it if it does. */ - if (rpcap_discard(pr->rmt_sockctrl, header.plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, fp->errbuf) == -1) return -1; return 0; @@ -1761,19 +1822,19 @@ static int pcap_setsampling_remote(pcap_t *fp) sampling_pars->method = (uint8)fp->rmt_samp.method; sampling_pars->value = (uint16)htonl(fp->rmt_samp.value); - if (sock_send(pr->rmt_sockctrl, sendbuf, sendbufidx, fp->errbuf, + if (sock_send(pr->rmt_sockctrl, pr->ctrl_ssl, sendbuf, sendbufidx, fp->errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->protocol_version, + if (rpcap_process_msg_header(pr->rmt_sockctrl, pr->ctrl_ssl, pr->protocol_version, RPCAP_MSG_SETSAMPLING_REQ, &header, fp->errbuf) == -1) return -1; /* * It shouldn't have any contents; discard it if it does. */ - if (rpcap_discard(pr->rmt_sockctrl, header.plen, fp->errbuf) == -1) + if (rpcap_discard(pr->rmt_sockctrl, pr->ctrl_ssl, header.plen, fp->errbuf) == -1) return -1; return 0; @@ -1808,7 +1869,7 @@ static int pcap_setsampling_remote(pcap_t *fp) * \return '0' if everything is fine, '-1' for an error. For errors, * an error message string is returned in the 'errbuf' variable. */ -static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) +static int rpcap_doauth(SOCKET sockctrl, SSL *ssl, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) { int status; @@ -1818,7 +1879,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, * First try with the maximum version number we support. */ *ver = RPCAP_MAX_VERSION; - status = rpcap_sendauth(sockctrl, ver, auth, errbuf); + status = rpcap_sendauth(sockctrl, ssl, ver, auth, errbuf); if (status == 0) { // @@ -1839,7 +1900,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, * support that version. *ver has been set to that version; try * authenticating again with that version. */ - status = rpcap_sendauth(sockctrl, ver, auth, errbuf); + status = rpcap_sendauth(sockctrl, ssl, ver, auth, errbuf); if (status == 0) { // @@ -1889,7 +1950,7 @@ static int rpcap_doauth(SOCKET sockctrl, uint8 *ver, struct pcap_rmtauth *auth, * support, or '-1' for other errors. For errors, an error message string * is returned in the 'errbuf' variable. */ -static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) +static int rpcap_sendauth(SOCKET sock, SSL *ssl, uint8 *ver, struct pcap_rmtauth *auth, char *errbuf) { char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data that has to be sent is buffered */ int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ @@ -1987,14 +2048,14 @@ static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, ch rpauth->slen2 = htons(rpauth->slen2); } - if (sock_send(sock, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) + if (sock_send(sock, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; /* Receive the reply */ - if (rpcap_recv_msg_header(sock, &header, errbuf) == -1) + if (rpcap_recv_msg_header(sock, ssl, &header, errbuf) == -1) return -1; - if (rpcap_check_msg_type(sock, RPCAP_MSG_AUTH_REQ, &header, + if (rpcap_check_msg_type(sock, ssl, RPCAP_MSG_AUTH_REQ, &header, &errcode, errbuf) == -1) { /* Error message - or something else, which is a protocol error. */ @@ -2039,7 +2100,7 @@ static int rpcap_sendauth(SOCKET sock, uint8 *ver, struct pcap_rmtauth *auth, ch * * Discard the rest of it. */ - if (rpcap_discard(sock, header.plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header.plen, errbuf) == -1) return -1; return 0; @@ -2110,6 +2171,7 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim struct activehosts *activeconn; /* active connection, if there is one */ int error; /* '1' if rpcap_remoteact_getsock returned an error */ SOCKET sockctrl; + SSL *ssl = NULL; uint8 protocol_version; /* negotiated protocol version */ int active; uint32 plen; @@ -2153,12 +2215,22 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim pr = fp->priv; pr->rmt_flags = flags; + // Also, SSL is not supported with UDP at the moment, so if the user + // asks for both we'd better bail out now: + if (pr->uses_ssl && (pr->rmt_flags & PCAP_OPENFLAG_DATATX_UDP)) + { + pcap_close(fp); + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "SSL not supported with UDP forward of remote packets"); + return NULL; + } + /* * determine the type of the source (NULL, file, local, remote) * You must have a valid source string even if we're in active mode, because otherwise * the call to the following function will fail. */ - if (pcap_parsesrcstr(fp->opt.device, &retval, host, ctrlport, iface, errbuf) == -1) + if (pcap_parsesrcstr_ex(fp->opt.device, &retval, host, ctrlport, iface, &pr->uses_ssl, errbuf) == -1) { pcap_close(fp); return NULL; @@ -2185,12 +2257,14 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim activeconn = rpcap_remoteact_getsock(host, &error, errbuf); if (activeconn != NULL) { + active = 1; sockctrl = activeconn->sockctrl; + ssl = activeconn->ssl; protocol_version = activeconn->protocol_version; - active = 1; } else { + active = 0; // Must be set before jumping to error struct addrinfo hints; /* temp, needed to open a socket connection */ struct addrinfo *addrinfo; /* temp, needed to open a socket connection */ @@ -2239,15 +2313,29 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim /* addrinfo is no longer used */ freeaddrinfo(addrinfo); - if (rpcap_doauth(sockctrl, &protocol_version, auth, errbuf) == -1) + if (pr->uses_ssl) { - sock_close(sockctrl, NULL, 0); +#ifdef HAVE_OPENSSL + ssl = ssl_promotion(0, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + pcap_close(fp); + return NULL; + } +#else + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "No TLS support"); pcap_close(fp); return NULL; +#endif } - active = 0; + + if (rpcap_doauth(sockctrl, ssl, &protocol_version, auth, errbuf) == -1) + goto error_nodiscard; } + /* All good so far, save the ssl handler */ + ssl_main = ssl; + /* * Now it's time to start playing with the RPCAP protocol * RPCAP open command: create the request message @@ -2263,28 +2351,29 @@ pcap_t *pcap_open_rpcap(const char *source, int snaplen, int flags, int read_tim RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) goto error_nodiscard; - if (sock_send(sockctrl, sendbuf, sendbufidx, errbuf, + if (sock_send(sockctrl, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(sockctrl, protocol_version, + if (rpcap_process_msg_header(sockctrl, ssl, protocol_version, RPCAP_MSG_OPEN_REQ, &header, errbuf) == -1) goto error_nodiscard; plen = header.plen; /* Read the reply body */ - if (rpcap_recv(sockctrl, (char *)&openreply, + if (rpcap_recv(sockctrl, ssl, (char *)&openreply, sizeof(struct rpcap_openreply), &plen, errbuf) == -1) goto error; /* Discard the rest of the message, if there is any. */ - if (rpcap_discard(sockctrl, plen, errbuf) == -1) + if (rpcap_discard(sockctrl, ssl, plen, errbuf) == -1) goto error_nodiscard; /* Set proper fields into the pcap_t struct */ fp->linktype = ntohl(openreply.linktype); pr->rmt_sockctrl = sockctrl; + pr->ctrl_ssl = ssl; pr->protocol_version = protocol_version; pr->rmt_clientside = 1; @@ -2316,11 +2405,16 @@ error: * We already reported an error; if this gets an error, just * drive on. */ - (void)rpcap_discard(sockctrl, plen, NULL); + (void)rpcap_discard(sockctrl, pr->ctrl_ssl, plen, NULL); error_nodiscard: if (!active) + { +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Have to be done before the socket is closed +#endif sock_close(sockctrl, NULL, 0); + } pcap_close(fp); return NULL; @@ -2348,6 +2442,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i int error; /* '1' if rpcap_remoteact_getsock returned an error */ uint8 protocol_version; /* protocol version */ SOCKET sockctrl; /* socket descriptor of the control connection */ + SSL *ssl = NULL; /* optional SSL handler for sockctrl */ uint32 plen; struct rpcap_header header; /* structure that keeps the general header of the rpcap protocol */ int i, j; /* temp variables */ @@ -2355,6 +2450,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i int active; /* 'true' if we the other end-party is in active mode */ int type; char host[PCAP_BUF_SIZE], port[PCAP_BUF_SIZE]; + uint8 uses_ssl; char tmpstring[PCAP_BUF_SIZE + 1]; /* Needed to convert names and descriptions from 'old' syntax to the 'new' one */ pcap_if_t *lastdev; /* Last device in the pcap_if_t list */ pcap_if_t *dev; /* Device we're adding to the pcap_if_t list */ @@ -2364,7 +2460,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i lastdev = NULL; /* Retrieve the needed data for getting adapter list */ - if (pcap_parsesrcstr(source, &type, host, port, NULL, errbuf) == -1) + if (pcap_parsesrcstr_ex(source, &type, host, port, NULL, &uses_ssl, errbuf) == -1) return -1; /* Warning: this call can be the first one called by the user. */ @@ -2376,12 +2472,14 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i activeconn = rpcap_remoteact_getsock(host, &error, errbuf); if (activeconn != NULL) { + active = 1; sockctrl = activeconn->sockctrl; + ssl = activeconn->ssl; protocol_version = activeconn->protocol_version; - active = 1; } else { + active = 0; struct addrinfo hints; /* temp variable needed to resolve hostnames into to socket representation */ struct addrinfo *addrinfo; /* temp variable needed to resolve hostnames into to socket representation */ @@ -2423,24 +2521,42 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i freeaddrinfo(addrinfo); addrinfo = NULL; - if (rpcap_doauth(sockctrl, &protocol_version, auth, errbuf) == -1) + if (uses_ssl) + { +#ifdef HAVE_OPENSSL + ssl = ssl_promotion(0, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + sock_close(sockctrl, NULL, 0); + return -1; + } +#else + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "No TLS support"); + sock_close(sockctrl, NULL, 0);; + return -1; +#endif + } + + if (rpcap_doauth(sockctrl, ssl, &protocol_version, auth, errbuf) == -1) { +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Must be done before the socket is closed +#endif sock_close(sockctrl, NULL, 0); return -1; } - active = 0; } /* RPCAP findalldevs command */ rpcap_createhdr(&header, protocol_version, RPCAP_MSG_FINDALLIF_REQ, 0, 0); - if (sock_send(sockctrl, (char *)&header, sizeof(struct rpcap_header), + if (sock_send(sockctrl, ssl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) < 0) goto error_nodiscard; /* Receive and process the reply message header. */ - if (rpcap_process_msg_header(sockctrl, protocol_version, + if (rpcap_process_msg_header(sockctrl, ssl, protocol_version, RPCAP_MSG_FINDALLIF_REQ, &header, errbuf) == -1) goto error_nodiscard; @@ -2460,7 +2576,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i tmpstring2[PCAP_BUF_SIZE] = 0; /* receive the findalldevs structure from remote host */ - if (rpcap_recv(sockctrl, (char *)&findalldevs_if, + if (rpcap_recv(sockctrl, ssl, (char *)&findalldevs_if, sizeof(struct rpcap_findalldevs_if), &plen, errbuf) == -1) goto error; @@ -2509,7 +2625,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i } /* Retrieve adapter name */ - if (rpcap_recv(sockctrl, tmpstring, + if (rpcap_recv(sockctrl, ssl, tmpstring, findalldevs_if.namelen, &plen, errbuf) == -1) goto error; @@ -2542,7 +2658,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i } /* Retrieve adapter description */ - if (rpcap_recv(sockctrl, tmpstring, + if (rpcap_recv(sockctrl, ssl, tmpstring, findalldevs_if.desclen, &plen, errbuf) == -1) goto error; @@ -2575,7 +2691,7 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i struct rpcap_findalldevs_ifaddr ifaddr; /* Retrieve the interface addresses */ - if (rpcap_recv(sockctrl, (char *)&ifaddr, + if (rpcap_recv(sockctrl, ssl, (char *)&ifaddr, sizeof(struct rpcap_findalldevs_ifaddr), &plen, errbuf) == -1) goto error; @@ -2649,13 +2765,16 @@ pcap_findalldevs_ex_remote(const char *source, struct pcap_rmtauth *auth, pcap_i } /* Discard the rest of the message. */ - if (rpcap_discard(sockctrl, plen, errbuf) == 1) + if (rpcap_discard(sockctrl, ssl, plen, errbuf) == 1) goto error_nodiscard; /* Control connection has to be closed only in case the remote machine is in passive mode */ if (!active) { /* DO not send RPCAP_CLOSE, since we did not open a pcap_t; no need to free resources */ +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Has to be done before the socket is closed +#endif if (sock_close(sockctrl, errbuf, PCAP_ERRBUF_SIZE)) return -1; } @@ -2679,12 +2798,17 @@ error: * * Checks if all the data has been read; if not, discard the data in excess */ - (void) rpcap_discard(sockctrl, plen, NULL); + (void) rpcap_discard(sockctrl, ssl, plen, NULL); error_nodiscard: /* Control connection has to be closed only in case the remote machine is in passive mode */ if (!active) + { +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Has to be done before the socket is closed +#endif sock_close(sockctrl, NULL, 0); + } /* To avoid inconsistencies in the number of sock_init() */ sock_cleanup(); @@ -2702,7 +2826,7 @@ error_nodiscard: * to implement; we provide some APIs for it that work only with rpcap. */ -SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, char *errbuf) +SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, struct pcap_rmtauth *auth, int uses_ssl, char *errbuf) { /* socket-related variables */ struct addrinfo hints; /* temporary struct to keep settings needed to open the new socket */ @@ -2710,6 +2834,7 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * struct sockaddr_storage from; /* generic sockaddr_storage variable */ socklen_t fromlen; /* keeps the length of the sockaddr_storage variable */ SOCKET sockctrl; /* keeps the main socket identifier */ + SSL *ssl = NULL; /* Optional SSL handler for sockctrl */ uint8 protocol_version; /* negotiated protocol version */ struct activehosts *temp, *prev; /* temp var needed to scan he host list chain */ @@ -2770,11 +2895,31 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * return (SOCKET)-2; } + /* Promote to SSL early before any error message may be sent */ + if (uses_ssl) + { +#ifdef HAVE_OPENSSL + ssl = ssl_promotion(0, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + sock_close(sockctrl, NULL, 0); + return (SOCKET)-1; + } +#else + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "No TLS support"); + sock_close(sockctrl, NULL, 0); + return (SOCKET)-1; +#endif + } + /* Get the numeric for of the name of the connecting host */ if (getnameinfo((struct sockaddr *) &from, fromlen, connectinghost, RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST)) { sock_geterror("getnameinfo(): ", errbuf, PCAP_ERRBUF_SIZE); - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } @@ -2782,7 +2927,10 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * /* checks if the connecting host is among the ones allowed */ if (sock_check_hostlist((char *)hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0) { - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } @@ -2790,10 +2938,13 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * /* * Send authentication to the remote machine. */ - if (rpcap_doauth(sockctrl, &protocol_version, auth, errbuf) == -1) + if (rpcap_doauth(sockctrl, ssl, &protocol_version, auth, errbuf) == -1) { /* Unrecoverable error. */ - rpcap_senderror(sockctrl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-3; } @@ -2830,13 +2981,17 @@ SOCKET pcap_remoteact_accept(const char *address, const char *port, const char * { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); - rpcap_senderror(sockctrl, protocol_version, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); + rpcap_senderror(sockctrl, ssl, protocol_version, PCAP_ERR_REMOTEACCEPT, errbuf, NULL); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); return (SOCKET)-1; } memcpy(&temp->host, &from, fromlen); temp->sockctrl = sockctrl; + temp->ssl = ssl; temp->protocol_version = protocol_version; temp->next = NULL; @@ -2883,7 +3038,7 @@ int pcap_remoteact_close(const char *host, char *errbuf) * Don't check for errors, since we're * just cleaning up. */ - if (sock_send(temp->sockctrl, + if (sock_send(temp->sockctrl, temp->ssl, (char *)&header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) < 0) @@ -2892,12 +3047,18 @@ int pcap_remoteact_close(const char *host, char *errbuf) * Let that error be the one we * report. */ +#ifdef HAVE_OPENSSL + if (temp->ssl) SSL_free(temp->ssl); +#endif (void)sock_close(temp->sockctrl, NULL, 0); status = -1; } else { +#ifdef HAVE_OPENSSL + if (temp->ssl) SSL_free(temp->ssl); +#endif if (sock_close(temp->sockctrl, errbuf, PCAP_ERRBUF_SIZE) == -1) status = -1; @@ -2949,6 +3110,13 @@ void pcap_remoteact_cleanup(void) sock_cleanup(); } +# ifdef HAVE_OPENSSL + if (ssl_main) + { + SSL_free(ssl_main); + ssl_main = NULL; + } +# endif } int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) @@ -2998,11 +3166,11 @@ int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf) /* * Receive the header of a message. */ -static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char *errbuf) +static int rpcap_recv_msg_header(SOCKET sock, SSL *ssl, struct rpcap_header *header, char *errbuf) { int nrecv; - nrecv = sock_recv(sock, (char *) header, sizeof(struct rpcap_header), + nrecv = sock_recv(sock, ssl, (char *) header, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nrecv == -1) @@ -3018,7 +3186,7 @@ static int rpcap_recv_msg_header(SOCKET sock, struct rpcap_header *header, char * Make sure the protocol version of a received message is what we were * expecting. */ -static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_header *header, char *errbuf) +static int rpcap_check_msg_ver(SOCKET sock, SSL *ssl, uint8 expected_ver, struct rpcap_header *header, char *errbuf) { /* * Did the server specify the version we negotiated? @@ -3028,7 +3196,7 @@ static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_hea /* * Discard the rest of the message. */ - if (rpcap_discard(sock, header->plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header->plen, errbuf) == -1) return -1; /* @@ -3049,7 +3217,7 @@ static int rpcap_check_msg_ver(SOCKET sock, uint8 expected_ver, struct rpcap_hea * Check the message type of a received message, which should either be * the expected message type or RPCAP_MSG_ERROR. */ -static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf) +static int rpcap_check_msg_type(SOCKET sock, SSL *ssl, uint8 request_type, struct rpcap_header *header, uint16 *errcode, char *errbuf) { const char *request_type_string; const char *msg_type_string; @@ -3064,7 +3232,7 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he * Hand that error back to our caller. */ *errcode = ntohs(header->value); - rpcap_msg_err(sock, header->plen, errbuf); + rpcap_msg_err(sock, ssl, header->plen, errbuf); return -1; } @@ -3083,7 +3251,7 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he /* * Discard the rest of the message. */ - if (rpcap_discard(sock, header->plen, errbuf) == -1) + if (rpcap_discard(sock, ssl, header->plen, errbuf) == -1) return -1; /* @@ -3119,11 +3287,11 @@ static int rpcap_check_msg_type(SOCKET sock, uint8 request_type, struct rpcap_he /* * Receive and process the header of a message. */ -static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 request_type, struct rpcap_header *header, char *errbuf) +static int rpcap_process_msg_header(SOCKET sock, SSL *ssl, uint8 expected_ver, uint8 request_type, struct rpcap_header *header, char *errbuf) { uint16 errcode; - if (rpcap_recv_msg_header(sock, header, errbuf) == -1) + if (rpcap_recv_msg_header(sock, ssl, header, errbuf) == -1) { /* Network error. */ return -1; @@ -3132,13 +3300,13 @@ static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 reque /* * Did the server specify the version we negotiated? */ - if (rpcap_check_msg_ver(sock, expected_ver, header, errbuf) == -1) + if (rpcap_check_msg_ver(sock, ssl, expected_ver, header, errbuf) == -1) return -1; /* * Check the message type. */ - return rpcap_check_msg_type(sock, request_type, header, + return rpcap_check_msg_type(sock, ssl, request_type, header, &errcode, errbuf); } @@ -3151,7 +3319,7 @@ static int rpcap_process_msg_header(SOCKET sock, uint8 expected_ver, uint8 reque * Returns 0 on success, logs a message and returns -1 on a network * error. */ -static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, char *errbuf) +static int rpcap_recv(SOCKET sock, SSL *ssl, void *buffer, size_t toread, uint32 *plen, char *errbuf) { int nread; @@ -3161,7 +3329,7 @@ static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, ch pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); return -1; } - nread = sock_recv(sock, buffer, toread, + nread = sock_recv(sock, ssl, buffer, toread, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -3174,7 +3342,7 @@ static int rpcap_recv(SOCKET sock, void *buffer, size_t toread, uint32 *plen, ch /* * This handles the RPCAP_MSG_ERROR message. */ -static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) +static void rpcap_msg_err(SOCKET sockctrl, SSL *ssl, uint32 plen, char *remote_errbuf) { char errbuf[PCAP_ERRBUF_SIZE]; @@ -3184,7 +3352,7 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) * Message is too long; just read as much of it as we * can into the buffer provided, and discard the rest. */ - if (sock_recv(sockctrl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, + if (sock_recv(sockctrl, ssl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -3201,7 +3369,7 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) /* * Throw away the rest. */ - (void)rpcap_discard(sockctrl, plen - (PCAP_ERRBUF_SIZE - 1), remote_errbuf); + (void)rpcap_discard(sockctrl, ssl, plen - (PCAP_ERRBUF_SIZE - 1), remote_errbuf); } else if (plen == 0) { @@ -3210,7 +3378,7 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) } else { - if (sock_recv(sockctrl, remote_errbuf, plen, + if (sock_recv(sockctrl, ssl, remote_errbuf, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -3232,11 +3400,11 @@ static void rpcap_msg_err(SOCKET sockctrl, uint32 plen, char *remote_errbuf) * Returns 0 on success, logs a message and returns -1 on a network * error. */ -static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf) +static int rpcap_discard(SOCKET sock, SSL *ssl, uint32 len, char *errbuf) { if (len != 0) { - if (sock_discard(sock, len, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_discard(sock, ssl, len, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. return -1; @@ -3249,7 +3417,7 @@ static int rpcap_discard(SOCKET sock, uint32 len, char *errbuf) * Read bytes into the pcap_t's buffer until we have the specified * number of bytes read or we get an error or interrupt indication. */ -static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size) +static int rpcap_read_packet_msg(struct pcap_rpcap const *rp, pcap_t *p, size_t size) { u_char *bp; int cc; @@ -3268,9 +3436,10 @@ static int rpcap_read_packet_msg(SOCKET sock, pcap_t *p, size_t size) * We haven't read all of the packet header yet. * Read what remains, which could be all of it. */ - bytes_read = sock_recv(sock, bp, size - cc, + bytes_read = sock_recv(rp->rmt_sockdata, rp->data_ssl, bp, size - cc, SOCK_RECEIVEALL_NO|SOCK_EOF_IS_ERROR, p->errbuf, PCAP_ERRBUF_SIZE); + if (bytes_read == -1) { /* @@ -1599,7 +1599,8 @@ pcap_parse_source(const char *source, char **schemep, char **userinfop, * * XXX - %-escaping? */ - if (pcap_strcasecmp(scheme, "rpcap") == 0 && + if ((pcap_strcasecmp(scheme, "rpcap") == 0 || + pcap_strcasecmp(scheme, "rpcaps") == 0) && strchr(colonp + 3, '/') == NULL) { /* * Local device. @@ -1869,8 +1870,8 @@ pcap_createsrcstr(char *source, int type, const char *host, const char *port, } int -pcap_parsesrcstr(const char *source, int *type, char *host, char *port, - char *name, char *errbuf) +pcap_parsesrcstr_ex(const char *source, int *type, char *host, char *port, + char *name, unsigned char *uses_ssl, char *errbuf) { char *scheme, *tmpuserinfo, *tmphost, *tmpport, *tmppath; @@ -1881,6 +1882,8 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, *port = '\0'; if (name) *name = '\0'; + if (uses_ssl) + *uses_ssl = 0; /* Parse the source string */ if (pcap_parse_source(source, &scheme, &tmpuserinfo, &tmphost, @@ -1906,12 +1909,20 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, return (0); } - if (strcmp(scheme, "rpcap") == 0) { + int is_rpcap = 0; + if (strcmp(scheme, "rpcaps") == 0) { + is_rpcap = 1; + if (uses_ssl) *uses_ssl = 1; + } else if (strcmp(scheme, "rpcap") == 0) { + is_rpcap = 1; + } + + if (is_rpcap) { /* - * rpcap:// + * rpcap[s]:// * * pcap_parse_source() has already handled the case of - * rpcap://device + * rpcap[s]://device */ if (host && tmphost) { if (tmpuserinfo) @@ -1965,6 +1976,13 @@ pcap_parsesrcstr(const char *source, int *type, char *host, char *port, free(scheme); return (0); } + +int +pcap_parsesrcstr(const char *source, int *type, char *host, char *port, + char *name, char *errbuf) +{ + return pcap_parsesrcstr_ex(source, type, host, port, name, NULL, errbuf); +} #endif pcap_t * diff --git a/pcap/pcap.h b/pcap/pcap.h index 8b5ecba5..571c88c0 100644 --- a/pcap/pcap.h +++ b/pcap/pcap.h @@ -631,6 +631,9 @@ PCAP_API const char *pcap_lib_version(void); * - rpcap:// [lists all local adapters] * - rpcap://host:port/ [lists the devices available on a remote host] * + * In all the above, "rpcaps://" can be substituted for "rpcap://" to enable + * SSL (if it has been compiled in). + * * Referring to the 'host' and 'port' parameters, they can be either numeric or literal. Since * IPv6 is fully supported, these are the allowed formats: * @@ -947,7 +950,7 @@ PCAP_API struct pcap_samp *pcap_setsampling(pcap_t *p); PCAP_API SOCKET pcap_remoteact_accept(const char *address, const char *port, const char *hostlist, char *connectinghost, - struct pcap_rmtauth *auth, char *errbuf); + struct pcap_rmtauth *auth, int uses_ssl, char *errbuf); PCAP_API int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf); PCAP_API int pcap_remoteact_close(const char *host, char *errbuf); diff --git a/rpcap-protocol.c b/rpcap-protocol.c index 692f7c5c..0cdc0ba3 100644 --- a/rpcap-protocol.c +++ b/rpcap-protocol.c @@ -61,6 +61,8 @@ * * \param sock: the socket we are currently using. * + * \param ssl: if compiled with openssl, the optional ssl handler to use with the above socket. + * * \param ver: the protocol version we want to put in the reply. * * \param errcode: a integer which tells the other party the type of error @@ -78,7 +80,7 @@ * error message is returned in the 'errbuf' variable. */ int -rpcap_senderror(SOCKET sock, uint8 ver, unsigned short errcode, const char *error, char *errbuf) +rpcap_senderror(SOCKET sock, SSL *ssl, uint8 ver, unsigned short errcode, const char *error, char *errbuf) { char sendbuf[RPCAP_NETBUF_SIZE]; /* temporary buffer in which data to be sent is buffered */ int sendbufidx = 0; /* index which keeps the number of bytes currently buffered */ @@ -99,7 +101,7 @@ rpcap_senderror(SOCKET sock, uint8 ver, unsigned short errcode, const char *erro RPCAP_NETBUF_SIZE, SOCKBUF_BUFFERIZE, errbuf, PCAP_ERRBUF_SIZE)) return -1; - if (sock_send(sock, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) + if (sock_send(sock, ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) < 0) return -1; return 0; diff --git a/rpcap-protocol.h b/rpcap-protocol.h index 7598a0ae..ad66b755 100644 --- a/rpcap-protocol.h +++ b/rpcap-protocol.h @@ -445,9 +445,10 @@ struct rpcap_sampling *********************************************************/ #include "sockutils.h" +#include "sslutils.h" extern void rpcap_createhdr(struct rpcap_header *header, uint8 ver, uint8 type, uint16 value, uint32 length); extern const char *rpcap_msg_type_string(uint8 type); -extern int rpcap_senderror(SOCKET sock, uint8 ver, uint16 errcode, const char *error, char *errbuf); +extern int rpcap_senderror(SOCKET sock, SSL *ssl, uint8 ver, uint16 errcode, const char *error, char *errbuf); #endif diff --git a/rpcapd/Makefile.in b/rpcapd/Makefile.in index 1b530d5a..659f0a7c 100644 --- a/rpcapd/Makefile.in +++ b/rpcapd/Makefile.in @@ -84,7 +84,7 @@ SRC = daemon.c \ log-stderr.c \ rpcapd.c -OBJ = $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o +OBJ = $(SRC:.c=.o) ../rpcap-protocol.o ../sockutils.o ../fmtutils.o ../sslutils.o PUBHDR = HDR = $(PUBHDR) log.h diff --git a/rpcapd/daemon.c b/rpcapd/daemon.c index e30eeef1..12b555ec 100644 --- a/rpcapd/daemon.c +++ b/rpcapd/daemon.c @@ -64,6 +64,11 @@ #include "daemon.h" #include "log.h" +#ifdef HAVE_OPENSSL +#include <openssl/ssl.h> +#include "sslutils.h" +#endif + #define RPCAP_TIMEOUT_INIT 90 /* Initial timeout for RPCAP connections (default: 90 sec) */ #define RPCAP_TIMEOUT_RUNTIME 180 /* Run-time timeout for RPCAP connections (default: 3 min) */ #define RPCAP_SUSPEND_WRONGAUTH 1 /* If the authentication is wrong, stops 1 sec before accepting a new auth message */ @@ -73,6 +78,7 @@ struct daemon_slpars { SOCKET sockctrl_in; //!< SOCKET ID of the input side of the control connection SOCKET sockctrl_out; //!< SOCKET ID of the output side of the control connection + SSL *ssl; //!< Optional SSL handler for the controlling sockets uint8 protocol_version; //!< negotiated protocol version int isactive; //!< Not null if the daemon has to run in active mode int nullAuthAllowed; //!< '1' if we permit NULL authentication, '0' otherwise @@ -84,6 +90,7 @@ struct daemon_slpars struct session { SOCKET sockctrl_out; SOCKET sockdata; + SSL *ctrl_ssl, *data_ssl; // optional SSL handlers for sockctrl_out and sockdata. uint8 protocol_version; pcap_t *fp; unsigned int TotCapt; @@ -107,18 +114,18 @@ struct thread_handle { }; // Locally defined functions -static int daemon_msg_err(SOCKET sockctrl_in, uint32 plen); +static int daemon_msg_err(SOCKET sockctrl_in, SSL *, uint32 plen); static int daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen); static int daemon_AuthUserPwd(char *username, char *password, char *errbuf); static int daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen); static int daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_t sourcelen); -static int daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param); +static int daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param, int uses_ssl); static int daemon_msg_endcap_req(struct daemon_slpars *pars, struct session *session, struct thread_handle *threaddata); static int daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, uint32 plen); -static int daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *plenp, char *errbuf); +static int daemon_unpackapplyfilter(SOCKET sockctrl_in, SSL *, struct session *session, uint32 *plenp, char *errbuf); static int daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 plen, struct pcap_stat *stats, unsigned int svrcapt); @@ -131,12 +138,13 @@ static unsigned __stdcall daemon_thrdatamain(void *ptr); static void *daemon_thrdatamain(void *ptr); #endif -static int rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp); -static int rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf); -static int rpcapd_discard(SOCKET sock, uint32 len); +static int rpcapd_recv_msg_header(SOCKET sock, SSL *, struct rpcap_header *headerp); +static int rpcapd_recv(SOCKET sock, SSL *, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf); +static int rpcapd_discard(SOCKET sock, SSL *, uint32 len); +static void session_close(struct session *); int -daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nullAuthAllowed) +daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, SSL *ssl, int isactive, int nullAuthAllowed, int uses_ssl) { struct daemon_slpars pars; // service loop parameters char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed @@ -167,6 +175,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // Set parameters structure pars.sockctrl_in = sockctrl_in; pars.sockctrl_out = sockctrl_out; + pars.ssl = ssl; pars.protocol_version = 0; // not yet known pars.isactive = isactive; // active mode pars.nullAuthAllowed = nullAuthAllowed; @@ -217,7 +226,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu if (retval == -1) { sock_geterror("select failed: ", errmsgbuf, PCAP_ERRBUF_SIZE); - if (rpcap_senderror(pars.sockctrl_out, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -226,7 +235,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // So, this was a fake connection. Drop it down if (retval == 0) { - if (rpcap_senderror(pars.sockctrl_out, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, 0, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } @@ -235,7 +244,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // // Read the message header from the client. // - nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, &header); + nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, pars.ssl, &header); if (nrecv == -1) { // Fatal error. @@ -291,7 +300,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // reply_version = RPCAP_MAX_VERSION; } - if (rpcap_senderror(pars.sockctrl_out, reply_version, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, reply_version, PCAP_ERR_WRONGVER, "RPCAP version number mismatch", errbuf) == -1) { @@ -301,7 +310,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Network error. goto end; @@ -345,7 +354,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // Discard the rest of the message, if // there is anything more. // - (void)rpcapd_discard(pars.sockctrl_in, plen); + (void)rpcapd_discard(pars.sockctrl_in, pars.ssl, plen); // We're done with this client. goto end; @@ -357,7 +366,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // an error message rather than a "let // me log in" message, indicating that // we're not allowed to connect to them? - (void)daemon_msg_err(pars.sockctrl_in, plen); + (void)daemon_msg_err(pars.sockctrl_in, pars.ssl, plen); goto end; case RPCAP_MSG_FINDALLIF_REQ: @@ -380,7 +389,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message of type %u sent before authentication was completed", header.type); } - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -388,7 +397,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Network error. goto end; @@ -416,7 +425,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); } - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -424,7 +433,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -436,7 +445,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // Unknown message type. // pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -444,7 +453,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -496,7 +505,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu if (retval == -1) { sock_geterror("select failed: ", errmsgbuf, PCAP_ERRBUF_SIZE); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1) rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -507,7 +516,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // So, this was a fake connection. Drop it down if (retval == 0) { - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_INITTIMEOUT, "The RPCAP initial timeout has expired", @@ -520,7 +529,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // // Read the message header from the client. // - nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, &header); + nrecv = rpcapd_recv_msg_header(pars.sockctrl_in, pars.ssl, &header); if (nrecv == -1) { // Fatal error. @@ -547,7 +556,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // so they don't reject it as having the wrong // version. // - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, header.ver, PCAP_ERR_WRONGVER, "RPCAP version in message isn't the negotiated version", errbuf) == -1) @@ -558,7 +567,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } // Discard the rest of the message. - (void)rpcapd_discard(pars.sockctrl_in, plen); + (void)rpcapd_discard(pars.sockctrl_in, pars.ssl, plen); // Give up on them. goto end; } @@ -567,7 +576,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { case RPCAP_MSG_ERROR: // The other endpoint reported an error { - (void)daemon_msg_err(pars.sockctrl_in, plen); + (void)daemon_msg_err(pars.sockctrl_in, pars.ssl, plen); // Do nothing; just exit; the error code is already into the errbuf // XXX - actually exit.... break; @@ -612,7 +621,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu { // They never told us what device // to capture on! - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_STARTCAPTURE, "No capture device was specified", @@ -623,14 +632,14 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); goto end; } - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { goto end; } break; } - if (daemon_msg_startcap_req(&pars, plen, &threaddata, source, &session, &samp_param) == -1) + if (daemon_msg_startcap_req(&pars, plen, &threaddata, source, &session, &samp_param, uses_ssl) == -1) { // Fatal error; a message has // been logged, so just give up. @@ -652,7 +661,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } else { - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_UPDATEFILTER, "Device not opened. Cannot update filter", @@ -719,7 +728,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu } else { - rpcap_senderror(pars.sockctrl_out, + rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_ENDCAPTURE, "Device not opened. Cannot close the capture", @@ -746,7 +755,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // get to reauthenticate. // rpcapd_log(LOGPRIO_INFO, "The client sent an RPCAP_MSG_AUTH_REQ message after authentication was completed"); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, "RPCAP_MSG_AUTH_REQ request sent after authentication was completed", @@ -756,7 +765,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -786,7 +795,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu rpcapd_log(LOGPRIO_INFO, "The client sent a server-to-client message of type %u", header.type); pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Server-to-client message of type %u received from client", header.type); } - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errmsgbuf, errbuf) == -1) { @@ -794,7 +803,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -807,7 +816,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu // rpcapd_log(LOGPRIO_INFO, "The client sent a message of type %u", header.type); pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Unknown message type %u", header.type); - if (rpcap_senderror(pars.sockctrl_out, + if (rpcap_senderror(pars.sockctrl_out, pars.ssl, pars.protocol_version, PCAP_ERR_WRONGMSG, errbuf, errmsgbuf) == -1) { @@ -815,7 +824,7 @@ daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, int nu goto end; } // Discard the rest of the message. - if (rpcapd_discard(pars.sockctrl_in, plen) == -1) + if (rpcapd_discard(pars.sockctrl_in, pars.ssl, plen) == -1) { // Fatal error. goto end; @@ -865,12 +874,8 @@ end: #endif threaddata.have_thread = 0; } - if (session->sockdata) - { - sock_close(session->sockdata, NULL, 0); - session->sockdata = 0; - } - pcap_close(session->fp); + + session_close(session); free(session); session = NULL; } @@ -886,7 +891,7 @@ end: * This handles the RPCAP_MSG_ERR message. */ static int -daemon_msg_err(SOCKET sockctrl_in, uint32 plen) +daemon_msg_err(SOCKET sockctrl_in, SSL *ssl, uint32 plen) { char errbuf[PCAP_ERRBUF_SIZE]; char remote_errbuf[PCAP_ERRBUF_SIZE]; @@ -897,7 +902,7 @@ daemon_msg_err(SOCKET sockctrl_in, uint32 plen) * Message is too long; just read as much of it as we * can into the buffer provided, and discard the rest. */ - if (sock_recv(sockctrl_in, remote_errbuf, PCAP_ERRBUF_SIZE - 1, + if (sock_recv(sockctrl_in, ssl, remote_errbuf, PCAP_ERRBUF_SIZE - 1, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -905,7 +910,7 @@ daemon_msg_err(SOCKET sockctrl_in, uint32 plen) rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); return -1; } - if (rpcapd_discard(sockctrl_in, plen - (PCAP_ERRBUF_SIZE - 1)) == -1) + if (rpcapd_discard(sockctrl_in, ssl, plen - (PCAP_ERRBUF_SIZE - 1)) == -1) { // Network error. return -1; @@ -923,7 +928,7 @@ daemon_msg_err(SOCKET sockctrl_in, uint32 plen) } else { - if (sock_recv(sockctrl_in, remote_errbuf, plen, + if (sock_recv(sockctrl_in, ssl, remote_errbuf, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE) == -1) { @@ -973,7 +978,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) int status; struct rpcap_auth auth; // RPCAP authentication header - status = rpcapd_recv(pars->sockctrl_in, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, (char *) &auth, sizeof(struct rpcap_auth), &plen, errmsgbuf); if (status == -1) { return -1; @@ -1009,7 +1014,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) PCAP_ERRBUF_SIZE, errno, "malloc() failed"); goto error; } - status = rpcapd_recv(pars->sockctrl_in, username, usernamelen, &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, username, usernamelen, &plen, errmsgbuf); if (status == -1) { free(username); @@ -1031,7 +1036,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) free(username); goto error; } - status = rpcapd_recv(pars->sockctrl_in, passwd, passwdlen, &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, passwd, passwdlen, &plen, errmsgbuf); if (status == -1) { free(username); @@ -1054,7 +1059,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) // free(username); free(passwd); - if (rpcap_senderror(pars->sockctrl_out, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1) { @@ -1091,7 +1096,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_AUTH_REPLY, 0, 0); // Send the ok message back - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a messsage and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1099,7 +1104,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -1107,7 +1112,7 @@ daemon_msg_auth_req(struct daemon_slpars *pars, uint32 plen) return 0; error: - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -1117,7 +1122,7 @@ error: error_noreply: // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -1262,7 +1267,7 @@ daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen) uint16 nif = 0; // counts the number of interface listed // Discard the rest of the message; there shouldn't be any payload. - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -1274,7 +1279,7 @@ daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen) if (alldevs == NULL) { - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_NOREMOTEIF, "No interfaces found! Make sure libpcap/WinPcap is properly installed" " and you have the right to access to the remote device.", @@ -1428,7 +1433,7 @@ daemon_msg_findallif_req(struct daemon_slpars *pars, uint32 plen) pcap_freealldevs(alldevs); // Send a final command that says "now send it!" - if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -1440,7 +1445,7 @@ error: if (alldevs) pcap_freealldevs(alldevs); - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_FINDALLIF, errmsgbuf, errbuf) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1470,7 +1475,7 @@ daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_ goto error; } - nread = sock_recv(pars->sockctrl_in, source, plen, + nread = sock_recv(pars->sockctrl_in, pars->ssl, source, plen, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -1513,7 +1518,7 @@ daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_ pcap_close(fp); // Send the reply. - if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -1521,7 +1526,7 @@ daemon_msg_open_req(struct daemon_slpars *pars, uint32 plen, char *source, size_ return 0; error: - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_OPEN, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -1530,7 +1535,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -1542,7 +1547,7 @@ error: to discard excess data in the message, if present) */ static int -daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param _U_) +daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_handle *threaddata, char *source, struct session **sessionp, struct rpcap_sampling *samp_param _U_, int uses_ssl) { char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors char errmsgbuf[PCAP_ERRBUF_SIZE]; // buffer for errors to send to the client @@ -1572,7 +1577,7 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h addrinfo = NULL; - status = rpcapd_recv(pars->sockctrl_in, (char *) &startcapreq, + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, (char *) &startcapreq, sizeof(struct rpcap_startcapreq), &plen, errmsgbuf); if (status == -1) { @@ -1585,6 +1590,15 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h startcapreq.flags = ntohs(startcapreq.flags); + // Check that the client does not ask for UDP is the server has been asked + // to enforce encryption, as SSL is not supported yet with UDP: + if (uses_ssl && (startcapreq.flags & RPCAP_STARTCAPREQ_FLAG_DGRAM)) + { + pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, + "SSL not supported with UDP forward of remote packets"); + goto error; + } + // Create a session structure session = malloc(sizeof(struct session)); if (session == NULL) @@ -1593,6 +1607,8 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h goto error; } + session->ctrl_ssl = session->data_ssl = NULL; + // Open the selected device if ((session->fp = pcap_open_live(source, ntohl(startcapreq.snaplen), @@ -1687,10 +1703,11 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h // Needed to send an error on the ctrl connection session->sockctrl_out = pars->sockctrl_out; + session->ctrl_ssl = pars->ssl; session->protocol_version = pars->protocol_version; // Now I can set the filter - ret = daemon_unpackapplyfilter(pars->sockctrl_in, session, &plen, errmsgbuf); + ret = daemon_unpackapplyfilter(pars->sockctrl_in, pars->ssl, session, &plen, errmsgbuf); if (ret == -1) { // Fatal error. A message has been logged; just give up. @@ -1725,7 +1742,7 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h startcapreply->portdata = htons(port); } - if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1754,6 +1771,22 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h sockdata = socktemp; } + SSL *ssl = NULL; + if (uses_ssl) + { +#ifdef HAVE_OPENSSL + /* In both active or passive cases, wait for the client to initiate the + * TLS handshake. Yes during that time the control socket will not be + * served, but the same was true from the above call to accept(). */ + ssl = ssl_promotion(1, sockdata, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + rpcapd_log(LOGPRIO_ERROR, "TLS handshake failed: %s", errbuf); + goto error; + } +#endif + } + session->data_ssl = ssl; session->sockdata = sockdata; // Now we have to create a new thread to receive packets @@ -1784,7 +1817,7 @@ daemon_msg_startcap_req(struct daemon_slpars *pars, uint32 plen, struct thread_h threaddata->have_thread = 1; // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) goto fatal_error; *sessionp = session; @@ -1822,10 +1855,16 @@ error: { if (session->fp) pcap_close(session->fp); +#ifdef HAVE_OPENSSL + if (session->ctrl_ssl) + SSL_free(session->ctrl_ssl); + if (session->data_ssl) + SSL_free(session->data_ssl); +#endif free(session); } - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_STARTCAPTURE, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -1834,7 +1873,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -1894,6 +1933,12 @@ fatal_error: { if (session->fp) pcap_close(session->fp); +#ifdef HAVE_OPENSSL + if (session->ctrl_ssl) + SSL_free(session->ctrl_ssl); + if (session->data_ssl) + SSL_free(session->data_ssl); +#endif free(session); } @@ -1940,18 +1985,13 @@ daemon_msg_endcap_req(struct daemon_slpars *pars, struct session *session, struc #endif threaddata->have_thread = 0; } - if (session->sockdata) - { - sock_close(session->sockdata, NULL, 0); - session->sockdata = 0; - } - pcap_close(session->fp); + session_close(session); rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_ENDCAP_REPLY, 0, 0); - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof(struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a message and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -1962,7 +2002,7 @@ daemon_msg_endcap_req(struct daemon_slpars *pars, struct session *session, struc } static int -daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *plenp, char *errmsgbuf) +daemon_unpackapplyfilter(SOCKET sockctrl_in, SSL *ctrl_ssl, struct session *session, uint32 *plenp, char *errmsgbuf) { int status; struct rpcap_filter filter; @@ -1971,7 +2011,7 @@ daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *pl struct bpf_program bf_prog; unsigned int i; - status = rpcapd_recv(sockctrl_in, (char *) &filter, + status = rpcapd_recv(sockctrl_in, ctrl_ssl, (char *) &filter, sizeof(struct rpcap_filter), plenp, errmsgbuf); if (status == -1) { @@ -2002,7 +2042,7 @@ daemon_unpackapplyfilter(SOCKET sockctrl_in, struct session *session, uint32 *pl for (i = 0; i < bf_prog.bf_len; i++) { - status = rpcapd_recv(sockctrl_in, (char *) &insn, + status = rpcapd_recv(sockctrl_in, ctrl_ssl, (char *) &insn, sizeof(struct rpcap_filterbpf_insn), plenp, errmsgbuf); if (status == -1) { @@ -2047,7 +2087,7 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, int ret; // status of daemon_unpackapplyfilter() struct rpcap_header header; // keeps the answer to the updatefilter command - ret = daemon_unpackapplyfilter(pars->sockctrl_in, session, &plen, errmsgbuf); + ret = daemon_unpackapplyfilter(pars->sockctrl_in, pars->ssl, session, &plen, errmsgbuf); if (ret == -1) { // Fatal error. A message has been logged; just give up. @@ -2060,7 +2100,7 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -2070,7 +2110,7 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_UPDATEFILTER_REPLY, 0, 0); - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE)) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof (struct rpcap_header), pcap_geterr(session->fp), PCAP_ERRBUF_SIZE)) { // That failed; log a messsage and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); @@ -2080,11 +2120,11 @@ daemon_msg_updatefilter_req(struct daemon_slpars *pars, struct session *session, return 0; error: - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } - rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_UPDATEFILTER, errmsgbuf, NULL); return 0; @@ -2102,7 +2142,7 @@ daemon_msg_setsampling_req(struct daemon_slpars *pars, uint32 plen, struct rpcap struct rpcap_sampling rpcap_samp; int status; - status = rpcapd_recv(pars->sockctrl_in, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf); + status = rpcapd_recv(pars->sockctrl_in, pars->ssl, (char *) &rpcap_samp, sizeof(struct rpcap_sampling), &plen, errmsgbuf); if (status == -1) { return -1; @@ -2120,14 +2160,14 @@ daemon_msg_setsampling_req(struct daemon_slpars *pars, uint32 plen, struct rpcap rpcap_createhdr(&header, pars->protocol_version, RPCAP_MSG_SETSAMPLING_REPLY, 0, 0); - if (sock_send(pars->sockctrl_out, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, (char *) &header, sizeof (struct rpcap_header), errbuf, PCAP_ERRBUF_SIZE) == -1) { // That failed; log a messsage and give up. rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; } - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -2135,7 +2175,7 @@ daemon_msg_setsampling_req(struct daemon_slpars *pars, uint32 plen, struct rpcap return 0; error: - if (rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + if (rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_AUTH, errmsgbuf, errbuf) == -1) { // That failed; log a message and give up. @@ -2144,7 +2184,7 @@ error: } // Check if all the data has been read; if not, discard the data in excess - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { return -1; } @@ -2162,7 +2202,7 @@ daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 struct rpcap_stats *netstats; // statistics sent on the network // Checks that the header does not contain other data; if so, discard it - if (rpcapd_discard(pars->sockctrl_in, plen) == -1) + if (rpcapd_discard(pars->sockctrl_in, pars->ssl, plen) == -1) { // Network error. return -1; @@ -2206,7 +2246,7 @@ daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 } // Send the packet - if (sock_send(pars->sockctrl_out, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_send(pars->sockctrl_out, pars->ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE) == -1) { rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf); return -1; @@ -2215,7 +2255,7 @@ daemon_msg_stats_req(struct daemon_slpars *pars, struct session *session, uint32 return 0; error: - rpcap_senderror(pars->sockctrl_out, pars->protocol_version, + rpcap_senderror(pars->sockctrl_out, pars->ssl, pars->protocol_version, PCAP_ERR_GETSTATS, errmsgbuf, NULL); return 0; } @@ -2376,7 +2416,7 @@ daemon_thrdatamain(void *ptr) // Send the packet // If the client dropped the connection, don't report an // error, just quit. - status = sock_send(session->sockdata, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE); + status = sock_send(session->sockdata, session->data_ssl, sendbuf, sendbufidx, errbuf, PCAP_ERRBUF_SIZE); if (status < 0) { if (status == -1) @@ -2401,14 +2441,13 @@ daemon_thrdatamain(void *ptr) if (retval == -1) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error reading the packets: %s", pcap_geterr(session->fp)); - rpcap_senderror(session->sockctrl_out, session->protocol_version, + rpcap_senderror(session->sockctrl_out, session->ctrl_ssl, session->protocol_version, PCAP_ERR_READEX, errbuf, NULL); goto error; } error: - closesocket(session->sockdata); - session->sockdata = 0; + session_close(session); free(sendbuf); @@ -2497,12 +2536,12 @@ void sleep_secs(int secs) * Read the header of a message. */ static int -rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp) +rpcapd_recv_msg_header(SOCKET sock, SSL *ssl, struct rpcap_header *headerp) { int nread; char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors - nread = sock_recv(sock, (char *) headerp, sizeof(struct rpcap_header), + nread = sock_recv(sock, ssl, (char *) headerp, sizeof(struct rpcap_header), SOCK_RECEIVEALL_YES|SOCK_EOF_ISNT_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -2529,7 +2568,7 @@ rpcapd_recv_msg_header(SOCKET sock, struct rpcap_header *headerp) * error. */ static int -rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf) +rpcapd_recv(SOCKET sock, SSL *ssl, char *buffer, size_t toread, uint32 *plen, char *errmsgbuf) { int nread; char errbuf[PCAP_ERRBUF_SIZE]; // buffer for network errors @@ -2540,7 +2579,7 @@ rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsg pcap_snprintf(errmsgbuf, PCAP_ERRBUF_SIZE, "Message payload is too short"); return -2; } - nread = sock_recv(sock, buffer, toread, + nread = sock_recv(sock, ssl, buffer, toread, SOCK_RECEIVEALL_YES|SOCK_EOF_IS_ERROR, errbuf, PCAP_ERRBUF_SIZE); if (nread == -1) { @@ -2558,13 +2597,13 @@ rpcapd_recv(SOCKET sock, char *buffer, size_t toread, uint32 *plen, char *errmsg * error. */ static int -rpcapd_discard(SOCKET sock, uint32 len) +rpcapd_discard(SOCKET sock, SSL *ssl, uint32 len) { char errbuf[PCAP_ERRBUF_SIZE + 1]; // keeps the error string, prior to be printed if (len != 0) { - if (sock_discard(sock, len, errbuf, PCAP_ERRBUF_SIZE) == -1) + if (sock_discard(sock, ssl, len, errbuf, PCAP_ERRBUF_SIZE) == -1) { // Network error. rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf); @@ -2573,3 +2612,27 @@ rpcapd_discard(SOCKET sock, uint32 len) } return 0; } + +/* + * Close the socket associated with the session, the optional SSL handle, + * and the underlying packet capture handle. We of course do not touch + * the controlling socket that's also copied into the session. + */ +static void session_close(struct session *session) +{ +#ifdef HAVE_OPENSSL + if (session->data_ssl) + { + SSL_free(session->data_ssl); // Must happen *before* the socket is closed + session->data_ssl = NULL; + } +#endif + + if (session->sockdata) + { + sock_close(session->sockdata, NULL, 0); + session->sockdata = 0; + } + + pcap_close(session->fp); +} diff --git a/rpcapd/daemon.h b/rpcapd/daemon.h index bd240b80..83901e77 100644 --- a/rpcapd/daemon.h +++ b/rpcapd/daemon.h @@ -33,12 +33,18 @@ #ifndef __DAEMON_H__ #define __DAEMON_H__ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "sslutils.h" + // // Returns 1 if the client closed the control connection explicitly, 0 // otherwise; used in active mode only. // -int daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, int isactive, - int nullAuthAllowed); +int daemon_serviceloop(SOCKET sockctrl_in, SOCKET sockctrl_out, SSL *ssl, + int isactive, int nullAuthAllowed, int uses_ssl); void sleep_secs(int secs); diff --git a/rpcapd/rpcapd.c b/rpcapd/rpcapd.c index 9e46a2de..0509147a 100644 --- a/rpcapd/rpcapd.c +++ b/rpcapd/rpcapd.c @@ -53,6 +53,10 @@ #include "daemon.h" // the true main() method of this daemon #include "log.h" +#ifdef HAVE_OPENSSL +#include "sslutils.h" +#endif + #ifdef _WIN32 #include <process.h> // for thread stuff #include "win32-svc.h" // for Win32 service stuff @@ -86,6 +90,7 @@ static HANDLE state_change_event; //!< event to signal that a state change shou #endif static volatile sig_atomic_t shutdown_server; //!< '1' if the server is to shut down static volatile sig_atomic_t reread_config; //!< '1' if the server is to re-read its configuration +static int uses_ssl; //!< '1' to use TLS over the data socket extern char *optarg; // for getopt() @@ -144,6 +149,12 @@ static void printusage(void) #ifndef _WIN32 " -i run in inetd mode (UNIX only)\n\n" #endif +#ifdef HAVE_OPENSSL + " -S encrypt all communication with SSL (implements rpcaps://)\n" + " -C enable compression\n" + " -K <pem_file> uses the SSL private key in this file (default: key.pem)\n" + " -X <pem_file> uses the certificate from this file (default: cert.pem)\n" +#endif " -s <config_file> save the current configuration to file\n\n" " -f <config_file> load the current configuration from file; all switches\n" " specified from the command line are ignored\n\n" @@ -169,6 +180,9 @@ int main(int argc, char *argv[]) #ifndef _WIN32 struct sigaction action; #endif +#ifdef HAVE_OPENSSL + int enable_compression = 0; +#endif savefile[0] = 0; loadfile[0] = 0; @@ -194,7 +208,15 @@ int main(int argc, char *argv[]) mainhints.ai_socktype = SOCK_STREAM; // Getting the proper command line options - while ((retval = getopt(argc, argv, "b:dhip:4l:na:s:f:v")) != -1) +# ifdef HAVE_OPENSSL +# define SSL_CLOPTS "SK:X:C" +# else +# define SSL_CLOPTS "" +# endif + +# define CLOPTS "b:dhip:4l:na:s:f:v" SSL_CLOPTS + + while ((retval = getopt(argc, argv, CLOPTS)) != -1) { switch (retval) { @@ -266,6 +288,20 @@ int main(int argc, char *argv[]) case 's': pcap_strlcpy(savefile, optarg, MAX_LINE); break; +#ifdef HAVE_OPENSSL + case 'S': + uses_ssl = 1; + break; + case 'C': + enable_compression = 1; + break; + case 'K': + snprintf(ssl_keyfile, sizeof ssl_keyfile, "%s", optarg); + break; + case 'X': + snprintf(ssl_certfile, sizeof ssl_certfile, "%s", optarg); + break; +#endif case 'h': printusage(); exit(0); @@ -331,6 +367,10 @@ int main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); #endif +# ifdef HAVE_OPENSSL + if (uses_ssl) init_ssl_or_die(1, enable_compression); +# endif + #ifndef _WIN32 if (isrunbyinetd) { @@ -381,13 +421,26 @@ int main(int argc, char *argv[]) close(devnull_fd); } + SSL *ssl = NULL; +#ifdef HAVE_OPENSSL + if (uses_ssl) + { + ssl = ssl_promotion_rw(1, sockctrl_in, sockctrl_out, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s", + errbuf); + exit(2); + } + } +#endif // // Handle this client. // This is passive mode, so we don't care whether we were // told by the client to close. // - (void)daemon_serviceloop(sockctrl_in, sockctrl_out, 0, - nullAuthAllowed); + (void)daemon_serviceloop(sockctrl_in, sockctrl_out, ssl, 0, + nullAuthAllowed, uses_ssl); // // Nothing more to do. @@ -1033,6 +1086,22 @@ accept_connections(void) sock_cleanup(); } +#ifdef _WIN32 +// +// A structure to hold the parameter to the windows thread +// (on unix there is no need for this explicit copy since the +// fork "inherits" the parent stack) +// +struct sock_copy { + SOCKET sockctrl; +# ifdef HAVE_OPENSSL + SSL *ssl; +# else + void *ssl; +# endif +}; +#endif + // // Accept a connection and start a worker thread, on Windows, or a // worker process, on UN*X, to handle the connection. @@ -1045,10 +1114,12 @@ accept_connection(SOCKET listen_sock) struct sockaddr_storage from; // generic sockaddr_storage variable socklen_t fromlen; // keeps the length of the sockaddr_storage variable + SSL *ssl = NULL; + #ifdef _WIN32 HANDLE threadId; // handle for the subthread u_long off = 0; - SOCKET *sockctrl_temp; + struct sock_copy *sock_copy = NULL; #else pid_t pid; #endif @@ -1087,15 +1158,29 @@ accept_connection(SOCKET listen_sock) return; } +#ifdef HAVE_OPENSSL + /* We have to upgrade to TLS as soon as possible so that the whole protocol + * goes through the encrypted tunnel, including early error messages. */ + if (uses_ssl) + { + ssl = ssl_promotion(1, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s", + errbuf); + goto error; + } + } +#endif + // // We have a connection. // Check whether the connecting host is among the ones allowed. // if (sock_check_hostlist(hostlist, RPCAP_HOSTLIST_SEP, &from, errbuf, PCAP_ERRBUF_SIZE) < 0) { - rpcap_senderror(sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL); - sock_close(sockctrl, NULL, 0); - return; + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL); + goto error; } #ifdef _WIN32 @@ -1111,16 +1196,14 @@ accept_connection(SOCKET listen_sock) if (WSAEventSelect(sockctrl, NULL, 0) == SOCKET_ERROR) { sock_geterror("ioctlsocket(FIONBIO): ", errbuf, PCAP_ERRBUF_SIZE); - rpcap_senderror(sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL); - sock_close(sockctrl, NULL, 0); - return; + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL); + goto error; } if (ioctlsocket(sockctrl, FIONBIO, &off) == SOCKET_ERROR) { sock_geterror("ioctlsocket(FIONBIO): ", errbuf, PCAP_ERRBUF_SIZE); - rpcap_senderror(sockctrl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL); - sock_close(sockctrl, NULL, 0); - return; + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_HOSTNOAUTH, errbuf, NULL); + goto error; } // @@ -1130,26 +1213,24 @@ accept_connection(SOCKET listen_sock) // I guess we *could* just cast sockctrl to a void *, but that's // a bit ugly. // - sockctrl_temp = (SOCKET *)malloc(sizeof (SOCKET)); - if (sockctrl_temp == NULL) + sock_copy = malloc(sizeof(*sock_copy)); + if (sock_copy == NULL) { pcap_fmt_errmsg_for_errno(errbuf, PCAP_ERRBUF_SIZE, errno, "malloc() failed"); - rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL); - sock_close(sockctrl, NULL, 0); - return; + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_OPEN, errbuf, NULL); + goto error; } - *sockctrl_temp = sockctrl; + sock_copy->sockctrl = sockctrl; + sock_copy->ssl = NULL; threadId = (HANDLE)_beginthreadex(NULL, 0, - main_passive_serviceloop_thread, (void *) sockctrl_temp, 0, NULL); + main_passive_serviceloop_thread, (void *) sock_copy, 0, NULL); if (threadId == 0) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the child thread"); - rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL); - sock_close(sockctrl, NULL, 0); - free(sockctrl_temp); - return; + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_OPEN, errbuf, NULL); + goto error; } CloseHandle(threadId); #else @@ -1157,9 +1238,8 @@ accept_connection(SOCKET listen_sock) if (pid == -1) { pcap_snprintf(errbuf, PCAP_ERRBUF_SIZE, "Error creating the child process"); - rpcap_senderror(sockctrl, 0, PCAP_ERR_OPEN, errbuf, NULL); - sock_close(sockctrl, NULL, 0); - return; + rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_OPEN, errbuf, NULL); + goto error; } if (pid == 0) { @@ -1190,8 +1270,8 @@ accept_connection(SOCKET listen_sock) // This is passive mode, so we don't care whether we were // told by the client to close. // - (void)daemon_serviceloop(sockctrl, sockctrl, 0, - nullAuthAllowed); + (void)daemon_serviceloop(sockctrl, sockctrl, ssl, 0, + nullAuthAllowed, uses_ssl); close(sockctrl); @@ -1200,8 +1280,25 @@ accept_connection(SOCKET listen_sock) // I am the parent // Close the socket for this session (must be open only in the child) +#ifdef HAVE_OPENSSL + if (ssl) + { + SSL_free(ssl); + ssl = NULL; + } +#endif closesocket(sockctrl); #endif + return; + +error: +#ifdef _WIN32 + if (sock_copy) free(sock_copy); +#endif +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); // Have to be done before closing soskctrl +#endif + sock_close(sockctrl, NULL, 0); } /*! @@ -1225,6 +1322,7 @@ main_active(void *ptr) struct addrinfo hints; // temporary struct to keep settings needed to open the new socket struct addrinfo *addrinfo; // keeps the addrinfo chain; required to open a new socket struct active_pars *activepars; + SSL *ssl = NULL; activepars = (struct active_pars *) ptr; @@ -1269,9 +1367,28 @@ main_active(void *ptr) continue; } - activeclose = daemon_serviceloop(sockctrl, sockctrl, 1, - nullAuthAllowed); +#ifdef HAVE_OPENSSL + /* Even in active mode the other other end has to initiate the TLS handshake + * as we still are the server as far as TLS is concerned: */ + if (uses_ssl) + { + ssl = ssl_promotion(1, sockctrl, errbuf, PCAP_ERRBUF_SIZE); + if (! ssl) + { + rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s", + errbuf); + sock_close(sockctrl, NULL, 0); + continue; + } + } +#endif + + activeclose = daemon_serviceloop(sockctrl, sockctrl, ssl, 1, + nullAuthAllowed, uses_ssl); +#ifdef HAVE_OPENSSL + if (ssl) SSL_free(ssl); +#endif sock_close(sockctrl, NULL, 0); // If the connection is closed by the user explicitely, don't try to connect to it again @@ -1290,9 +1407,7 @@ main_active(void *ptr) // unsigned __stdcall main_passive_serviceloop_thread(void *ptr) { - SOCKET sockctrl; - - sockctrl = *((SOCKET *)ptr); + struct sock_copy sock = *(struct sock_copy *)ptr; free(ptr); // @@ -1300,9 +1415,10 @@ unsigned __stdcall main_passive_serviceloop_thread(void *ptr) // This is passive mode, so we don't care whether we were // told by the client to close. // - (void)daemon_serviceloop(sockctrl, sockctrl, 0, nullAuthAllowed); + (void)daemon_serviceloop(sock.sockctrl, sock.sockctrl, sock.ssl, 0, + nullAuthAllowed, uses_ssl); - sock_close(sockctrl, NULL, 0); + sock_close(sock.sockctrl, NULL, 0); return 0; } diff --git a/sockutils.c b/sockutils.c index 7ed7d298..25844c6c 100644 --- a/sockutils.c +++ b/sockutils.c @@ -640,7 +640,7 @@ int sock_initaddress(const char *host, const char *port, * '-2' if we got one of those errors. * For errors, an error message is returned in the 'errbuf' variable. */ -int sock_send(SOCKET sock, const char *buffer, size_t size, +int sock_send(SOCKET sock, SSL *ssl, const char *buffer, size_t size, char *errbuf, int errbuflen) { int remaining; @@ -659,6 +659,12 @@ int sock_send(SOCKET sock, const char *buffer, size_t size, remaining = (int)size; do { +#ifdef HAVE_OPENSSL + if (ssl) return ssl_send(ssl, buffer, remaining, errbuf, errbuflen); +#else + (void)ssl; +#endif + #ifdef MSG_NOSIGNAL /* * Send with MSG_NOSIGNAL, so that we don't get SIGPIPE @@ -835,7 +841,7 @@ int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int * The error message is returned in the 'errbuf' variable. */ -int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, +int sock_recv(SOCKET sock, SSL *ssl, void *buffer, size_t size, int flags, char *errbuf, int errbuflen) { int recv_flags = 0; @@ -870,7 +876,20 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, * Win32. */ for (;;) { +#ifdef HAVE_OPENSSL + if (ssl) + { + /* + * XXX - what about MSG_PEEK? + */ + nread = ssl_recv(ssl, bufp, remaining, errbuf, errbuflen); + if (nread == -2) return -1; + } + else + nread = recv(sock, bufp, remaining, recv_flags); +#else nread = recv(sock, bufp, remaining, recv_flags); +#endif if (nread == -1) { @@ -928,7 +947,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, * * Returns the size of the datagram on success or -1 on error. */ -int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, +int sock_recv_dgram(SOCKET sock, SSL *ssl, void *buffer, size_t size, char *errbuf, int errbuflen) { ssize_t nread; @@ -953,6 +972,13 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, return -1; } + // TODO: DTLS + if (ssl) + { + pcap_snprintf(errbuf, errbuflen, "DTLS not implemented yet"); + return -1; + } + /* * This should be a datagram socket, so we should get the * entire datagram in one recv() or recvmsg() call, and @@ -1063,7 +1089,7 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, * \return '0' if everything is fine, '-1' if some errors occurred. * The error message is returned in the 'errbuf' variable. */ -int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) +int sock_discard(SOCKET sock, SSL *ssl, int size, char *errbuf, int errbuflen) { #define TEMP_BUF_SIZE 32768 @@ -1079,7 +1105,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) */ while (size > TEMP_BUF_SIZE) { - if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) + if (sock_recv(sock, ssl, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) return -1; size -= TEMP_BUF_SIZE; @@ -1091,7 +1117,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) */ if (size) { - if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) + if (sock_recv(sock, ssl, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1) return -1; } diff --git a/sockutils.h b/sockutils.h index 3102fcef..fc271bf9 100644 --- a/sockutils.h +++ b/sockutils.h @@ -101,6 +101,8 @@ #define closesocket(a) close(a) #endif +#include "sslutils.h" // for SSL type, whatever that turns out to be + /* * MingW headers include this definition, but only for Windows XP and above. * MSDN states that this function is available for most versions on Windows. @@ -208,17 +210,17 @@ void sock_geterror(const char *caller, char *errbuf, int errbufsize); int sock_initaddress(const char *address, const char *port, struct addrinfo *hints, struct addrinfo **addrinfo, char *errbuf, int errbuflen); -int sock_recv(SOCKET sock, void *buffer, size_t size, int receiveall, +int sock_recv(SOCKET sock, SSL *, void *buffer, size_t size, int receiveall, char *errbuf, int errbuflen); -int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, +int sock_recv_dgram(SOCKET sock, SSL *, void *buffer, size_t size, char *errbuf, int errbuflen); SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen); int sock_close(SOCKET sock, char *errbuf, int errbuflen); -int sock_send(SOCKET sock, const char *buffer, size_t size, +int sock_send(SOCKET sock, SSL *, const char *buffer, size_t size, char *errbuf, int errbuflen); int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen); -int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen); +int sock_discard(SOCKET sock, SSL *, int size, char *errbuf, int errbuflen); int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage *from, char *errbuf, int errbuflen); int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second); diff --git a/sslutils.c b/sslutils.c new file mode 100644 index 00000000..c6fb80c5 --- /dev/null +++ b/sslutils.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_OPENSSL +#include <stdlib.h> + +#include "portability.h" +#include "sslutils.h" +#include "pcap/pcap.h" + +char ssl_keyfile[PATH_MAX]; //!< file containing the private key in PEM format +char ssl_certfile[PATH_MAX]; //!< file containing the server's certificate in PEM format +char ssl_rootfile[PATH_MAX]; //!< file containing the list of CAs trusted by the client +// TODO: a way to set ssl_rootfile from the command line, or an envvar? + +// TODO: lock? +static SSL_CTX *ctx; + +static int ssl_init_once(int is_server, int enable_compression, char *errbuf, size_t errbuflen) +{ + static int inited = 0; + if (inited) return 0; + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + if (enable_compression) + SSL_COMP_get_compression_methods(); + + SSL_METHOD const *meth = + is_server ? SSLv23_server_method() : SSLv23_client_method(); + ctx = SSL_CTX_new(meth); + if (! ctx) + { + pcap_snprintf(errbuf, errbuflen, "Cannot get a new SSL context: %s", ERR_error_string(ERR_get_error(), NULL)); + goto die; + } + + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + + if (is_server) + { + char const *certfile = ssl_certfile[0] ? ssl_certfile : "cert.pem"; + if (1 != SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM)) + { + pcap_snprintf(errbuf, errbuflen, "Cannot read certificate file %s: %s", certfile, ERR_error_string(ERR_get_error(), NULL)); + goto die; + } + + char const *keyfile = ssl_keyfile[0] ? ssl_keyfile : "key.pem"; + if (1 != SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM)) + { + pcap_snprintf(errbuf, errbuflen, "Cannot read private key file %s: %s", keyfile, ERR_error_string(ERR_get_error(), NULL)); + goto die; + } + } + else + { + if (ssl_rootfile[0]) + { + if (! SSL_CTX_load_verify_locations(ctx, ssl_rootfile, 0)) + { + pcap_snprintf(errbuf, errbuflen, "Cannot read CA list from %s", ssl_rootfile); + goto die; + } + } + else + { + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + } + } + +#if 0 + if (! RAND_load_file(RANDOM, 1024*1024)) + { + pcap_snprintf(errbuf, errbuflen, "Cannot init random"); + goto die; + } + + if (is_server) + { + SSL_CTX_set_session_id_context(ctx, (void *)&s_server_session_id_context, sizeof(s_server_session_id_context)); + } +#endif + + inited = 1; + return 0; + +die: + return -1; +} + +void init_ssl_or_die(int is_server, int enable_compression) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + + if (ssl_init_once(is_server, enable_compression, errbuf, sizeof errbuf) < 0) + { + fprintf(stderr, "%s\n", errbuf); + exit(3); + } +} + +SSL *ssl_promotion_rw(int is_server, SOCKET in, SOCKET out, char *errbuf, size_t errbuflen) +{ + if (ssl_init_once(is_server, 1, errbuf, errbuflen) < 0) { + return NULL; + } + + SSL *ssl = SSL_new(ctx); // TODO: also a DTLS context + SSL_set_rfd(ssl, in); + SSL_set_wfd(ssl, out); + + if (is_server) { + if (SSL_accept(ssl) <= 0) { + pcap_snprintf(errbuf, errbuflen, "SSL_accept(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + } else { + if (SSL_connect(ssl) <= 0) { + pcap_snprintf(errbuf, errbuflen, "SSL_connect(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + } + + return ssl; +} + +SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen) +{ + return ssl_promotion_rw(is_server, s, s, errbuf, errbuflen); +} + +// Same return value as sock_send: +// 0 on OK, -1 on error but closed connection (-2). +int ssl_send(SSL *ssl, char const *buffer, size_t size, char *errbuf, size_t errbuflen) +{ + int status = SSL_write(ssl, buffer, size); + if (status > 0) + { + // "SSL_write() will only return with success, when the complete contents (...) has been written." + return 0; + } + else + { + int ssl_err = SSL_get_error(ssl, status); // TODO: does it pop the error? + if (ssl_err == SSL_ERROR_ZERO_RETURN) + { + return -2; + } + else if (ssl_err == SSL_ERROR_SYSCALL) + { +#ifndef _WIN32 + if (errno == ECONNRESET || errno == EPIPE) return -2; +#endif + } + pcap_snprintf(errbuf, errbuflen, "SSL_write(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } +} + +// Returns the number of bytes read, or -1 on syserror, or -2 on SSL error. +int ssl_recv(SSL *ssl, char *buffer, size_t size, char *errbuf, size_t errbuflen) +{ + int status = SSL_read(ssl, buffer, size); + if (status <= 0) + { + int ssl_err = SSL_get_error(ssl, status); + if (ssl_err == SSL_ERROR_ZERO_RETURN) + { + return 0; + } + else if (ssl_err == SSL_ERROR_SYSCALL) + { + return -1; + } + else + { + // Should not happen + pcap_snprintf(errbuf, errbuflen, "SSL_read(): %s", + ERR_error_string(ERR_get_error(), NULL)); + return -2; + } + } + else + { + return status; + } +} + +#endif // HAVE_OPENSSL diff --git a/sslutils.h b/sslutils.h new file mode 100644 index 00000000..41a243e3 --- /dev/null +++ b/sslutils.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002 - 2003 + * NetGroup, Politecnico di Torino (Italy) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Politecnico di Torino nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __SSLUTILS_H__ +#define __SSLUTILS_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_OPENSSL +#include <openssl/ssl.h> +#include <openssl/err.h> +#include "pcap/pcap.h" // for SOCKET + +/* + * Configuration parameters + */ + +extern char ssl_keyfile[PATH_MAX]; +extern char ssl_certfile[PATH_MAX]; +extern char ssl_rootfile[PATH_MAX]; + +/* + * Utility functions + */ + +void init_ssl_or_die(int is_server, int enable_compression); +SSL *ssl_promotion(int is_server, SOCKET s, char *errbuf, size_t errbuflen); +SSL *ssl_promotion_rw(int is_server, SOCKET in, SOCKET out, char *errbuf, size_t errbuflen); +int ssl_send(SSL *, char const *buffer, size_t size, char *errbuf, size_t errbuflen); +int ssl_recv(SSL *, char *buffer, size_t size, char *errbuf, size_t errbuflen); + +#else // HAVE_OPENSSL + +// This saves us from a lot of ifdefs: +#define SSL void const + +#endif // HAVE_OPENSSL + +#endif // __SSLUTILS_H__ |