From 064c6cde3a7891bd60126b4eb044236565400833 Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Sat, 22 Jan 2005 23:11:13 +0000 Subject: [PATCH] First checkin of the test suite --- test/Makefile | 19 ++++ test/README | 27 +++++ test/auth.c | 215 ++++++++++++++++++++++++++++++++++ test/forward.c | 272 ++++++++++++++++++++++++++++++++++++++++++++ test/libssh2-test.h | 102 +++++++++++++++++ test/libssh2.h | 0 test/main.c | 174 ++++++++++++++++++++++++++++ test/methods.c | 219 +++++++++++++++++++++++++++++++++++ test/util.c | 222 ++++++++++++++++++++++++++++++++++++ 9 files changed, 1250 insertions(+) create mode 100644 test/Makefile create mode 100644 test/README create mode 100644 test/auth.c create mode 100644 test/forward.c create mode 100644 test/libssh2-test.h create mode 100644 test/libssh2.h create mode 100644 test/main.c create mode 100644 test/methods.c create mode 100644 test/util.c diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 00000000..8eaad226 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,19 @@ +LIBS = -L../src -lssh2 +CFLAGS = -c -I../include +CC = gcc -Wall -g +OBJS = main.o util.o methods.o auth.o forward.o + +all: libssh2-test + +libssh2-test: $(OBJS) + $(CC) -o libssh2-test $(OBJS) $(LIBS) + + +main.o: main.c libssh2.h +util.o: util.c libssh2.h +methods.o: methods.c libssh2.h +auth.o: auth.c libssh2.h +forward.o: forward.c libssh2.h + +clean: + rm -f $(OBJS) libssh2-test diff --git a/test/README b/test/README new file mode 100644 index 00000000..41256c72 --- /dev/null +++ b/test/README @@ -0,0 +1,27 @@ +This is meant to test the libssh2 code's protocol compliance against other +implementations. If you want to add a test, you will need to do the following: + +* create a new source file, and add it to the Makefile + +* call your main function (runtest_yourtest) from main() in main.c + +* at the top of your source file, put: + + #include + #include "libssh2-test.h" + + extern struct authdefs auth; + + +* call init_test("description of your test", number_of_steps) + +* before every step in your test, call increase_progress() + +* after every step, if it was successful, call step_successful() + + + + + + + diff --git a/test/auth.c b/test/auth.c new file mode 100644 index 00000000..13fbb23e --- /dev/null +++ b/test/auth.c @@ -0,0 +1,215 @@ +/* + * auth.c -- test authentication methods + * + * Copyright (C) 2005 Bert Vermeulen + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 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. + * + * Neither the name of the copyright holder nor the names + * of any other 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. + */ + +#include +#include +#include +#include + +#include +#include "libssh2-test.h" + +extern struct authdefs auth; + + +static int auth_publickey(LIBSSH2_SESSION *session) +{ + + if(libssh2_userauth_publickey_fromfile(session, auth.username, + auth.pubkey, auth.privkey, auth.passphrase)) + { + log_line(ERROR, "Public key authentication failed\n"); + return(0); + } + + if(!libssh2_userauth_authenticated(session)) + { + log_line(ERROR, "Public key authentication succeeded, but authentication not set\n"); + return(0); + } + + return(1); +} + + +static int auth_password(LIBSSH2_SESSION *session) +{ + + if(libssh2_userauth_password(session, auth.username, auth.password)) + { + log_line(ERROR, "Password authentication failed\n"); + return(0); + } + + if(!libssh2_userauth_authenticated(session)) + { + log_line(ERROR, "Password authentication succeeded, but authentication not set\n"); + return(0); + } + + return(1); +} + + +static void all_auth(void) +{ + LIBSSH2_SESSION *session; + int sock, size, res, sum, i; + unsigned char *hash; + char *errmsg, *_authlist, *authlist, *authmethod, *sep; + + authlist = NULL; + authmethod = ""; + while(authmethod) + { + sock = new_socket(); + if(sock == -1) + { + log_line(ERROR, "new_socket() failed\n"); + return; + } + + session = libssh2_session_init(); + + res = libssh2_session_startup(session, sock); + if(res) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "session_startup() failed: %s\n", errmsg); + close(sock); + return; + } + + if(!authlist) + { + _authlist = libssh2_userauth_list(session, auth.username, strlen(auth.username)); + if(_authlist == NULL) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "userauth_list() failed: %s\n", errmsg); + libssh2_session_disconnect(session, "All done."); + libssh2_session_free(session); + close(sock); + return; + } + + authlist = strdup(_authlist); + authmethod = authlist; + + /* only need to check hostkey hashes once... might as well do that here */ + increase_progress(); + hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5); + if(hash) + { + sum = 0; + for(i = 0; i < 16; i++) + sum += hash[i]; + if(sum > 0) + step_successful(); + else + log_line(ERROR, "MD5 hostkey hash invalid\n"); + } + else + log_line(ERROR, "MD5 hostkey hash failed\n"); + + increase_progress(); + hash = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); + if(hash) + { + sum = 0; + for(i = 0; i < 20; i++) + sum += hash[i]; + if(sum > 0) + step_successful(); + else + log_line(ERROR, "SHA1 hostkey hash invalid\n"); + } + else + log_line(ERROR, "SHA1 hostkey hash failed\n"); + + } + + if( (sep = strchr(authmethod, ',')) ) + *sep++ = '\0'; + + if(!strcasecmp(authmethod, "publickey")) + { + increase_progress(); + if(auth_publickey(session)) + step_successful(); + } + else if(!strcasecmp(authmethod, "password")) + { + increase_progress(); + if(auth_password(session)) + step_successful(); + } + else if(!strcasecmp(authmethod, "keyboard-interactive")) + { + /* no idea how to test this */ + } + else + { + log_line(DEBUG, "Unknown authentication method %s\n", authmethod); + } + + authmethod = sep; + + libssh2_session_disconnect(session, "All done."); + libssh2_session_free(session); + close(sock); + } + + free(authlist); + + printf("\n"); + +} + + +void runtest_auth(void) +{ + + init_test("authentication", 4); + + all_auth(); + +} + diff --git a/test/forward.c b/test/forward.c new file mode 100644 index 00000000..6069e81d --- /dev/null +++ b/test/forward.c @@ -0,0 +1,272 @@ +/* + * forward.c -- ssh2 port forwarding test + * + * Copyright (C) 2005 Bert Vermeulen + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 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. + * + * Neither the name of the copyright holder nor the names + * of any other 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libssh2-test.h" + +struct authdefs auth; +extern struct addrinfo *cur_ai; + +#define LISTEN_PORT 22617 +#define TESTBUF_SIZE 10 + + +static int loopback(LIBSSH2_SESSION *session, int hostbind, int portbind) +{ + LIBSSH2_CHANNEL *inbound, *outbound; + LIBSSH2_LISTENER *listener; + int listen_port, size, i, res; + char paramstr[64], ipstr[128], *errmsg, *host, *sendbuf, *recvbuf; + + snprintf(paramstr, 64, "(%shost bind, %sport bind)", hostbind ? "" : "no ", portbind ? "" : "no "); + + host = NULL; + if(hostbind) + { + if(getnameinfo(cur_ai->ai_addr, cur_ai->ai_addrlen, ipstr, sizeof(ipstr), NULL, 0, NI_NUMERICHOST)) + { + log_line(ERROR, "getnameinfo() failed\n"); + return(0); + } + host = ipstr; + } + + listen_port = 0; + if(portbind) + listen_port = LISTEN_PORT; + + listener = libssh2_channel_forward_listen_ex(session, host, listen_port, &listen_port, 2); + if(!listener) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "Listen failed %s: %s\n", paramstr, errmsg); + + return(0); + } + + outbound = libssh2_channel_direct_tcpip(session, auth.hostname, listen_port); + if(!outbound) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "Outbound channel setup failed %s: %s\n", paramstr, errmsg); + + libssh2_channel_forward_cancel(listener); + + return(0); + } + + inbound = libssh2_channel_forward_accept(listener); + if(!inbound) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "Forwarding channel accept failed %s: %s\n", paramstr, errmsg); + + libssh2_channel_free(outbound); + libssh2_channel_forward_cancel(listener); + + return(0); + } + + sendbuf = malloc(TESTBUF_SIZE); + if(!sendbuf) + { + log_line(ERROR, "sendbuf malloc failed\n"); + + libssh2_channel_free(inbound); + libssh2_channel_free(outbound); + libssh2_channel_forward_cancel(listener); + + return(0); + } + + for(i = 0; i < TESTBUF_SIZE; i++) + sendbuf[i] = (char) random; + + res = libssh2_channel_write(outbound, sendbuf, TESTBUF_SIZE); + if(res != TESTBUF_SIZE) + { + if(res == -1) + libssh2_session_last_error(session, &errmsg, &size, 0); + else + errmsg = NULL; + log_line(ERROR, "Unable to send %d bytes across tunnel %s%s%s\n", + TESTBUF_SIZE, paramstr, errmsg ? ": " : "", errmsg ? errmsg : ""); + + free(sendbuf); + libssh2_channel_free(inbound); + libssh2_channel_free(outbound); + libssh2_channel_forward_cancel(listener); + + return(0); + } + + recvbuf = malloc(TESTBUF_SIZE); + if(!recvbuf) + { + log_line(ERROR, "recvbuf malloc failed\n"); + + free(sendbuf); + libssh2_channel_free(inbound); + libssh2_channel_free(outbound); + libssh2_channel_forward_cancel(listener); + + return(0); + } + + res = libssh2_channel_read(inbound, recvbuf, TESTBUF_SIZE); + if(res != TESTBUF_SIZE) + { + if(res == -1) + libssh2_session_last_error(session, &errmsg, &size, 0); + else + errmsg = NULL; + log_line(ERROR, "Unable to receive %d bytes across tunnel %s%s%s\n", + TESTBUF_SIZE, paramstr, errmsg ? ": " : "", errmsg ? errmsg : ""); + + free(sendbuf); + free(recvbuf); + libssh2_channel_free(inbound); + libssh2_channel_free(outbound); + libssh2_channel_forward_cancel(listener); + + return(0); + } + + res = 1; + for(i = 0; i < TESTBUF_SIZE; i++) + { + if(recvbuf[i] != sendbuf[i]) + { + log_line(ERROR, "Received data did not match sent data %s\n", paramstr); + + res = 0; + break; + } + } + + free(sendbuf); + free(recvbuf); + libssh2_channel_free(inbound); + libssh2_channel_free(outbound); + libssh2_channel_forward_cancel(listener); + + return(res); +} + + +static void all_forward(void) +{ + LIBSSH2_SESSION *session; + int sock, res, size; + char *errmsg; + + sock = new_socket(); + if(sock == -1) + { + log_line(ERROR, "Unable to open a socket\n"); + return; + } + + session = libssh2_session_init(); + + res = libssh2_session_startup(session, sock); + if(res) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "Session startup failed: %s\n", errmsg); + close(sock); + return; + } + + if(libssh2_userauth_password(session, auth.username, auth.password)) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "Authentication failed%s%s\n", errmsg[0] ? ": " : "", errmsg); + libssh2_session_disconnect(session, "All done."); + libssh2_session_free(session); + close(sock); + return; + } + + increase_progress(); + if(loopback(session, 1, 1)) + step_successful(); + + increase_progress(); + if(loopback(session, 1, 0)) + step_successful(); + + increase_progress(); + if(loopback(session, 0, 1)) + step_successful(); + + increase_progress(); + if(loopback(session, 0, 0)) + step_successful(); + + libssh2_session_disconnect(session, "All done."); + libssh2_session_free(session); + close(sock); + printf("\n"); + +} + + + + + +void runtest_forward(void) +{ + + init_test("TCP port forwarding/tunneling", 4); + + all_forward(); + +} diff --git a/test/libssh2-test.h b/test/libssh2-test.h new file mode 100644 index 00000000..bd49a63a --- /dev/null +++ b/test/libssh2-test.h @@ -0,0 +1,102 @@ +/* + * prototest.h + * + * Copyright (C) 2005 Bert Vermeulen + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 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. + * + * Neither the name of the copyright holder nor the names + * of any other 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 LIBSSH2_TEST_H +#define LIBSSH2_TEST_H 1 + +#define MAX_LOGLINE_LEN 256 + + +void log_line(int priority, char *format, ...); +void init_test(char *msg, int num_items); +void increase_progress(void); +void step_successful(void); +void output_testresults(void); +int new_socket(void); +void runtest_methods(void); +void runtest_auth(void); +void runtest_forward(void); + + + +struct authdefs { + char *hostname; + char *port; + char *username; + char *password; + char *privkey; + char *pubkey; + char *passphrase; +}; + +struct methodlist { + int method_type; + char *description; + char **list; + int cursor; + int done; +}; + +struct logentry { + int priority; + char *logline; + struct logentry *next; +}; + +struct logresults { + char *description; + int num_steps; + int success_steps; + int progress; + struct logentry *log; + struct logresults *next; +}; + + +enum { + ERROR, + WARNING, + NORMAL, + DEBUG +}; + + + +#endif diff --git a/test/libssh2.h b/test/libssh2.h new file mode 100644 index 00000000..e69de29b diff --git a/test/main.c b/test/main.c new file mode 100644 index 00000000..3472ee00 --- /dev/null +++ b/test/main.c @@ -0,0 +1,174 @@ +/* + * main.c -- ssh2 protocol compliance tester for libssh2 + * + * Copyright (C) 2005 Bert Vermeulen + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 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. + * + * Neither the name of the copyright holder nor the names + * of any other 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libssh2-test.h" + +struct authdefs auth; +char *progress_message; +int progress, progress_max; +struct addrinfo *hostai = NULL, *cur_ai = NULL; + +extern struct logresults *testlogs; +extern struct logentry *logfile; + + + +void cleanup(void) +{ + struct logresults *test, *nexttest; + struct logentry *logfile, *nextlog; + + if(hostai) + freeaddrinfo(hostai); + + test = testlogs; + while(test) + { + logfile = test->log; + while(logfile) + { + nextlog = logfile->next; + free(logfile->logline); + free(logfile); + + logfile = nextlog; + } + nexttest = test->next; + free(test); + + test = nexttest; + } + + free(auth.hostname); + free(auth.port); + free(auth.username); + free(auth.password); + free(auth.privkey); + free(auth.pubkey); + free(auth.passphrase); + +} + + +char *get_interactive(char *prompt, int size, char *default_value) +{ + char *str; + + if( !(str = malloc(size)) ) + { + log_line(ERROR, "unable to malloc %d bytes for %s\n", size, prompt); + return(NULL); + } + + printf("%s [%s]: ", prompt, default_value); + fgets(str, size, stdin); + if(str[strlen(str)-1] == '\n') + str[strlen(str)-1] = 0; + if(!str[0]) + strncpy(str, default_value, size); + + return(str); +} + + +char *resolve_tilde(char *path) +{ + wordexp_t we; + + if( (wordexp(path, &we, 0)) == 0 && we.we_wordc == 1) + { + free(path); + path = strdup(we.we_wordv[0]); + wordfree(&we); + } + + return(path); +} + + +void get_auth(void) +{ + + auth.hostname = get_interactive("hostname", 64, "localhost"); + auth.port = get_interactive("port", 6, "22"); +// auth.username = get_interactive("username", 20, getenv("USER")); +// auth.password = get_interactive("password", 20, ""); + auth.username = get_interactive("username", 20, "bert2"); + auth.password = get_interactive("password", 20, "blinko"); + + auth.privkey = resolve_tilde(get_interactive("private key filename", 128, "~/.ssh/id_dsa")); + auth.pubkey = resolve_tilde(get_interactive("public key filename", 128, "~/.ssh/id_dsa.pub")); + auth.passphrase = get_interactive("passphrase", 256, ""); + +} + + +int main(int argc, char **argv) +{ + + get_auth(); + if(!strlen(auth.username) || !strlen(auth.password)) + { + printf("Not enough authentication info to continue.\n"); + return(1); + } + + runtest_methods(); + runtest_auth(); + runtest_forward(); + + output_testresults(); + + cleanup(); + + return(0); +} + diff --git a/test/methods.c b/test/methods.c new file mode 100644 index 00000000..41e45cd4 --- /dev/null +++ b/test/methods.c @@ -0,0 +1,219 @@ +/* + * methods.c -- test all available key exchange, hostkey, encryption, mac and compression methods + * + * Copyright (C) 2005 Bert Vermeulen + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 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. + * + * Neither the name of the copyright holder nor the names + * of any other 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. + */ + +#include +#include + +#include +#include "libssh2-test.h" + +extern struct authdefs auth; + + +static char *kex_methods[] = { + "diffie-hellman-group1-sha1", + "diffie-hellman-group14-sha1", + "diffie-hellman-group-exchange-sha1", + NULL +}; + +static char *hostkey_methods[] = { + "ssh-dss", + "ssh-rsa", + NULL +}; + +static char *crypt_methods[] = { + "aes256-cbc", + "aes192-cbc", + "aes128-cbc", + "blowfish-cbc", + "arcfour", + "cast128-cbc", + "3des-cbc", + NULL +}; + +static char *mac_methods[] = { + "hmac-sha1", + "hmac-sha1-96", + "hmac-md5", + "hmac-md5-96", + "hmac-ripemd160", + NULL +}; + +static char *compression_methods[] = { + "zlib", + NULL +}; + + +static struct methodlist methods[] = { + { LIBSSH2_METHOD_KEX, "kex", kex_methods, 0, 0 }, + { LIBSSH2_METHOD_HOSTKEY, "hostkey", hostkey_methods, 0, 0 }, + { LIBSSH2_METHOD_CRYPT_CS, "crypt (cs)", crypt_methods, 0, 0 }, + { LIBSSH2_METHOD_CRYPT_SC, "crypt (sc)", crypt_methods, 0, 0 }, + { LIBSSH2_METHOD_MAC_CS, "MAC (cs)", mac_methods, 0, 0 }, + { LIBSSH2_METHOD_MAC_SC, "MAC (sc)", mac_methods, 0, 0 }, + { LIBSSH2_METHOD_COMP_CS, "compression (cs)", compression_methods, 0, 0 }, + { LIBSSH2_METHOD_COMP_SC, "compression (sc)", compression_methods, 0, 0 }, + { 0, NULL, NULL, 0, 0 } +}; + + +/* +static void dump_methods(LIBSSH2_SESSION *session) +{ + + printf(" Key exchange methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_KEX)); + printf(" Hostkey methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY)); + printf(" Crypt C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS)); + printf(" Crypt S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC)); + printf(" MAC C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS)); + printf(" MAC S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC)); + printf("Compression C->S methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS)); + printf("Compression S->C methods: %s\n", libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC)); + +} +*/ + + +static void cycle_methods(void) +{ + LIBSSH2_SESSION *session; + int sock, size, methods_done, i, res; + struct methodlist *method; + char methodstring[256], *errmsg; + + while(1) + { + increase_progress(); + sock = new_socket(); + if(sock == -1) + { + log_line(ERROR, "new_socket() failed"); + return; + } + + session = libssh2_session_init(); + + i = 0; + methodstring[0] = '\0'; + while(methods[i].description) + { + method = &methods[i]; + strncat(methodstring, method->list[method->cursor], 256); + strncat(methodstring, " ", 256); + res = libssh2_session_method_pref(session, method->method_type, method->list[method->cursor]); + if(res != 0) + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "%s method set to '%s' failed: %s\n", + method->description, method->list[method->cursor], errmsg); + return; + } + + i++; + } + + res = libssh2_session_startup(session, sock); + if(res == 0) + { + if(libssh2_userauth_password(session, auth.username, auth.password)) + { + log_line(ERROR, "Authentication failed\n"); + } + else + step_successful(); + } + else + { + libssh2_session_last_error(session, &errmsg, &size, 0); + log_line(ERROR, "session startup with methods [ %s] failed: %s\n", methodstring, errmsg); + } + + libssh2_session_disconnect(session, "All done."); + libssh2_session_free(session); + close(sock); + + /* increment method cursors */ + i = 0; + methods_done = 0; + while( (method = &methods[i++]) && method->description ) + { + if(!method->list[++method->cursor]) + { + method->done = 1; + method->cursor = 0; + } + + methods_done += method->done; + } + if(--i == methods_done) + break; + + } + printf("\n"); + +} + + +void runtest_methods(void) +{ + struct methodlist *method; + int i, j, max; + + max = 0; + for(i = 0; methods[i].description; i++) + { + method = &methods[i]; + for(j = 0; method->list[j]; j++) + ; + if(j > max) + max = j; + } + + init_test("kex/hostkey/crypt/max/compression methods", max); + + cycle_methods(); + +} + diff --git a/test/util.c b/test/util.c new file mode 100644 index 00000000..2613e7d6 --- /dev/null +++ b/test/util.c @@ -0,0 +1,222 @@ +/* + * util.c + * + * Copyright (C) 2005 Bert Vermeulen + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 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. + * + * Neither the name of the copyright holder nor the names + * of any other 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "libssh2-test.h" + + +extern struct addrinfo *hostai, *cur_ai; +struct logresults *testlogs = NULL, *cur_testlog; +struct logentry *logfile = NULL, *cur_logentry; + +extern struct authdefs auth; +extern char *progress_message; +extern int progress, progress_max; + + +struct loglevel { + int priority; + char *descr; +} loglevels[] = { + { ERROR, "ERROR" }, + { WARNING, "WARNING" }, + { NORMAL, "NORMAL" }, + { DEBUG, "DEBUG" } +}; + + + +void log_line(int priority, char *format, ...) +{ + va_list args; + struct logentry *entry; + char line[MAX_LOGLINE_LEN]; + + va_start(args, format); + vsnprintf(line, MAX_LOGLINE_LEN, format, args); + va_end(args); + + entry = malloc(sizeof(struct logentry)); + entry->priority = priority; + entry->logline = malloc(strlen(line)+1); + strcpy(entry->logline, line); + entry->next = NULL; + + if(!cur_testlog->log) + cur_testlog->log = entry; + else + cur_logentry->next = entry; + + cur_logentry = entry; + +} + + +void init_test(char *msg, int num_items) +{ + struct logresults *newtest; + + newtest = malloc(sizeof(struct logresults)); + newtest->description = msg; + newtest->num_steps = num_items; + newtest->success_steps = 0; + newtest->progress = 0; + newtest->log = NULL; + newtest->next = NULL; + + if(!testlogs) + testlogs = newtest; + else + cur_testlog->next = newtest; + + cur_testlog = newtest; + +} + + +void increase_progress(void) +{ + + cur_testlog->progress++; + + printf("Testing %s... %3d/%d\r", cur_testlog->description, cur_testlog->progress, cur_testlog->num_steps); + fflush(stdout); + +} + + +void step_successful(void) +{ + + cur_testlog->success_steps++; + +} + + +void output_testresults(void) +{ + struct logresults *test; + struct logentry *logfile; + int total_steps, total_success; + + printf("\nTest results\n============\n"); + + total_steps = 0; + total_success = 0; + test = testlogs; + while(test) + { + total_steps += test->num_steps; + total_success += test->success_steps; + printf("Test: %s (%d/%d)\n", test->description, test->success_steps, test->num_steps); + logfile = test->log; + while(logfile) + { + printf(" %s", logfile->logline); + logfile = logfile->next; + } + + test = test->next; + } + + printf("%d/%d steps successful\n", total_success, total_steps); + +} + + +int new_socket(void) +{ + int sock, res; + struct addrinfo hints, *ai; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + + if(!hostai) + { + res = getaddrinfo(auth.hostname, auth.port, &hints, &hostai); + if(res) + { + printf("unable to resolve %s: %s\n", auth.hostname, gai_strerror(res)); + return(-1); + } + } + + sock = 0; + ai = hostai; + while(ai) + { + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if(sock > 0) + { + res = connect(sock, ai->ai_addr, ai->ai_addrlen); + if(res == 0) + break; + + close(sock); + sock = 0; + } + ai = ai->ai_next; + } + + if(!sock) + { + printf("unable to connect: %s\n", strerror(errno)); + close(sock); + sock = -1; + } + + cur_ai = ai; + + return(sock); +} + +