aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorralcini <roberto.alcini@gmail.com>2017-04-20 19:01:08 +0200
committerBruce A. Mah <bmah@kitchenlab.org>2017-04-20 10:01:08 -0700
commita51045de196f762fb74c86184b03da148c4e8f07 (patch)
tree2d6d223168994e38f93cd635d12caeb55ca6d82c
parent05600c201ac47d02c33e8ccfb2f17f1bcc977c4b (diff)
downloadiperf3-a51045de196f762fb74c86184b03da148c4e8f07.tar.gz
Service Authentication (#517)
Add an optional mode that requires clients to authenticate with the server. In this mode, clients need to provide a username and a password, which are checked against a password file on the server. The authentication credentials are protected by an RSA public keypair...the encrypted credentials are sent along with the test parameters. Operationally the use of this feature places the following additional requirements on the build and installation of iperf3: o The presence of the OpenSSL headers and libraries to build iperf3, and the libraries available on the client and server at runtime. o Generation of an RSA public keypair; the private part is used by the server and the public part must be distributed to the clients. o Username/password pairs for all authorized users, to be stored in a file on the server. o Loose time synchronization between the server and clients (to within approximately 30 seconds). o Appropriate command-line flags given on the client and server. Note that iperf3 can be built and run as before, without fulfilling any of these requirements. Partial documentation for this feature is included in this commit. It is anticipated that additional documentation text and editing will follow this merge. Submitted by @ralcini. First suggested by @codyhanson in pull request #242.
-rw-r--r--ax_check_openssl.m4124
-rwxr-xr-xconfigure274
-rw-r--r--configure.ac9
-rw-r--r--docs/invoking.rst56
-rw-r--r--iperf3.spec.in4
-rw-r--r--src/Makefile.in35
-rwxr-xr-xsrc/iperf.h3
-rw-r--r--src/iperf3.150
-rwxr-xr-xsrc/iperf_api.c149
-rwxr-xr-xsrc/iperf_api.h7
-rw-r--r--src/iperf_auth.c312
-rw-r--r--src/iperf_auth.h36
-rw-r--r--src/iperf_config.h.in3
-rw-r--r--src/iperf_error.c15
-rw-r--r--src/iperf_locale.c17
-rw-r--r--src/iperf_locale.h2
16 files changed, 1064 insertions, 32 deletions
diff --git a/ax_check_openssl.m4 b/ax_check_openssl.m4
new file mode 100644
index 0000000..28e48cb
--- /dev/null
+++ b/ax_check_openssl.m4
@@ -0,0 +1,124 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_openssl.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]])
+#
+# DESCRIPTION
+#
+# Look for OpenSSL in a number of default spots, or in a user-selected
+# spot (via --with-openssl). Sets
+#
+# OPENSSL_INCLUDES to the include directives required
+# OPENSSL_LIBS to the -l directives required
+# OPENSSL_LDFLAGS to the -L or -R flags required
+#
+# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately
+#
+# This macro sets OPENSSL_INCLUDES such that source files should use the
+# openssl/ directory in include directives:
+#
+# #include <openssl/hmac.h>
+#
+# LICENSE
+#
+# Copyright (c) 2009,2010 Zmanda Inc. <http://www.zmanda.com/>
+# Copyright (c) 2009,2010 Dustin J. Mitchell <dustin@zmanda.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 10
+
+AU_ALIAS([CHECK_SSL], [AX_CHECK_OPENSSL])
+AC_DEFUN([AX_CHECK_OPENSSL], [
+ found=false
+ AC_ARG_WITH([openssl],
+ [AS_HELP_STRING([--with-openssl=DIR],
+ [root of the OpenSSL directory])],
+ [
+ case "$withval" in
+ "" | y | ye | yes | n | no)
+ AC_MSG_ERROR([Invalid --with-openssl value])
+ ;;
+ *) ssldirs="$withval"
+ ;;
+ esac
+ ], [
+ # if pkg-config is installed and openssl has installed a .pc file,
+ # then use that information and don't search ssldirs
+ AC_CHECK_TOOL([PKG_CONFIG], [pkg-config])
+ if test x"$PKG_CONFIG" != x""; then
+ OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null`
+ if test $? = 0; then
+ OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null`
+ OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null`
+ found=true
+ fi
+ fi
+
+ # no such luck; use some default ssldirs
+ if ! $found; then
+ ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr"
+ fi
+ ]
+ )
+
+
+ # note that we #include <openssl/foo.h>, so the OpenSSL headers have to be in
+ # an 'openssl' subdirectory
+
+ if ! $found; then
+ OPENSSL_INCLUDES=
+ for ssldir in $ssldirs; do
+ AC_MSG_CHECKING([for openssl/ssl.h in $ssldir])
+ if test -f "$ssldir/include/openssl/ssl.h"; then
+ OPENSSL_INCLUDES="-I$ssldir/include"
+ OPENSSL_LDFLAGS="-L$ssldir/lib"
+ OPENSSL_LIBS="-lssl -lcrypto"
+ found=true
+ AC_MSG_RESULT([yes])
+ break
+ else
+ AC_MSG_RESULT([no])
+ fi
+ done
+
+ # if the file wasn't found, well, go ahead and try the link anyway -- maybe
+ # it will just work!
+ fi
+
+ # try the preprocessor and linker with our new flags,
+ # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS
+
+ AC_MSG_CHECKING([whether compiling and linking against OpenSSL works])
+ echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \
+ "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&AS_MESSAGE_LOG_FD
+
+ save_LIBS="$LIBS"
+ save_LDFLAGS="$LDFLAGS"
+ save_CPPFLAGS="$CPPFLAGS"
+ LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS"
+ LIBS="$OPENSSL_LIBS $LIBS"
+ CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([#include <openssl/ssl.h>], [SSL_new(NULL)])],
+ [
+ AC_MSG_RESULT([yes])
+ $1
+ ], [
+ AC_MSG_RESULT([no])
+ $2
+ ])
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+
+ AC_SUBST([OPENSSL_INCLUDES])
+ AC_SUBST([OPENSSL_LIBS])
+ AC_SUBST([OPENSSL_LDFLAGS])
+])
diff --git a/configure b/configure
index 84b7bc1..95ec086 100755
--- a/configure
+++ b/configure
@@ -635,6 +635,10 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
+OPENSSL_LDFLAGS
+OPENSSL_LIBS
+OPENSSL_INCLUDES
+PKG_CONFIG
CPP
LT_SYS_LIBRARY_PATH
OTOOL64
@@ -765,6 +769,7 @@ with_aix_soname
with_gnu_ld
with_sysroot
enable_libtool_lock
+with_openssl
'
ac_precious_vars='build_alias
host_alias
@@ -1420,6 +1425,7 @@ Optional Packages:
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
--with-sysroot[=DIR] Search for dependent libraries within DIR (or the
compiler's sysroot if not specified).
+ --with-openssl=DIR root of the OpenSSL directory
Some influential environment variables:
CC C compiler command
@@ -2217,6 +2223,63 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
ac_compiler_gnu=$ac_cv_c_compiler_gnu
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_openssl.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_OPENSSL([action-if-found[, action-if-not-found]])
+#
+# DESCRIPTION
+#
+# Look for OpenSSL in a number of default spots, or in a user-selected
+# spot (via --with-openssl). Sets
+#
+# OPENSSL_INCLUDES to the include directives required
+# OPENSSL_LIBS to the -l directives required
+# OPENSSL_LDFLAGS to the -L or -R flags required
+#
+# and calls ACTION-IF-FOUND or ACTION-IF-NOT-FOUND appropriately
+#
+# This macro sets OPENSSL_INCLUDES such that source files should use the
+# openssl/ directory in include directives:
+#
+# #include <openssl/hmac.h>
+#
+# LICENSE
+#
+# Copyright (c) 2009,2010 Zmanda Inc. <http://www.zmanda.com/>
+# Copyright (c) 2009,2010 Dustin J. Mitchell <dustin@zmanda.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 10
+
+# This is what autoupdate's m4 run will expand. It fires
+# the warning (with _au_warn_XXX), outputs it into the
+# updated configure.ac (with AC_DIAGNOSE), and then outputs
+# the replacement expansion.
+
+
+# This is an auxiliary macro that is also run when
+# autoupdate runs m4. It simply calls m4_warning, but
+# we need a wrapper so that each warning is emitted only
+# once. We break the quoting in m4_warning's argument in
+# order to expand this macro's arguments, not AU_DEFUN's.
+
+
+# Finally, this is the expansion that is picked up by
+# autoconf. It tells the user to run autoupdate, and
+# then outputs the replacement expansion. We do not care
+# about autoupdate's warning because that contains
+# information on what to do *after* running autoupdate.
+
+
+
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -12673,6 +12736,217 @@ fi
done
+# Check for OPENSSL support
+
+ found=false
+
+# Check whether --with-openssl was given.
+if test "${with_openssl+set}" = set; then :
+ withval=$with_openssl;
+ case "$withval" in
+ "" | y | ye | yes | n | no)
+ as_fn_error $? "Invalid --with-openssl value" "$LINENO" 5
+ ;;
+ *) ssldirs="$withval"
+ ;;
+ esac
+
+else
+
+ # if pkg-config is installed and openssl has installed a .pc file,
+ # then use that information and don't search ssldirs
+ 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_prog_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$PKG_CONFIG"; then
+ ac_cv_prog_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test.
+else
+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_prog_PKG_CONFIG="${ac_tool_prefix}pkg-config"
+ $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
+
+fi
+fi
+PKG_CONFIG=$ac_cv_prog_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_prog_PKG_CONFIG"; then
+ ac_ct_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_prog_ac_ct_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_PKG_CONFIG"; then
+ ac_cv_prog_ac_ct_PKG_CONFIG="$ac_ct_PKG_CONFIG" # Let the user override the test.
+else
+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_prog_ac_ct_PKG_CONFIG="pkg-config"
+ $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
+
+fi
+fi
+ac_ct_PKG_CONFIG=$ac_cv_prog_ac_ct_PKG_CONFIG
+if test -n "$ac_ct_PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_PKG_CONFIG" >&5
+$as_echo "$ac_ct_PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_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_ct_PKG_CONFIG
+ fi
+else
+ PKG_CONFIG="$ac_cv_prog_PKG_CONFIG"
+fi
+
+ if test x"$PKG_CONFIG" != x""; then
+ OPENSSL_LDFLAGS=`$PKG_CONFIG openssl --libs-only-L 2>/dev/null`
+ if test $? = 0; then
+ OPENSSL_LIBS=`$PKG_CONFIG openssl --libs-only-l 2>/dev/null`
+ OPENSSL_INCLUDES=`$PKG_CONFIG openssl --cflags-only-I 2>/dev/null`
+ found=true
+ fi
+ fi
+
+ # no such luck; use some default ssldirs
+ if ! $found; then
+ ssldirs="/usr/local/ssl /usr/lib/ssl /usr/ssl /usr/pkg /usr/local /usr"
+ fi
+
+
+fi
+
+
+
+ # note that we #include <openssl/foo.h>, so the OpenSSL headers have to be in
+ # an 'openssl' subdirectory
+
+ if ! $found; then
+ OPENSSL_INCLUDES=
+ for ssldir in $ssldirs; do
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $ssldir" >&5
+$as_echo_n "checking for openssl/ssl.h in $ssldir... " >&6; }
+ if test -f "$ssldir/include/openssl/ssl.h"; then
+ OPENSSL_INCLUDES="-I$ssldir/include"
+ OPENSSL_LDFLAGS="-L$ssldir/lib"
+ OPENSSL_LIBS="-lssl -lcrypto"
+ found=true
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ break
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ done
+
+ # if the file wasn't found, well, go ahead and try the link anyway -- maybe
+ # it will just work!
+ fi
+
+ # try the preprocessor and linker with our new flags,
+ # being careful not to pollute the global LIBS, LDFLAGS, and CPPFLAGS
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiling and linking against OpenSSL works" >&5
+$as_echo_n "checking whether compiling and linking against OpenSSL works... " >&6; }
+ echo "Trying link with OPENSSL_LDFLAGS=$OPENSSL_LDFLAGS;" \
+ "OPENSSL_LIBS=$OPENSSL_LIBS; OPENSSL_INCLUDES=$OPENSSL_INCLUDES" >&5
+
+ save_LIBS="$LIBS"
+ save_LDFLAGS="$LDFLAGS"
+ save_CPPFLAGS="$CPPFLAGS"
+ LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS"
+ LIBS="$OPENSSL_LIBS $LIBS"
+ CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <openssl/ssl.h>
+int
+main ()
+{
+SSL_new(NULL)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_SSL 1" >>confdefs.h
+
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+
+
+
+
+
+LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS"
+LIBS="$OPENSSL_LIBS $LIBS"
+CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS"
+
# Check for TCP_CONGESTION sockopt (believed to be Linux and FreeBSD only)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking TCP_CONGESTION socket option" >&5
$as_echo_n "checking TCP_CONGESTION socket option... " >&6; }
diff --git a/configure.ac b/configure.ac
index 17f12e7..faae23f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,6 +25,7 @@
# Initialize the autoconf system for the specified tool, version and mailing list
AC_INIT(iperf, 3-CURRENT, https://github.com/esnet/iperf, iperf, http://software.es.net/iperf/)
+m4_include([ax_check_openssl.m4])
AC_LANG(C)
# Specify where the auxiliary files created by configure should go. The config
@@ -97,6 +98,14 @@ AC_CHECK_HEADERS([netinet/sctp.h],
#endif
])
+# Check for OPENSSL support
+AX_CHECK_OPENSSL(
+ AC_DEFINE([HAVE_SSL], [1], [OpenSSL Is Available])
+)
+LDFLAGS="$LDFLAGS $OPENSSL_LDFLAGS"
+LIBS="$OPENSSL_LIBS $LIBS"
+CPPFLAGS="$OPENSSL_INCLUDES $CPPFLAGS"
+
# Check for TCP_CONGESTION sockopt (believed to be Linux and FreeBSD only)
AC_CACHE_CHECK([TCP_CONGESTION socket option],
[iperf3_cv_header_tcp_congestion],
diff --git a/docs/invoking.rst b/docs/invoking.rst
index 354b0b5..aa057b4 100644
--- a/docs/invoking.rst
+++ b/docs/invoking.rst
@@ -110,7 +110,15 @@ the executable.
-1, --one-off
handle one client connection, then exit.
-
+
+ --rsa-private-key-path (if built with OpenSSL support)
+ path to the RSA private key used to decrypt authentication
+ credentials (not password protected)
+
+ --authorized-users-path (if built with OpenSSL support)
+ path to the configuration file containing authorized users
+ credendientals to run iperf tests. File is a comma separated
+ list of usernames and password hashes.
CLIENT SPECIFIC OPTIONS
-c, --client host
@@ -235,8 +243,48 @@ the executable.
will be in human-readable format). If the client is run with
--json, the server output is included in a JSON object; other-
wise it is appended at the bottom of the human-readable output.
-
-
+
+ --username (if built with OpenSSL support)
+ username assigned by server adminitrators to access to the iperf
+ service.
+
+ --rsa-public-key-path (if built with OpenSSL support)
+ path to the RSA public key used to encrypt authentication
+ credentials
+
+ EXAMPLES
+
+ Authentication - RSA Keypair
+
+ Authentication feature requires a pair of public and private RSA
+ keys. The public key is used to encrypt the authentication
+ token containing the user credentials, the private key is used
+ to decrypt the authentication token.
+ An example of linux command to generate correct keypair follows:
+
+ $> openssl genrsa -des3 -out private.pem 2048
+ $> openssl rsa -in private.pem -outform PEM -pubout \
+ -out public.pem
+ $> openssl rsa -in private.pem -out private_not_protected.pem \
+ -outform PEM
+
+ Authentication - Authorized users configuration file
+
+ A simple plaintext file can be provided to iperf3 server in
+ order to specify the authorized user c redentials allowd to use
+ iperf3 server. File can contain commented lines (starting with
+ # char) and is a simple list of comma separated pair of
+ username password hash. Password hash is a sha256 hash of
+ string "{$user}$password":
+
+ $> S_USER=mario S_PASSWD=rossi
+ $> echo -ne "{$S_USER}$S_PASSWD"|sha256sum|awk '{ print $1 }'
+
+ $> cat credentials.csv
+ # file format: username,sha256
+ mario,44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c0....
+ $>
+
AUTHORS
A list of the contributors to iperf3 can be found within the documenta-
tion located at http://software.es.net/iperf/dev.html#authors.
@@ -247,7 +295,7 @@ the executable.
- ESnet January 2017 IPERF3(1)
+ ESnet Januar 2017 IPERF3(1)
The iperf3 manual page will typically be installed in manual
section 1.
diff --git a/iperf3.spec.in b/iperf3.spec.in
index e344b31..8d89c8b 100644
--- a/iperf3.spec.in
+++ b/iperf3.spec.in
@@ -10,7 +10,9 @@ Source0: http://stats.es.net/software/iperf-%{version}.tar.gz
BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
%if 0%{?el5}
-BuildRequires: e2fsprogs-devel
+BuildRequires: e2fsprogs-devel, openssl-devel
+%else
+BuildRequires: openssl-devel
%endif
%description
diff --git a/src/Makefile.in b/src/Makefile.in
index dca6cdd..dd29a2d 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -138,9 +138,9 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
LTLIBRARIES = $(lib_LTLIBRARIES)
libiperf_la_LIBADD =
am_libiperf_la_OBJECTS = cjson.lo iperf_api.lo iperf_error.lo \
- iperf_client_api.lo iperf_locale.lo iperf_server_api.lo \
- iperf_tcp.lo iperf_udp.lo iperf_sctp.lo iperf_util.lo dscp.lo \
- net.lo tcp_info.lo tcp_window_size.lo timer.lo units.lo
+ iperf_client_api.lo iperf_locale.lo iperf_auth.lo iperf_server_api.lo \
+ iperf_tcp.lo iperf_udp.lo iperf_sctp.lo iperf_util.lo dscp.lo net.lo \
+ tcp_info.lo tcp_window_size.lo timer.lo units.lo
libiperf_la_OBJECTS = $(am_libiperf_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -158,6 +158,7 @@ am__objects_1 = iperf3_profile-cjson.$(OBJEXT) \
iperf3_profile-iperf_error.$(OBJEXT) \
iperf3_profile-iperf_client_api.$(OBJEXT) \
iperf3_profile-iperf_locale.$(OBJEXT) \
+ iperf3_profile-iperf_auth.$(OBJEXT) \
iperf3_profile-iperf_server_api.$(OBJEXT) \
iperf3_profile-iperf_tcp.$(OBJEXT) \
iperf3_profile-iperf_udp.$(OBJEXT) \
@@ -574,7 +575,9 @@ libiperf_la_SOURCES = \
iperf_api.c \
iperf_api.h \
iperf_error.c \
- iperf_client_api.c \
+ iperf_auth.h \
+ iperf_auth.c \
+ iperf_client_api.c \
iperf_locale.c \
iperf_locale.h \
iperf_server_api.c \
@@ -582,14 +585,14 @@ libiperf_la_SOURCES = \
iperf_tcp.h \
iperf_udp.c \
iperf_udp.h \
- iperf_sctp.c \
- iperf_sctp.h \
+ iperf_sctp.c \
+ iperf_sctp.h \
iperf_util.c \
iperf_util.h \
dscp.c \
net.c \
net.h \
- portable_endian.h \
+ portable_endian.h \
queue.h \
tcp_info.c \
tcp_window_size.c \
@@ -813,6 +816,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_client_api.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_error.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_locale.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_auth.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_sctp.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_server_api.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf3_profile-iperf_tcp.Po@am__quote@
@@ -828,6 +832,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_client_api.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_error.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_locale.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_auth.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_sctp.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_server_api.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iperf_tcp.Plo@am__quote@
@@ -961,6 +966,22 @@ iperf3_profile-iperf_locale.obj: iperf_locale.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -c -o iperf3_profile-iperf_locale.obj `if test -f 'iperf_locale.c'; then $(CYGPATH_W) 'iperf_locale.c'; else $(CYGPATH_W) '$(srcdir)/iperf_locale.c'; fi`
+iperf3_profile-iperf_auth.o: iperf_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_auth.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_auth.Tpo -c -o iperf3_profile-iperf_auth.o `test -f 'iperf_auth.c' || echo '$(srcdir)/'`iperf_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_auth.Tpo $(DEPDIR)/iperf3_profile-iperf_auth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_auth.c' object='iperf3_profile-iperf_auth.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -c -o iperf3_profile-iperf_auth.o `test -f 'iperf_auth.c' || echo '$(srcdir)/'`iperf_auth.c
+
+iperf3_profile-iperf_auth.obj: iperf_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_auth.obj -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_auth.Tpo -c -o iperf3_profile-iperf_auth.obj `if test -f 'iperf_auth.c'; then $(CYGPATH_W) 'iperf_auth.c'; else $(CYGPATH_W) '$(srcdir)/iperf_auth.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_auth.Tpo $(DEPDIR)/iperf3_profile-iperf_auth.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='iperf_auth.c' object='iperf3_profile-iperf_auth.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -c -o iperf3_profile-iperf_auth.obj `if test -f 'iperf_auth.c'; then $(CYGPATH_W) 'iperf_auth.c'; else $(CYGPATH_W) '$(srcdir)/iperf_auth.c'; fi`
+
+
+
iperf3_profile-iperf_server_api.o: iperf_server_api.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(iperf3_profile_CFLAGS) $(CFLAGS) -MT iperf3_profile-iperf_server_api.o -MD -MP -MF $(DEPDIR)/iperf3_profile-iperf_server_api.Tpo -c -o iperf3_profile-iperf_server_api.o `test -f 'iperf_server_api.c' || echo '$(srcdir)/'`iperf_server_api.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/iperf3_profile-iperf_server_api.Tpo $(DEPDIR)/iperf3_profile-iperf_server_api.Po
diff --git a/src/iperf.h b/src/iperf.h
index 9ee7ea7..5073ad9 100755
--- a/src/iperf.h
+++ b/src/iperf.h
@@ -123,6 +123,7 @@ struct iperf_settings
iperf_size_t blocks; /* number of blocks (packets) to send */
char unit_format; /* -f */
int num_ostreams; /* SCTP initmsg settings */
+ char *authtoken; /* Authentication token */
};
struct iperf_test;
@@ -235,6 +236,8 @@ struct iperf_test
int prot_listener;
int ctrl_sck_mss; /* MSS for the control channel */
+ char *server_rsa_private_key;
+ char *server_authorized_users;
/* boolean variables for Options */
int daemon; /* -D option */
diff --git a/src/iperf3.1 b/src/iperf3.1
index 2d89f11..48d58e2 100644
--- a/src/iperf3.1
+++ b/src/iperf3.1
@@ -85,7 +85,14 @@ write a file with the process ID, most useful when running as a daemon.
.TP
.BR -1 ", " --one-off
handle one client connection, then exit.
-
+.TP
+.BR --rsa-private-key-path " \fIfile\fR" " (if built with OpenSSL support)
+path to the RSA private key used to decrypt authentication credentials (not
+password protected)
+.TP
+.BR --authorized-users-path " \fIfile\fR" " (if built with OpenSSL support)
+path to the configuration file containing authorized users credendientals to run
+iperf tests. File is a comma separated list of usernames and password hashes.
.SH "CLIENT SPECIFIC OPTIONS"
.TP
.BR -c ", " --client " \fIhost\fR"
@@ -101,7 +108,7 @@ use UDP rather than TCP
.TP
.BR -b ", " --bandwidth " \fIn\fR[KM]"
set target bandwidth to \fIn\fR bits/sec (default 1 Mbit/sec for UDP, unlimited for TCP).
-If there are multiple streams (\-P flag), the bandwidth limit is applied
+If there are multiple streams (\-P flag), the bandwidth limit is applied
separately to each stream.
You can also add a '/' and a number to the bandwidth specifier.
This is called "burst mode".
@@ -222,6 +229,45 @@ JSON format, otherwise it will be in human-readable format).
If the client is run with \fB--json\fR, the server output is included
in a JSON object; otherwise it is appended at the bottom of the
human-readable output.
+.TP
+.BR --username " \fIusername\fR" " (if built with OpenSSL support)
+username assigned by server adminitrators to access to the iperf service.
+.TP
+.BR --rsa-public-key-path " \fIfile\fR" " (if built with OpenSSL support)
+path to the RSA public key used to encrypt authentication credentials
+
+.SH EXAMPLES
+.TP
+.BR "Authentication - RSA Keypair"
+Authentication feature requires a pair of public and private RSA keys. The
+public key is used to encrypt the authentication token containing the
+user credentials, the private key is used to decrypt the authentication token.
+An example of linux command to generate correct keypair follows:
+.sp 1
+.in +.5i $> openssl genrsa -des3 -out private.pem 2048
+.sp 0
+$> openssl rsa -in private.pem -outform PEM -pubout -out public.pem
+.sp 0
+$> openssl rsa -in private.pem -out private_not_protected.pem -outform PEM
+.TP
+.BR "Authentication - Authorized users configuration file"
+A simple plaintext file can be provided to iperf3 server in order to specify
+the authorized user credentials allowd to use iperf3 server. File can contain
+commented lines (starting with # char) and is a simple list of comma separated
+pair of username password hash. Password hash is a sha256 hash of string
+"{$user}$password":
+.sp 1
+.in +.5i $> S_USER=mario S_PASSWD=rossi
+.sp 0
+$> echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'
+.sp 0
+$> cat credentials.csv
+.sp 0
+# file format: username,sha256
+.sp 0
+mario,44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c0....
+.sp 0
+$>
.SH AUTHORS
A list of the contributors to iperf3 can be found within the
diff --git a/src/iperf_api.c b/src/iperf_api.c
index 73b8e5a..61971ec 100755
--- a/src/iperf_api.c
+++ b/src/iperf_api.c
@@ -77,6 +77,9 @@
#include "iperf_util.h"
#include "iperf_locale.h"
#include "version.h"
+#if defined(HAVE_SSL)
+#include "iperf_auth.h"
+#endif /* HAVE_SSL */
/* Forwards. */
static int send_parameters(struct iperf_test *test);
@@ -671,6 +674,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"get-server-output", no_argument, NULL, OPT_GET_SERVER_OUTPUT},
{"udp-counters-64bit", no_argument, NULL, OPT_UDP_COUNTERS_64BIT},
{"no-fq-socket-pacing", no_argument, NULL, OPT_NO_FQ_SOCKET_PACING},
+#if defined(HAVE_SSL)
+ {"username", required_argument, NULL, OPT_CLIENT_USERNAME},
+ {"rsa-public-key-path", required_argument, NULL, OPT_CLIENT_RSA_PUBLIC_KEY},
+ {"rsa-private-key-path", required_argument, NULL, OPT_SERVER_RSA_PRIVATE_KEY},
+ {"authorized-users-path", required_argument, NULL, OPT_SERVER_AUTHORIZED_USERS},
+#endif /* HAVE_SSL */
{"fq-rate", required_argument, NULL, OPT_FQ_RATE},
{"debug", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
@@ -688,6 +697,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
blksize = 0;
server_flag = client_flag = rate_flag = duration_flag = 0;
+#if defined(HAVE_SSL)
+ char *client_username = NULL, *client_rsa_public_key = NULL;
+#endif /* HAVE_SSL */
+
while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) {
switch (flag) {
case 'p':
@@ -981,6 +994,20 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
return -1;
#endif
break;
+#if defined(HAVE_SSL)
+ case OPT_CLIENT_USERNAME:
+ client_username = strdup(optarg);
+ break;
+ case OPT_CLIENT_RSA_PUBLIC_KEY:
+ client_rsa_public_key = strdup(optarg);
+ break;
+ case OPT_SERVER_RSA_PRIVATE_KEY:
+ test->server_rsa_private_key = strdup(optarg);
+ break;
+ case OPT_SERVER_AUTHORIZED_USERS:
+ test->server_authorized_users = strdup(optarg);
+ break;
+#endif /* HAVE_SSL */
case 'h':
usage_long(stdout);
exit(0);
@@ -992,23 +1019,64 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
/* Set logging to a file if specified, otherwise use the default (stdout) */
if (test->logfile) {
- test->outfile = fopen(test->logfile, "a+");
- if (test->outfile == NULL) {
- i_errno = IELOGFILE;
- return -1;
- }
+ test->outfile = fopen(test->logfile, "a+");
+ if (test->outfile == NULL) {
+ i_errno = IELOGFILE;
+ return -1;
+ }
}
/* Check flag / role compatibility. */
if (test->role == 'c' && server_flag) {
- i_errno = IESERVERONLY;
- return -1;
+ i_errno = IESERVERONLY;
+ return -1;
}
if (test->role == 's' && client_flag) {
- i_errno = IECLIENTONLY;
- return -1;
+ i_errno = IECLIENTONLY;
+ return -1;
}
+#if defined(HAVE_SSL)
+
+ if (test->role == 's' && (client_username || client_rsa_public_key)){
+ i_errno = IECLIENTONLY;
+ return -1;
+ } else if (test->role == 'c' && (client_username || client_rsa_public_key) &&
+ !(client_username && client_rsa_public_key)) {
+ i_errno = IESETCLIENTAUTH;
+ return -1;
+ } else if (test->role == 'c' && (client_username && client_rsa_public_key)){
+
+ char *client_password = NULL;
+ size_t s;
+ if (iperf_getpass(&client_password, &s, stdin) < 0){
+ return -1;
+ }
+
+ if (strlen(client_username) > 20 || strlen(client_password) > 20){
+ i_errno = IESETCLIENTAUTH;
+ return -1;
+ }
+
+ if (test_load_pubkey(client_rsa_public_key) < 0){
+ i_errno = IESETCLIENTAUTH;
+ return -1;
+ }
+ encode_auth_setting(client_username, client_password, client_rsa_public_key, &test->settings->authtoken);
+ }
+
+ if (test->role == 'c' && (test->server_rsa_private_key || test->server_authorized_users)){
+ i_errno = IESERVERONLY;
+ return -1;
+ } else if (test->role == 's' && (test->server_rsa_private_key || test->server_authorized_users) &&
+ !(test->server_rsa_private_key && test->server_authorized_users)) {
+ i_errno = IESETSERVERAUTH;
+ return -1;
+ } else if (test->role == 's' && test->server_rsa_private_key && test_load_private_key(test->server_rsa_private_key) < 0){
+ i_errno = IESETSERVERAUTH;
+ return -1;
+ }
+#endif //HAVE_SSL
if (!test->bind_address && test->bind_port) {
i_errno = IEBIND;
return -1;
@@ -1235,6 +1303,29 @@ iperf_create_send_timers(struct iperf_test * test)
return 0;
}
+#if defined(HAVE_SSL)
+int test_is_authorized(struct iperf_test *test){
+ if ( !(test->server_rsa_private_key && test->server_authorized_users)) {
+ return 0;
+ }
+
+ if (test->settings->authtoken){
+ char *username = NULL, *password = NULL;
+ time_t ts;
+ decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts);
+ int ret = check_authentication(username, password, ts, test->server_authorized_users);
+ if (ret == 0){
+ iperf_printf(test, report_authetication_successed, username, ts);
+ return 0;
+ } else {
+ iperf_printf(test, report_authetication_failed, username, ts);
+ return -1;
+ }
+ }
+ return -1;
+}
+#endif //HAVE_SSL
+
/**
* iperf_exchange_parameters - handles the param_Exchange part for client
*
@@ -1256,8 +1347,22 @@ iperf_exchange_parameters(struct iperf_test *test)
if (get_parameters(test) < 0)
return -1;
+#if defined(HAVE_SSL)
+ if (test_is_authorized(test) < 0){
+ if (iperf_set_send_state(test, SERVER_ERROR) != 0)
+ return -1;
+ i_errno = IEAUTHTEST;
+ err = htonl(i_errno);
+ if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
+ i_errno = IECTRLWRITE;
+ return -1;
+ }
+ return -1;
+ }
+#endif //HAVE_SSL
+
if ((s = test->protocol->listen(test)) < 0) {
- if (iperf_set_send_state(test, SERVER_ERROR) != 0)
+ if (iperf_set_send_state(test, SERVER_ERROR) != 0)
return -1;
err = htonl(i_errno);
if (Nwrite(test->ctrl_sck, (char*) &err, sizeof(err), Ptcp) < 0) {
@@ -1366,7 +1471,10 @@ send_parameters(struct iperf_test *test)
cJSON_AddNumberToObject(j, "get_server_output", iperf_get_test_get_server_output(test));
if (test->udp_counters_64bit)
cJSON_AddNumberToObject(j, "udp_counters_64bit", iperf_get_test_udp_counters_64bit(test));
-
+#if defined(HAVE_SSL)
+ if (test->settings->authtoken)
+ cJSON_AddStringToObject(j, "authtoken", test->settings->authtoken);
+#endif // HAVE_SSL
cJSON_AddStringToObject(j, "client_version", IPERF_VERSION);
if (test->debug) {
@@ -1448,7 +1556,10 @@ get_parameters(struct iperf_test *test)
iperf_set_test_get_server_output(test, 1);
if ((j_p = cJSON_GetObjectItem(j, "udp_counters_64bit")) != NULL)
iperf_set_test_udp_counters_64bit(test, 1);
-
+#if defined(HAVE_SSL)
+ if ((j_p = cJSON_GetObjectItem(j, "authtoken")) != NULL)
+ test->settings->authtoken = strdup(j_p->valuestring);
+#endif //HAVE_SSL
if (test->sender && test->protocol->id == Ptcp && has_tcpinfo_retransmits())
test->sender_has_retransmits = 1;
cJSON_Delete(j);
@@ -2764,8 +2875,18 @@ iperf_new_stream(struct iperf_test *test, int s)
if (test->tmp_template) {
snprintf(template, sizeof(template) / sizeof(char), "%s", test->tmp_template);
} else {
- char buf[] = "/tmp/iperf3.XXXXXX";
- snprintf(template, sizeof(template) / sizeof(char), "%s", buf);
+ //find the system temporary dir *unix, windows, cygwin support
+ char* tempdir = getenv("TMPDIR");
+ if (tempdir == 0){
+ tempdir = getenv("TEMP");
+ }
+ if (tempdir == 0){
+ tempdir = getenv("TMP");
+ }
+ if (tempdir == 0){
+ tempdir = "/tmp";
+ }
+ snprintf(template, sizeof(template) / sizeof(char), "%s/iperf3.XXXXXX", tempdir);
}
sp = (struct iperf_stream *) malloc(sizeof(struct iperf_stream));
diff --git a/src/iperf_api.h b/src/iperf_api.h
index 80e169c..4b172c5 100755
--- a/src/iperf_api.h
+++ b/src/iperf_api.h
@@ -54,6 +54,10 @@ struct iperf_stream;
#define OPT_NO_FQ_SOCKET_PACING 9 /* UNUSED */
#define OPT_FQ_RATE 10
#define OPT_DSCP 11
+#define OPT_CLIENT_USERNAME 12
+#define OPT_CLIENT_RSA_PUBLIC_KEY 13
+#define OPT_SERVER_RSA_PRIVATE_KEY 14
+#define OPT_SERVER_AUTHORIZED_USERS 15
/* states */
#define TEST_START 1
@@ -295,6 +299,8 @@ enum {
IEBIND = 19, // Local port specified with no local bind option
IEUDPBLOCKSIZE = 20, // Block size invalid
IEBADTOS = 21, // Bad TOS value
+ IESETCLIENTAUTH = 22, // Bad configuration of client authentication
+ IESETSERVERAUTH = 23, // Bad configuration of server authentication
/* Test errors */
IENEWTEST = 100, // Unable to create a new test (check perror)
IEINITTEST = 101, // Test initialization failed (check perror)
@@ -338,6 +344,7 @@ enum {
IESETSCTPBINDX= 139, // Unable to process sctp_bindx() parameters
IESETPACING= 140, // Unable to set socket pacing rate
IESETBUF2= 141, // Socket buffer size incorrect (written value != read value)
+ IEAUTHTEST = 142, // Test authorization failed
/* Stream errors */
IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror)
IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror)
diff --git a/src/iperf_auth.c b/src/iperf_auth.c
new file mode 100644
index 0000000..d42a471
--- /dev/null
+++ b/src/iperf_auth.c
@@ -0,0 +1,312 @@
+/*
+ * iperf, Copyright (c) 2014-2017, The Regents of the University of
+ * California, through Lawrence Berkeley National Laboratory (subject
+ * to receipt of any required approvals from the U.S. Dept. of
+ * Energy). All rights reserved.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE file
+ * for complete information.
+ */
+
+#include "iperf_config.h"
+
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <termios.h>
+
+#if defined(HAVE_SSL)
+
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/sha.h>
+#include <openssl/buffer.h>
+
+void sha256(const char *string, char outputBuffer[65])
+{
+ unsigned char hash[SHA256_DIGEST_LENGTH];
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, string, strlen(string));
+ SHA256_Final(hash, &sha256);
+ int i = 0;
+ for(i = 0; i < SHA256_DIGEST_LENGTH; i++)
+ {
+ sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
+ }
+ outputBuffer[64] = 0;
+}
+
+int check_authentication(const char *username, const char *password, const time_t ts, const char *filename){
+ time_t t = time(NULL);
+ time_t utc_seconds = mktime(localtime(&t));
+ if ( (utc_seconds - ts) < 10 && (utc_seconds - ts) > 0 ){
+ return 1;
+ }
+
+ char passwordHash[65];
+ char salted[strlen(username) + strlen(password) + 3];
+ sprintf(salted, "{%s}%s", username, password);
+ sha256(&salted[0], passwordHash);
+
+ char *s_username, *s_password;
+ int i;
+ FILE *ptr_file;
+ char buf[1024];
+
+ ptr_file =fopen(filename,"r");
+ if (!ptr_file)
+ return 2;
+
+ while (fgets(buf,1024, ptr_file)){
+ //strip the \n or \r\n chars
+ for (i = 0; buf[i] != '\0'; i++){
+ if (buf[i] == '\n' || buf[i] == '\r'){
+ buf[i] = '\0';
+ break;
+ }
+ }
+ //skip empty / not completed / comment lines
+ if (strlen(buf) == 0 || strchr(buf, ',') == NULL || buf[0] == '#'){
+ continue;
+ }
+ s_username = strtok(buf, ",");
+ s_password = strtok(NULL, ",");
+ if (strcmp( username, s_username ) == 0 && strcmp( passwordHash, s_password ) == 0){
+ return 0;
+ }
+ }
+ return 3;
+}
+
+
+int Base64Encode(unsigned char* buffer, const size_t length, char** b64text) { //Encodes a binary safe base 64 string
+ BIO *bio, *b64;
+ BUF_MEM *bufferPtr;
+
+ b64 = BIO_new(BIO_f_base64());
+ bio = BIO_new(BIO_s_mem());
+ bio = BIO_push(b64, bio);
+
+ BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
+ BIO_write(bio, buffer, length);
+ BIO_flush(bio);
+ BIO_get_mem_ptr(bio, &bufferPtr);
+ BIO_set_close(bio, BIO_NOCLOSE);
+ BIO_free_all(bio);
+
+ *b64text=(*bufferPtr).data;
+ (*b64text)[(*bufferPtr).length] = '\0';
+ return (0); //success
+}
+
+size_t calcDecodeLength(const char* b64input) { //Calculates the length of a decoded string
+ size_t len = strlen(b64input), padding = 0;
+ if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
+ padding = 2;
+ else if (b64input[len-1] == '=') //last char is =
+ padding = 1;
+
+ return (len*3)/4 - padding;
+}
+
+int Base64Decode(char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string
+ BIO *bio, *b64;
+
+ int decodeLen = calcDecodeLength(b64message);
+ *buffer = (unsigned char*)malloc(decodeLen + 1);
+ (*buffer)[decodeLen] = '\0';
+
+ bio = BIO_new_mem_buf(b64message, -1);
+ b64 = BIO_new(BIO_f_base64());
+ bio = BIO_push(b64, bio);
+
+ BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
+ *length = BIO_read(bio, *buffer, strlen(b64message));
+ assert(*length == decodeLen); //length should equal decodeLen, else something went horribly wrong
+ BIO_free_all(bio);
+
+ return (0); //success
+}
+
+
+EVP_PKEY *load_pubkey(const char *file) {
+ BIO *key = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ key = BIO_new_file(file, "r");
+ pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL);
+
+ BIO_free(key);
+ return (pkey);
+}
+
+EVP_PKEY *load_key(const char *file) {
+ BIO *key = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ key = BIO_new_file(file, "r");
+ pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL);
+
+ BIO_free(key);
+ return (pkey);
+}
+
+
+int test_load_pubkey(const char *file){
+ EVP_PKEY *key = load_pubkey(file);
+ if (key == NULL){
+ return -1;
+ }
+ EVP_PKEY_free(key);
+ return 0;
+}
+
+int test_load_private_key(const char *file){
+ EVP_PKEY *key = load_key(file);
+ if (key == NULL){
+ return -1;
+ }
+ EVP_PKEY_free(key);
+ return 0;
+}
+
+int encrypt_rsa_message(const char *plaintext, const char *public_keyfile, unsigned char **encryptedtext) {
+ EVP_PKEY *public_key = NULL;
+ RSA *rsa = NULL;
+ unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
+ int keysize, encryptedtext_len, rsa_buffer_len;
+
+ public_key = load_pubkey(public_keyfile);
+ rsa = EVP_PKEY_get1_RSA(public_key);
+ EVP_PKEY_free(public_key);
+
+ keysize = RSA_size(rsa);
+ rsa_buffer = OPENSSL_malloc(keysize * 2);
+ *encryptedtext = (unsigned char*)OPENSSL_malloc(keysize);
+
+ BIO *bioBuff = BIO_new_mem_buf((void*)plaintext, (int)strlen(plaintext));
+ rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
+ encryptedtext_len = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, pad);
+
+ RSA_free(rsa);
+ OPENSSL_free(rsa_buffer);
+ OPENSSL_free(bioBuff);
+
+ return encryptedtext_len;
+}
+
+int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, const char *private_keyfile, unsigned char **plaintext) {
+ EVP_PKEY *private_key = NULL;
+ RSA *rsa = NULL;
+ unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
+ int plaintext_len, rsa_buffer_len, keysize;
+
+ private_key = load_key(private_keyfile);
+ rsa = EVP_PKEY_get1_RSA(private_key);
+ EVP_PKEY_free(private_key);
+
+ keysize = RSA_size(rsa);
+ rsa_buffer = OPENSSL_malloc(keysize * 2);
+ *plaintext = (unsigned char*)OPENSSL_malloc(keysize);
+
+ BIO *bioBuff = BIO_new_mem_buf((void*)encryptedtext, encryptedtext_len);
+ rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
+ plaintext_len = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, pad);
+
+ RSA_free(rsa);
+ OPENSSL_free(rsa_buffer);
+ OPENSSL_free(bioBuff);
+
+ return plaintext_len;
+}
+
+int encode_auth_setting(const char *username, const char *password, const char *public_keyfile, char **authtoken){
+ time_t t = time(NULL);
+ time_t utc_seconds = mktime(localtime(&t));
+ char text[150];
+ sprintf (text, "user: %s\npwd: %s\nts: %ld", username, password, utc_seconds);
+ unsigned char *encrypted = NULL;
+ int encrypted_len;
+ encrypted_len = encrypt_rsa_message(text, public_keyfile, &encrypted);
+ Base64Encode(encrypted, encrypted_len, authtoken);
+ return (0); //success
+}
+
+int decode_auth_setting(int enable_debug, char *authtoken, const char *private_keyfile, char **username, char **password, time_t *ts){
+ unsigned char *encrypted_b64 = NULL;
+ size_t encrypted_len_b64;
+ Base64Decode(authtoken, &encrypted_b64, &encrypted_len_b64);
+
+ unsigned char *plaintext = NULL;
+ int plaintext_len;
+ plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_keyfile, &plaintext);
+ plaintext[plaintext_len] = '\0';
+
+ char s_username[20], s_password[20];
+ sscanf ((char *)plaintext,"user: %s\npwd: %s\nts: %ld", s_username, s_password, ts);
+ if (enable_debug) {
+ printf("Auth Token Content:\n%s\n", plaintext);
+ printf("Auth Token Credentials:\n--> %s %s\n", s_username, s_password);
+ }
+ *username = (char *) calloc(21, sizeof(char *));
+ *password = (char *) calloc(21, sizeof(char *));
+ strncpy(*username, s_username, 20);
+ strncpy(*password, s_password, 20);
+ return (0);
+}
+
+#endif //HAVE_SSL
+
+ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream) {
+ struct termios old, new;
+ ssize_t nread;
+
+ /* Turn echoing off and fail if we can't. */
+ if (tcgetattr (fileno (stream), &old) != 0)
+ return -1;
+ new = old;
+ new.c_lflag &= ~ECHO;
+ if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0)
+ return -1;
+
+ /* Read the password. */
+ printf("Password: ");
+ nread = getline (lineptr, n, stream);
+
+ /* Restore terminal. */
+ (void) tcsetattr (fileno (stream), TCSAFLUSH, &old);
+
+ //strip the \n or \r\n chars
+ char *buf = *lineptr;
+ int i;
+ for (i = 0; buf[i] != '\0'; i++){
+ if (buf[i] == '\n' || buf[i] == '\r'){
+ buf[i] = '\0';
+ break;
+ }
+ }
+
+ return nread;
+}
+
+
diff --git a/src/iperf_auth.h b/src/iperf_auth.h
new file mode 100644
index 0000000..38971d8
--- /dev/null
+++ b/src/iperf_auth.h
@@ -0,0 +1,36 @@
+/*
+ * iperf, Copyright (c) 2014-2017, The Regents of the University of
+ * California, through Lawrence Berkeley National Laboratory (subject
+ * to receipt of any required approvals from the U.S. Dept. of
+ * Energy). All rights reserved.
+ *
+ * If you have questions about your rights to use or distribute this
+ * software, please contact Berkeley Lab's Technology Transfer
+ * Department at TTD@lbl.gov.
+ *
+ * NOTICE. This software is owned by the U.S. Department of Energy.
+ * As such, the U.S. Government has been granted for itself and others
+ * acting on its behalf a paid-up, nonexclusive, irrevocable,
+ * worldwide license in the Software to reproduce, prepare derivative
+ * works, and perform publicly and display publicly. Beginning five
+ * (5) years after the date permission to assert copyright is obtained
+ * from the U.S. Department of Energy, and subject to any subsequent
+ * five (5) year renewals, the U.S. Government is granted for itself
+ * and others acting on its behalf a paid-up, nonexclusive,
+ * irrevocable, worldwide license in the Software to reproduce,
+ * prepare derivative works, distribute copies to the public, perform
+ * publicly and display publicly, and to permit others to do so.
+ *
+ * This code is distributed under a BSD style license, see the LICENSE file
+ * for complete information.
+ */
+
+#include <time.h>
+#include <sys/types.h>
+
+int test_load_pubkey(const char *public_keyfile);
+int test_load_private_key(const char *private_keyfile);
+int encode_auth_setting(const char *username, const char *password, const char *public_keyfile, char **authtoken);
+int decode_auth_setting(int enable_debug, const char *authtoken, const char *private_keyfile, char **username, char **password, time_t *ts);
+int check_authentication(const char *username, const char *password, const time_t ts, const char *filename);
+ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream);
diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in
index 6da34e7..07e3e6c 100644
--- a/src/iperf_config.h.in
+++ b/src/iperf_config.h.in
@@ -30,6 +30,9 @@
/* Define to 1 if you have the `sendfile' function. */
#undef HAVE_SENDFILE
+/* Define to 1 if you have the OPENSSL suppoert */
+#undef HAVE_SSL
+
/* Have SO_MAX_PACING_RATE sockopt. */
#undef HAVE_SO_MAX_PACING_RATE
diff --git a/src/iperf_error.c b/src/iperf_error.c
index 90b91e0..3e20f1e 100644
--- a/src/iperf_error.c
+++ b/src/iperf_error.c
@@ -128,9 +128,15 @@ iperf_strerror(int i_errno)
case IEUDPBLOCKSIZE:
snprintf(errstr, len, "block size invalid (minimum = %d bytes, maximum = %d bytes)", MIN_UDP_BLOCKSIZE, MAX_UDP_BLOCKSIZE);
break;
- case IEBADTOS:
- snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)");
- break;
+ case IEBADTOS:
+ snprintf(errstr, len, "bad TOS value (must be between 0 and 255 inclusive)");
+ break;
+ case IESETCLIENTAUTH:
+ snprintf(errstr, len, "you must specify username (max 20 chars), password (max 20 chars) and a path to a valid public rsa client to be used");
+ break;
+ case IESETSERVERAUTH:
+ snprintf(errstr, len, "you must specify path to a valid private rsa server to be used and a user credential file");
+ break;
case IEMSS:
snprintf(errstr, len, "TCP MSS too large (maximum = %d bytes)", MAX_MSS);
break;
@@ -168,6 +174,9 @@ iperf_strerror(int i_errno)
snprintf(errstr, len, "test initialization failed");
perr = 1;
break;
+ case IEAUTHTEST:
+ snprintf(errstr, len, "test authorization failed");
+ break;
case IELISTEN:
snprintf(errstr, len, "unable to start listener for connections");
perr = 1;
diff --git a/src/iperf_locale.c b/src/iperf_locale.c
index 6277808..700218d 100644
--- a/src/iperf_locale.c
+++ b/src/iperf_locale.c
@@ -117,6 +117,11 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" -D, --daemon run the server as a daemon\n"
" -I, --pidfile file write PID file\n"
" -1, --one-off handle one client connection then exit\n"
+#if defined(HAVE_SSL)
+ " --rsa-private-key-path path to the RSA private key used to decrypt authentication credentials\n"
+ " --authorized-users-path path to the configuration file containing user credendial authorized to use iperf server\n"
+ " file with username and password comma separated, one per line\n"
+#endif //HAVE_SSL
"Client specific:\n"
" -c, --client <host> run in client mode, connecting to <host>\n"
#if defined(HAVE_SCTP)
@@ -158,7 +163,11 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" -T, --title str prefix every output line with this string\n"
" --get-server-output get results from server\n"
" --udp-counters-64bit use 64-bit counters in UDP test packets\n"
-
+#if defined(HAVE_SSL)
+ " --username username to access to the server test\n"
+ " --rsa-public-key-path path to the RSA public key used to encrypt authentication credentials\n"
+#endif //HAVESSL
+
#ifdef NOT_YET_SUPPORTED /* still working on these */
#endif
@@ -249,6 +258,12 @@ const char report_time[] =
const char report_connecting[] =
"Connecting to host %s, port %d\n";
+const char report_authetication_successed[] =
+"Authentication successed for user '%s' ts %ld\n";
+
+const char report_authetication_failed[] =
+"Authentication failed for user '%s' ts %ld\n";
+
const char report_reverse[] =
"Reverse mode, remote host %s is sending\n";
diff --git a/src/iperf_locale.h b/src/iperf_locale.h
index 4bfd99b..e1c8383 100644
--- a/src/iperf_locale.h
+++ b/src/iperf_locale.h
@@ -54,6 +54,8 @@ extern const char report_reverse[] ;
extern const char report_accepted[] ;
extern const char report_cookie[] ;
extern const char report_connected[] ;
+extern const char report_authetication_successed[] ;
+extern const char report_authetication_failed[] ;
extern const char report_window[] ;
extern const char report_autotune[] ;
extern const char report_omit_done[] ;