diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 032ba1f7..122d4b5f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -218,6 +218,13 @@ if(DBUS_WITH_GLIB) add_test_executable(test-userdb internals/userdb.c ${TEST_LIBRARIES}) add_helper_executable(manual-authz manual-authz.c ${TEST_LIBRARIES}) add_helper_executable(manual-test-thread-blocking thread-blocking.c ${TEST_LIBRARIES}) + if(WIN32) + set(auth_windows_sspi_corrupt_SOURCES + test-auth-windows-sspi-corrupt.c + test-utils-auth.c + ) + add_test_executable(test-auth-windows-sspi-corrupt "${auth_windows_sspi_corrupt_SOURCES}" ${TEST_LIBRARIES}) + endif() endif() ### keep these in creation order, i.e. uppermost dirs first diff --git a/test/data/valid-config-files/debug-auth-sspi.conf.in b/test/data/valid-config-files/debug-auth-sspi.conf.in new file mode 100644 index 00000000..98cba0b3 --- /dev/null +++ b/test/data/valid-config-files/debug-auth-sspi.conf.in @@ -0,0 +1,15 @@ + + + + + @TEST_LISTEN@ + @DBUS_TEST_DATA@/valid-service-files + DBUS_WINDOWS_SSPI_NTLM + + + + + + + diff --git a/test/meson.build b/test/meson.build index a60afac6..698800ac 100644 --- a/test/meson.build +++ b/test/meson.build @@ -440,7 +440,17 @@ if platform_windows 'srcs': [ 'manual-paths.c' ], 'link': [ libdbus_testutils, ], 'test': false, - } + }, + { + 'name': 'test-auth-windows-sspi-corrupt', + 'srcs': [ + 'test-auth-windows-sspi-corrupt.c', + 'test-utils-auth.c' + ], + 'link': [ libdbus_testutils, ], + 'deps': [ glib, gio, ], + 'test': true, + }, ] endif diff --git a/test/test-auth-windows-sspi-corrupt.c b/test/test-auth-windows-sspi-corrupt.c new file mode 100755 index 00000000..e150dc9f --- /dev/null +++ b/test/test-auth-windows-sspi-corrupt.c @@ -0,0 +1,703 @@ +#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; + + /* The moment when we start to screw with the data being sent. + * phase 0 - don't do anything, just a successful auth + * phase 1 - corrupt the initial client->server AUTH message + * phase 2 - corrupt the first server->client DATA message + * phase 3 - corrupt the first client->server DATA message + * There's no phase 4 (server responds with OK, but SSPI is not playing any part in that) + */ + int phase; + /* offset of the byte to corrupt. Increases by 1 with each iteration. + * Once it goes past the end of the data, advance to the next phase. + */ + unsigned int corrupt_offset; + + DBusString outgoing; + int free_outgoing; + DBusString incoming; + int free_incoming; + DBusString decoded; + int free_decoded; +} 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, NULL, + &f->daemon_pid); + + if (f->address == NULL) + { + f->skip = TRUE; + return; + } + + f->sock.sock = INVALID_SOCKET; + + f->phase = 0; + f->corrupt_offset = 0; + + f->free_context = 0; + f->free_outgoing = 0; + f->free_incoming = 0; + f->free_decoded = 0; +} + +/* Returns TRUE to mean "keep calling me, i still have other ways + * to corrupt the data". + * Returns FALSE to mean "i'm done, don't call me again". + */ +static dbus_bool_t +do_corrupt_sspi_auth (Fixture *f, + gconstpointer context) +{ + 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; + volatile int bytes_written; + volatile 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; +#define PHASES 4 + char expect_error_in_phase[PHASES]; + unsigned long bytes_available; + char zero; + int old_offset, old_phase; + + old_offset = f->corrupt_offset; + old_phase = f->phase; + + if (_dbus_socket_is_valid (f->sock)) + { + /* Ensure that there's no leftover incoming data in the socket */ + do + { +#define HUNDRED 100 + char buf[HUNDRED]; + bytes_available = 0; + ioctlsocket (f->sock.sock, FIONREAD, &bytes_available); + bytes_read = 0; + if (bytes_available > 0) + bytes_read = recv (f->sock.sock, buf, bytes_available > HUNDRED ? HUNDRED : bytes_available, 0); +#undef HUNDRED + } while (bytes_read > 0); + } + + if (!_dbus_socket_is_valid (f->sock)) + { + f->sock = test_connect_to_bus_simple (f->ctx, f->address); + g_assert (_dbus_socket_is_valid (f->sock)); + zero = 0; + g_assert (send (f->sock.sock, &zero, 1, 0) == 1); + } + +#define HANDLE_BAD_RECV \ + if (bytes_read < 0) \ + { \ + g_print ("disconnected unexpectedly\n"); \ + f->corrupt_offset = old_offset; \ + f->phase = old_phase; \ + _dbus_close_socket (&f->sock, NULL); \ + _dbus_socket_invalidate (&f->sock); \ + \ + return TRUE; \ + } + +#define HANDLE_BAD_SEND \ + if (bytes_written < 0) \ + { \ + g_print ("disconnected unexpectedly\n"); \ + f->corrupt_offset = old_offset; \ + f->phase = old_phase; \ + _dbus_close_socket (&f->sock, NULL); \ + _dbus_socket_invalidate (&f->sock); \ + \ + return TRUE; \ + } + + g_assert (_dbus_string_init (&f->outgoing)); + f->free_outgoing = 1; + g_assert (_dbus_string_append (&f->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); + + memset (expect_error_in_phase, 0, PHASES); + + g_print ("phase 1\n"); + if (f->phase == 1) + { + if (f->corrupt_offset < output_sec_buffer.cbBuffer && f->corrupt_offset < 32) + { + BYTE x = ((BYTE *) output_sec_buffer.pvBuffer)[f->corrupt_offset]; + x = (x == 255 ? 0 : x + 1); + ((BYTE *) output_sec_buffer.pvBuffer)[f->corrupt_offset] = x; + + switch (f->corrupt_offset) + { + /* 4 bytes (12, 13, 14, 15) are flags, changing some of them makes no difference */ + case 13: + case 14: + case 15: + /* 2 bytes (16, 17) is domain SB lengh */ + /* 2 bytes (18, 19) is domain SB allocated space, changing it makes no difference */ + case 18: + case 19: + g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase); + break; + /* 2 bytes (24, 25) is workstation SB length, changing it causes an error later on */ + case 24: + g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase); + expect_error_in_phase[3] = TRUE; + break; + /* 2 bytes (26, 27) is workstation SB allocated space, changing it makes no difference */ + case 26: + case 27: + g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase); + break; + /* 4 bytes (28, 29, 30, 31) is workstation SB offset, changing it causes an error later on */ + case 28: + g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase); + expect_error_in_phase[3] = TRUE; + break; + /* 1 byte (32) is OS major version, changing it makes no difference */ + case 32: + /* 1 byte (33) is OS minor version, changing it makes no difference */ + case 33: + /* 2 byte (34, 35) is OS build number, changing it makes no difference */ + case 34: + case 35: + /* 4 bytes (36, 37, 38, 39) are unknown/reserved, changing them makes no difference */ + case 36: + case 37: + case 38: + case 39: + g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase); + break; + default: + if (f->corrupt_offset >= 40) + { + /* Data section starts at byte 40, its length depends on the argument we used, and changing it causes an error later on */ + g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase); + expect_error_in_phase[3] = TRUE; + } + else + { + g_print ("corrupted byte %d in phase %d, auth will fail in phase 1\n", f->corrupt_offset, f->phase); + expect_error_in_phase[1] = TRUE; + } + } + + f->corrupt_offset += 1; + } + else + { + f->phase += 1; + f->corrupt_offset = 0; + g_print ("will begin corrupting phase %d\n", f->phase); + } + } + + g_assert (_dbus_string_hex_encode (&f->plaintext, 0, &f->outgoing, _dbus_string_get_length (&f->outgoing))); + g_assert (_dbus_string_append (&f->outgoing, "\r\n")); + + _dbus_verbose ("sending `%.*s`\n", _dbus_string_get_length (&f->outgoing), _dbus_string_get_data (&f->outgoing)); + bytes_written = send (f->sock.sock, + _dbus_string_get_data (&f->outgoing), _dbus_string_get_length (&f->outgoing), + 0); + HANDLE_BAD_SEND + g_assert (bytes_written == _dbus_string_get_length (&f->outgoing)); + +#define DATASTR "DATA " +#define DATASTRLEN strlen (DATASTR) +#define REJSTR "REJECTED" +#define REJSTRLEN strlen (REJSTR) + g_assert (_dbus_string_init (&f->incoming)); + f->free_incoming = 1; + + g_print ("receiving in phase 1\n"); + g_assert (_dbus_string_set_length (&f->incoming, 100)); + buffer = _dbus_string_get_data (&f->incoming); + bytes_read = recv (f->sock.sock, buffer, 100, MSG_PEEK); + HANDLE_BAD_RECV + + g_print ("received %.*s\n", bytes_read, buffer); + + if (f->phase <= 1 && (expect_error_in_phase[1] != FALSE)) + { + g_assert (bytes_read >= (int) REJSTRLEN); + g_assert (strncmp (buffer, REJSTR, REJSTRLEN) == 0); + + g_print ("auth failed in phase 1\n"); + return TRUE; + } + + g_assert (bytes_read >= (int) DATASTRLEN); + g_assert (strncmp (buffer, DATASTR, DATASTRLEN) == 0); + + _dbus_string_free (&f->outgoing); + f->free_outgoing = 0; + + to_read = 0; + total_read = 0; + do + { + int i, should_break; + for (i = 0, should_break = FALSE; i < total_read - 1; i++) + { + if (buffer[i] == '\r' && buffer[i + 1] == '\n') + { + should_break = TRUE; + total_read = i + 2; + } + } + if (should_break) + break; + to_read += 100; + g_assert (_dbus_string_set_length (&f->incoming, to_read)); + buffer = _dbus_string_get_data (&f->incoming); + bytes_read = recv (f->sock.sock, &buffer[total_read], 100, 0); + HANDLE_BAD_RECV + g_assert (bytes_read > 0); + total_read += bytes_read; + } while (TRUE); + g_assert (buffer[total_read - 2] == '\r' && + buffer[total_read - 1] == '\n'); + buffer[total_read - 1] = buffer[total_read - 2] = '\0'; + + g_assert (_dbus_string_init (&f->decoded)); + f->free_decoded = 1; + g_assert (_dbus_string_hex_decode (&f->incoming, DATASTRLEN, &end, &f->decoded, 0)); + g_assert (end == total_read - 2); + _dbus_string_free (&f->incoming); + f->free_incoming = 0; + + input_sec_buffer.cbBuffer = _dbus_string_get_length (&f->decoded); + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_data (&f->decoded); + + g_assert (input_sec_buffer.cbBuffer > 0); + + g_print ("phase 2\n"); + if (f->phase == 2) + { + /* 2 bytes (40, 41) is target info SB length, changing it makes no difference */ + /* 2 bytes (42, 43) is target info SB allocated space, changing it makes no difference */ + /* 4 bytes (44, 45, 46, 47) is target info SB offset, changing it makes no difference */ + /* Data section starts at byte 48, its length depends on the argument we used, and changing it makes no difference + * because of local authentication. + */ + if (f->corrupt_offset < input_sec_buffer.cbBuffer && f->corrupt_offset < 40) + { + BYTE x = ((BYTE *) input_sec_buffer.pvBuffer)[f->corrupt_offset]; + x = (x == 255 ? 0 : x + 1); + ((BYTE *) input_sec_buffer.pvBuffer)[f->corrupt_offset] = x; + + switch (f->corrupt_offset) + { + /* 8 bytes (32, 33, 34, 35, 36, 37, 38, 39) is the context, changing it causes an error later on */ + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase); + expect_error_in_phase[3] = TRUE; + break; + default: + if (f->corrupt_offset >= 12 && f->corrupt_offset < 32) + { + /* 2 bytes (12, 13) is target name SB length, changing it makes no difference */ + /* 2 bytes (14, 15) is target name SB allocated space, changing it makes no difference */ + /* 4 bytes (16, 17, 18, 19) is target name SB offset, changing it makes no difference */ + /* 4 bytes (20, 21, 22, 23) are flags, changing some of them makes no difference */ + /* 8 bytes (24, 25, 26, 27, 28, 29, 30, 31) is the challenge, changing it makes no difference (yes! Because of local authentication) */ + g_print ("corrupted byte %d in phase %d, auth will succeed anyway. Skipping to offset 31\n", f->corrupt_offset, f->phase); + f->corrupt_offset = 31; + } + else + { + g_print ("corrupted byte %d in phase %d, auth will fail in phase 2\n", f->corrupt_offset, f->phase); + expect_error_in_phase[2] = TRUE; + } + } + + f->corrupt_offset += 1; + } + else + { + f->phase += 1; + f->corrupt_offset = 0; + g_print ("win begin corrupting phase %d", f->phase); + } + } + + g_assert (_dbus_string_init (&f->outgoing)); + f->free_outgoing = 1; + g_assert (_dbus_string_append (&f->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_print ("getting ISC result\n"); + if (f->phase <= 2 && expect_error_in_phase[2] != FALSE) + { + g_assert (ss != SEC_E_OK && + ss != SEC_I_COMPLETE_AND_CONTINUE && + ss != SEC_I_COMPLETE_NEEDED && + ss != SEC_I_CONTINUE_NEEDED); + + g_print ("ISC failed, aborting auth\n"); + /* Server still thinks we're doing AUTH, make sure it fails */ +#define FAKEAUTHDATA "DATA baadf00d\r\n" +#define FAKEAUTHDATALEN strlen (FAKEAUTHDATA) + bytes_written = send (f->sock.sock, FAKEAUTHDATA, FAKEAUTHDATALEN, 0); + HANDLE_BAD_SEND + g_assert (bytes_written == FAKEAUTHDATALEN); + + g_assert (_dbus_string_init (&f->incoming)); + f->free_incoming = 1; + g_assert (_dbus_string_set_length (&f->incoming, 100)); + buffer = _dbus_string_get_data (&f->incoming); + bytes_read = recv (f->sock.sock, buffer, 100, MSG_PEEK); + HANDLE_BAD_RECV + g_print ("received %.*s\n", bytes_read, buffer); + g_assert (bytes_read >= (int) REJSTRLEN); + g_assert (strncmp (buffer, REJSTR, REJSTRLEN) == 0); + + g_print ("auth failed in phase 2\n"); + return TRUE; + } + + 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_print ("phase 3\n"); + if (f->phase == 3) + { + /* Session key starts at byte 52, everything from that point is irrelevant */ + if (f->corrupt_offset < output_sec_buffer.cbBuffer && f->corrupt_offset < 52) + { + BYTE x = ((BYTE *)output_sec_buffer.pvBuffer)[f->corrupt_offset]; + x = (x == 255 ? 0 : x + 1); + ((BYTE *)output_sec_buffer.pvBuffer)[f->corrupt_offset] = x; + + switch (f->corrupt_offset) + { + /* 2 bytes (12, 13) is LM Response SB length, changing it causes an error */ + case 12: + case 13: + /* 2 bytes (20, 21) is NTLM Response SB length, changing it causes an error */ + case 20: + case 21: + /* 2 bytes (28, 29) is Target Name SB length, changing it causes an error */ + case 28: + case 29: + /* 2 bytes (36, 37) is User Name SB length, changing it causes an error */ + case 36: + case 37: + /* 2 bytes (44, 45) is Workstation Name SB length, changing it causes an error */ + case 44: + case 45: + g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase); + expect_error_in_phase[3] = TRUE; + break; + default: + if ((f->corrupt_offset > 13 && f->corrupt_offset < 20) || + (f->corrupt_offset > 21 && f->corrupt_offset < 28) || + (f->corrupt_offset > 29 && f->corrupt_offset < 36) || + (f->corrupt_offset > 37 && f->corrupt_offset < 44) || + (f->corrupt_offset > 45 && f->corrupt_offset < 52)) + { + /* 2 bytes (14, 15) is LM Response SB allocated space, changing it makes no difference */ + /* 4 bytes (16, 17, 18, 19) is LM Response SB offset, changing it makes no difference */ + /* 2 bytes (22, 23) is NTLM Response SB allocated space, changing it makes no difference */ + /* 4 bytes (24, 25, 26, 27) is NTLM Response SB offset, changing it makes no difference */ + /* and so on for Target Name, User name and Workstation Name */ + g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase); + } + else + { + g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase); + expect_error_in_phase[3] = TRUE; + } + } + + f->corrupt_offset += 1; + } + else + { + f->phase += 1; + f->corrupt_offset = 0; + g_print ("stopping corruption\n"); + } + } + + g_assert (_dbus_string_hex_encode (&f->plaintext, 0, &f->outgoing, + _dbus_string_get_length (&f->outgoing))); + + g_assert (_dbus_string_append (&f->outgoing, "\r\n")); + + bytes_written = send (f->sock.sock, + _dbus_string_get_data (&f->outgoing), _dbus_string_get_length (&f->outgoing), + 0); + HANDLE_BAD_SEND + g_assert (bytes_written == _dbus_string_get_length (&f->outgoing)); + +#undef DATASTR +#undef DATASTRLEN + g_print ("receiving in phase 3\n"); + + g_assert (_dbus_string_init (&f->incoming)); + f->free_incoming = 1; + g_assert (_dbus_string_set_length (&f->incoming, 100)); + buffer = _dbus_string_get_data (&f->incoming); + bytes_read = recv (f->sock.sock, buffer, 100, MSG_PEEK); + HANDLE_BAD_RECV + g_print ("received %.*s\n", bytes_read, buffer); + + if (f->phase <= 3 && expect_error_in_phase[3] != FALSE) + { + g_assert (bytes_read >= (int) REJSTRLEN); + g_assert (strncmp (buffer, REJSTR, REJSTRLEN) == 0); + + g_print ("auth failed in phase 3\n"); + + return TRUE; + } + +#undef REJSTR +#undef REJSTRLEN + +#define OKSTR "OK " +#define OKSTRLEN strlen (OKSTR) + + g_assert (bytes_read >= (int) OKSTRLEN); + g_assert (strncmp (buffer, OKSTR, OKSTRLEN) == 0); + + g_print ("auth succeeded\n"); + +#undef OKSTR +#undef OKSTRLEN + + /* Can't re-attempt AUTH after OK, must tear the connection down */ + _dbus_close_socket (&f->sock, NULL); + _dbus_socket_invalidate (&f->sock); + + _dbus_string_free (&f->outgoing); + f->free_outgoing = 0; + + _dbus_string_free (&f->incoming); + f->free_incoming = 0; + + DeleteSecurityContext (&f->context); + f->free_context = 0; + + if (f->phase == 0) + { + f->phase += 1; + g_print ("will begin corrupting phase %d\n", f->phase); + } + else if (f->phase >= 4) + return FALSE; + + return TRUE; +} + +static void +test_sspi_corrupt (Fixture *f, + gconstpointer context) +{ + while (TRUE) + { + /* clean up after previous iteration */ + if (f->free_context) + { + DeleteSecurityContext (&f->context); + f->free_context = 0; + } + + if (f->free_outgoing) + { + f->free_outgoing = 0; + _dbus_string_free (&f->outgoing); + } + if (f->free_incoming) + { + f->free_incoming = 0; + _dbus_string_free (&f->incoming); + } + if (f->free_decoded) + { + f->free_decoded = 0; + _dbus_string_free (&f->decoded); + } + + if (!do_corrupt_sspi_auth (f, context)) + break; + } +} + +static void +teardown (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + SECURITY_STATUS ss; + + if (_dbus_socket_is_valid (f->sock)) + { + _dbus_close_socket (&f->sock, NULL); + _dbus_socket_invalidate (&f->sock); + } + + ss = FreeCredentialsHandle (&f->credentials); + g_assert (ss == SEC_E_OK); + _dbus_string_free (&f->plaintext); + + dbus_error_free (&f->e); + + 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); +} + +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 00000000..6af7c3c1 --- /dev/null +++ b/test/test-utils-auth.c @@ -0,0 +1,159 @@ +#include +#include "test-utils-auth.h" +#include "test-utils-glib.h" + +static dbus_bool_t +_dbus_win_simple_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; +} + +static 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_simple_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; +} + +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++) + { + const char *host = dbus_address_entry_get_value (entries[i], "host"); + const char *port = dbus_address_entry_get_value (entries[i], "port"); + const char *family = dbus_address_entry_get_value (entries[i], "family"); + + if (port == NULL) + continue; + + if (host == NULL) + host = "localhost"; + + s = _dbus_connect_simple_tcp_socket (host, port, family); + if (!_dbus_socket_is_valid (s)) + continue; + + _dbus_verbose ("Successfully connected to tcp socket %s:%s\n", + host, port); + + 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 00000000..31aeb249 --- /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