diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.in | 35 | ||||
-rwxr-xr-x | src/iperf.h | 3 | ||||
-rw-r--r-- | src/iperf3.1 | 50 | ||||
-rwxr-xr-x | src/iperf_api.c | 149 | ||||
-rwxr-xr-x | src/iperf_api.h | 7 | ||||
-rw-r--r-- | src/iperf_auth.c | 312 | ||||
-rw-r--r-- | src/iperf_auth.h | 36 | ||||
-rw-r--r-- | src/iperf_config.h.in | 3 | ||||
-rw-r--r-- | src/iperf_error.c | 15 | ||||
-rw-r--r-- | src/iperf_locale.c | 17 | ||||
-rw-r--r-- | src/iperf_locale.h | 2 |
11 files changed, 602 insertions, 27 deletions
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[] ; |