From a9d34b9602961cca78de160d8581576ed0b3f34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Fri, 14 Jul 2017 18:02:20 +0000 Subject: [PATCH] DBUS_WINDOWS_SSPI_NTLM custom test This is a simple socket client that connects to a dbus test server daemon and tries to authenticate using the DBUS_WINDOWS_SSPI_NTLM mechanism. The code that does authentication is completely independent from libdbus, and is completely bare. The test is explicitly Windows-only, as it drops all platform-independent abstractions that libdbus would have provided (not to mention the fact that DBUS_WINDOWS_SSPI_NTLM is Windows-only...). https://bugs.freedesktop.org/show_bug.cgi?id=96577 --- test/Makefile.am | 19 +++ test/test-auth-windows-sspi-corrupt.c | 300 ++++++++++++++++++++++++++++++++++ test/test-utils-auth.c | 212 ++++++++++++++++++++++++ test/test-utils-auth.h | 13 ++ 4 files changed, 544 insertions(+) create mode 100755 test/test-auth-windows-sspi-corrupt.c create mode 100755 test/test-utils-auth.c create mode 100755 test/test-utils-auth.h diff --git a/test/Makefile.am b/test/Makefile.am index e7889c9..17e645b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -152,6 +152,15 @@ dist_installable_test_scripts = \ if DBUS_WIN installable_manual_tests += manual-paths +if DBUS_WITH_GLIB +libdbus_testutils_la_SOURCES += \ + test-utils-auth.c \ + test-utils-auth.h \ + $(NULL) +installable_tests += \ + test-auth-windows-sspi-corrupt \ + $(NULL) +endif endif if DBUS_WITH_GLIB @@ -315,6 +324,16 @@ test_fdpass_LDADD = \ $(GLIB_LIBS) \ $(NULL) +if DBUS_WIN +test_auth_windows_sspi_corrupt_SOURCES = test-auth-windows-sspi-corrupt.c +test_auth_windows_sspi_corrupt_LDADD = \ + $(top_builddir)/dbus/libdbus-1.la \ + $(top_builddir)/dbus/libdbus-internal.la \ + libdbus-testutils.la \ + $(GLIB_LIBS) \ + $(NULL) +endif + TESTS += $(dist_installable_test_scripts) installcheck_tests += $(dist_installable_test_scripts) diff --git a/test/test-auth-windows-sspi-corrupt.c b/test/test-auth-windows-sspi-corrupt.c new file mode 100755 index 0000000..e87758f --- /dev/null +++ b/test/test-auth-windows-sspi-corrupt.c @@ -0,0 +1,300 @@ +#include + +#include + +#include + +#include +#include + +#include "test-utils-glib.h" +#include "test-utils-auth.h" + +typedef struct { + gboolean skip; + DBusError e; + TestMainContext *ctx; + + GPid daemon_pid; + DBusServer *server; + + gchar *address; + + DBusSocket sock; + + size_t message_limit; + CredHandle credentials; + DBusString plaintext; + CtxtHandle context; + int free_context; +} Fixture; + +typedef struct { + const char *bug_ref; + guint min_messages; + const char *config_file; + enum { SPECIFY_ADDRESS = 0, RELY_ON_DEFAULT } connect_mode; +} Config; + +static void +setup (Fixture *f, + gconstpointer context) +{ + const Config *config = context; + SECURITY_STATUS ss; + PSecPkgInfoW sec_package_info; + TimeStamp lifetime; + wchar_t ntlm_ssp_name[] = L"NTLM"; + + ss = QuerySecurityPackageInfoW (ntlm_ssp_name, &sec_package_info); + g_assert (ss == SEC_E_OK); + f->message_limit = sec_package_info->cbMaxToken; + ss = FreeContextBuffer (sec_package_info); + g_assert (ss == SEC_E_OK); + ss = AcquireCredentialsHandleW (NULL, ntlm_ssp_name, SECPKG_CRED_OUTBOUND, 0, 0, 0, 0, + &f->credentials, &lifetime); + g_assert (ss == SEC_E_OK); + g_assert (_dbus_string_init (&f->plaintext)); + _dbus_string_set_length (&f->plaintext, f->message_limit); + + f->ctx = test_main_context_get (); + dbus_error_init (&f->e); + + f->address = test_get_dbus_daemon (config ? config->config_file : NULL, + TEST_USER_ME, + &f->daemon_pid); + + if (f->address == NULL) + { + f->skip = TRUE; + return; + } + + f->sock = test_connect_to_bus_simple (f->ctx, f->address); + f->free_context = 0; +} + +static void +test_sspi_corrupt (Fixture *f, + gconstpointer context) +{ + DBusString outgoing; + DBusString incoming; + DBusString decoded; + SECURITY_STATUS ss; + TimeStamp lifetime; + SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer }; + ULONG attributes; + int bytes_written; + int bytes_read; + char *buffer; + SecBuffer input_sec_buffer = { 0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc input_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &input_sec_buffer }; + int end; + int to_read, total_read; + char zero; + + zero = 0; + bytes_written = send (f->sock.sock, &zero, 1, 0); + g_assert (bytes_written == 1); + + g_assert (_dbus_string_init (&outgoing)); + g_assert (_dbus_string_append (&outgoing, "AUTH DBUS_WINDOWS_SSPI_NTLM ")); + + output_sec_buffer.cbBuffer = _dbus_string_get_length (&f->plaintext); + output_sec_buffer.pvBuffer = _dbus_string_get_data (&f->plaintext); + + ss = InitializeSecurityContextW (&f->credentials, + NULL, + NULL, + ISC_REQ_IDENTIFY, + 0, + SECURITY_NATIVE_DREP, + NULL, + 0, + &f->context, + &output_sec_buffers_descriptor, + &attributes, + &lifetime); + + f->free_context = 1; + + g_assert (ss == SEC_E_OK || + ss == SEC_I_COMPLETE_AND_CONTINUE || + ss == SEC_I_COMPLETE_NEEDED || + ss == SEC_I_CONTINUE_NEEDED); + + if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss)) + { + ss = CompleteAuthToken (&f->context, + &output_sec_buffers_descriptor); + g_assert (ss == SEC_E_OK); + } + + _dbus_string_set_length (&f->plaintext, output_sec_buffer.cbBuffer); + + g_assert (_dbus_string_hex_encode (&f->plaintext, 0, &outgoing, + _dbus_string_get_length (&outgoing))); + + g_assert (_dbus_string_append (&outgoing, "\r\n")); + + bytes_written = send (f->sock.sock, + _dbus_string_get_data (&outgoing), _dbus_string_get_length (&outgoing), + 0); + g_assert (bytes_written == _dbus_string_get_length (&outgoing)); + _dbus_string_free (&outgoing); + +#define DATASTR "DATA " +#define DATASTRLEN strlen (DATASTR) + g_assert (_dbus_string_init (&incoming)); + g_assert (_dbus_string_set_length (&incoming, DATASTRLEN)); + buffer = _dbus_string_get_data (&incoming); + bytes_read = recv (f->sock.sock, buffer, DATASTRLEN, 0); + g_assert (bytes_read == DATASTRLEN); + g_assert (strncmp (buffer, DATASTR, DATASTRLEN) == 0); + + to_read = 0; + total_read = DATASTRLEN; + do + { + to_read += 100; + g_assert (_dbus_string_set_length (&incoming, to_read)); + buffer = _dbus_string_get_data (&incoming); + bytes_read = recv (f->sock.sock, &buffer[total_read], 100, 0); + g_assert (bytes_read > 0); + total_read += bytes_read; + if (bytes_read < 100) + break; + } while (TRUE); + g_assert (buffer[total_read - 1] == '\n' && + buffer[total_read - 2] == '\r'); + buffer[total_read - 1] = buffer[total_read - 2] = '\0'; + + g_assert (_dbus_string_init (&decoded)); + g_assert (_dbus_string_hex_decode (&incoming, DATASTRLEN, &end, &decoded, 0)); + g_assert (_dbus_string_get_length (&incoming) == end); + _dbus_string_free (&incoming); + + input_sec_buffer.cbBuffer = _dbus_string_get_length (&decoded); + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_data (&decoded); + + g_assert (input_sec_buffer.cbBuffer > 0); + + g_assert (_dbus_string_init (&outgoing)); + g_assert (_dbus_string_append (&outgoing, "DATA ")); + + _dbus_string_set_length (&f->plaintext, f->message_limit); + output_sec_buffer.cbBuffer = _dbus_string_get_length (&f->plaintext); + output_sec_buffer.pvBuffer = _dbus_string_get_data (&f->plaintext); + + ss = InitializeSecurityContextW (&f->credentials, + &f->context, + NULL, + ISC_REQ_IDENTIFY, + 0, + SECURITY_NATIVE_DREP, + &input_sec_buffers_descriptor, + 0, + &f->context, + &output_sec_buffers_descriptor, + &attributes, + &lifetime); + + g_assert (ss == SEC_E_OK || + ss == SEC_I_COMPLETE_AND_CONTINUE || + ss == SEC_I_COMPLETE_NEEDED || + ss == SEC_I_CONTINUE_NEEDED); + + if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss)) + { + ss = CompleteAuthToken (&f->context, + &output_sec_buffers_descriptor); + g_assert (ss == SEC_E_OK); + } + + _dbus_string_set_length (&f->plaintext, output_sec_buffer.cbBuffer); + + g_assert (_dbus_string_hex_encode (&f->plaintext, 0, &outgoing, + _dbus_string_get_length (&outgoing))); + + g_assert (_dbus_string_append (&outgoing, "\r\n")); + + bytes_written = send (f->sock.sock, + _dbus_string_get_data (&outgoing), _dbus_string_get_length (&outgoing), + 0); + g_assert (bytes_written == _dbus_string_get_length (&outgoing)); + _dbus_string_free (&outgoing); + +#undef DATASTR +#undef DATASTRLEN + +#define OKSTR "OK " +#define OKSTRLEN strlen (OKSTR) + + g_assert (_dbus_string_init (&incoming)); + g_assert (_dbus_string_set_length (&incoming, OKSTRLEN)); + buffer = _dbus_string_get_data (&incoming); + bytes_read = recv (f->sock.sock, buffer, OKSTRLEN, 0); + g_assert (bytes_read == OKSTRLEN); + g_assert (strncmp (buffer, OKSTR, OKSTRLEN) == 0); +#undef OKSTR +#undef OKSTRLEN + _dbus_string_free (&incoming); + + DeleteSecurityContext (&f->context); + f->free_context = 0; +} + +static void +teardown (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + SECURITY_STATUS ss; + + ss = FreeCredentialsHandle (&f->credentials); + g_assert (ss == SEC_E_OK); + _dbus_string_free (&f->plaintext); + + dbus_error_free (&f->e); + + if (_dbus_socket_is_valid (f->sock)) + { + _dbus_close_socket (f->sock, NULL); + _dbus_socket_invalidate (&f->sock); + } + + if (f->daemon_pid != 0) + { + test_kill_pid (f->daemon_pid); + g_spawn_close_pid (f->daemon_pid); + f->daemon_pid = 0; + } + + test_main_context_unref (f->ctx); + g_free (f->address); + + if (f->free_context) + { + DeleteSecurityContext (&f->context); + f->free_context = 0; + } +} + +static Config sspi_corrupt_config = { + "96577", 1, "valid-config-files/debug-auth-sspi.conf", + SPECIFY_ADDRESS +}; + +int +main (int argc, + char **argv) +{ + test_init (&argc, &argv); + + g_test_add ("/auth/sspi-corrupt", Fixture, &sspi_corrupt_config, setup, test_sspi_corrupt, teardown); + + return g_test_run (); + + return 0; +} diff --git a/test/test-utils-auth.c b/test/test-utils-auth.c new file mode 100755 index 0000000..f2208ea --- /dev/null +++ b/test/test-utils-auth.c @@ -0,0 +1,212 @@ +#include +#include "test-utils-auth.h" +#include "test-utils-glib.h" + +DBusSocket +_dbus_connect_simple_tcp_socket (const char *host, + const char *port, + const char *family); + +dbus_bool_t +_dbus_win_startup_winsock (void) +{ + /* Straight from MSDN, deuglified */ + + WORD wVersionRequested; + WSADATA wsaData; + int err; + + wVersionRequested = MAKEWORD (2, 0); + + err = WSAStartup (wVersionRequested, &wsaData); + if (err != 0) + { + _dbus_assert_not_reached ("Could not initialize WinSock"); + _dbus_abort (); + } + + /* Confirm that the WinSock DLL supports 2.0. Note that if the DLL + * supports versions greater than 2.0 in addition to 2.0, it will + * still return 2.0 in wVersion since that is the version we + * requested. + */ + if (LOBYTE (wsaData.wVersion) != 2 || + HIBYTE (wsaData.wVersion) != 0) + { + _dbus_assert_not_reached ("No usable WinSock found"); + _dbus_abort (); + } + + return TRUE; +} + +DBusSocket +_dbus_connect_simple_tcp_socket (const char *host, + const char *port, + const char *family) +{ + DBusSocket fd = DBUS_SOCKET_INIT; + int res; + struct addrinfo hints; + struct addrinfo *ai, *tmp; + + if (!_dbus_win_startup_winsock ()) + { + return _dbus_socket_get_invalid (); + } + + _DBUS_ZERO (hints); + + if (!family) + hints.ai_family = AF_UNSPEC; + else if (!strcmp(family, "ipv4")) + hints.ai_family = AF_INET; + else if (!strcmp(family, "ipv6")) + hints.ai_family = AF_INET6; + else + { + return _dbus_socket_get_invalid (); + } + hints.ai_protocol = IPPROTO_TCP; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +#else + hints.ai_flags = 0; +#endif + + if ((res = getaddrinfo (host, port, &hints, &ai)) != 0 || !ai) + { + return _dbus_socket_get_invalid (); + } + + tmp = ai; + while (tmp) + { + if ((fd.sock = socket (tmp->ai_family, SOCK_STREAM, 0)) == INVALID_SOCKET) + { + freeaddrinfo(ai); + return _dbus_socket_get_invalid (); + } + + if (connect (fd.sock, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR) + { + closesocket (fd.sock); + fd.sock = INVALID_SOCKET; + tmp = tmp->ai_next; + continue; + } + + break; + } + freeaddrinfo (ai); + + if (!_dbus_socket_is_valid (fd)) + { + return _dbus_socket_get_invalid (); + } + + /* Every SOCKET is also a HANDLE. */ + SetHandleInformation((HANDLE) fd.sock, + HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE, + 0 /*disable both flags*/ ); + + return fd; +} + +dbus_bool_t +_dbus_string_alloc_space (DBusString *str, + int extra_bytes) +{ + if (!_dbus_string_lengthen (str, extra_bytes)) + return FALSE; + _dbus_string_shorten (str, extra_bytes); + + return TRUE; +} + +DBusSocket +_dbus_socket_new_for_tcp (const char *host, + const char *port, + const char *family); + +DBusSocket +_dbus_socket_open (DBusAddressEntry *entry); + +DBusSocket +_dbus_socket_new_for_tcp (const char *host, + const char *port, + const char *family) +{ + DBusSocket fd = DBUS_SOCKET_INIT; + + if (host == NULL) + host = "localhost"; + + fd = _dbus_connect_simple_tcp_socket (host, port, family); + if (!_dbus_socket_is_valid (fd)) + { + return fd; + } + + _dbus_verbose ("Successfully connected to tcp socket %s:%s\n", + host, port); + + return fd; +} + +DBusSocket +_dbus_socket_open (DBusAddressEntry *entry) +{ + DBusSocket s = DBUS_SOCKET_INIT; + const char *host = dbus_address_entry_get_value (entry, "host"); + const char *port = dbus_address_entry_get_value (entry, "port"); + const char *family = dbus_address_entry_get_value (entry, "family"); + + if (port == NULL) + return s; + + return _dbus_socket_new_for_tcp (host, port, family); +} + +static DBusSocket +_dbus_simple_connection_open_internal (const char *address, + DBusError *error) +{ + DBusSocket s = DBUS_SOCKET_INIT; + DBusAddressEntry **entries; + int len, i; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + _dbus_verbose ("opening simple connection to: %s\n", address); + + if (!dbus_parse_address (address, &entries, &len, error)) + return s; + + for (i = 0; i < len; i++) + { + s = _dbus_socket_open (entries[i]); + + if (_dbus_socket_is_valid (s)) + break; + + } + + dbus_address_entries_free (entries); + return s; +} + +DBusSocket +test_connect_to_bus_simple (TestMainContext *ctx, + const gchar *address) +{ + DBusSocket sock; + DBusError error = DBUS_ERROR_INIT; + + sock = _dbus_simple_connection_open_internal (address, &error); + test_assert_no_error (&error); + g_assert (_dbus_socket_is_valid (sock)); + + return sock; +} diff --git a/test/test-utils-auth.h b/test/test-utils-auth.h new file mode 100755 index 0000000..31aeb24 --- /dev/null +++ b/test/test-utils-auth.h @@ -0,0 +1,13 @@ +#ifndef TEST_UTILS_AUTH_H +#define TEST_UTILS_AUTH_H + +#include +#include "test-utils.h" +#include +#include + +DBusSocket +test_connect_to_bus_simple (TestMainContext *ctx, + const gchar *address); + +#endif \ No newline at end of file -- 2.4.0