summaryrefslogtreecommitdiff
path: root/tests/ntlm-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ntlm-test.c')
-rw-r--r--tests/ntlm-test.c281
1 files changed, 218 insertions, 63 deletions
diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c
index 00222e87..24a0f2e4 100644
--- a/tests/ntlm-test.c
+++ b/tests/ntlm-test.c
@@ -11,6 +11,8 @@
#include "test-utils.h"
+static SoupURI *uri;
+
typedef enum {
NTLM_UNAUTHENTICATED,
NTLM_RECEIVED_REQUEST,
@@ -19,12 +21,16 @@ typedef enum {
NTLM_AUTHENTICATED_BOB
} NTLMServerState;
+static const char *state_name[] = {
+ "unauth", "recv", "sent", "alice", "bob"
+};
+
#define NTLM_REQUEST_START "TlRMTVNTUAABAAAA"
#define NTLM_RESPONSE_START "TlRMTVNTUAADAAAA"
#define NTLM_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="
-#define NTLM_RESPONSE_USER(response) ((response)[102] == 'E' ? NTLM_AUTHENTICATED_ALICE : NTLM_AUTHENTICATED_BOB)
+#define NTLM_RESPONSE_USER(response) ((response)[86] == 'E' ? NTLM_AUTHENTICATED_ALICE : ((response)[86] == 'I' ? NTLM_AUTHENTICATED_BOB : NTLM_UNAUTHENTICATED))
static void
clear_state (gpointer connections, GObject *ex_connection)
@@ -134,16 +140,22 @@ server_callback (SoupServer *server, SoupMessage *msg,
}
}
+ debug_printf (2, " (S:%s)", state_name[state]);
g_hash_table_insert (connections, socket, GINT_TO_POINTER (state));
g_object_weak_ref (G_OBJECT (socket), clear_state, connections);
}
+static gboolean authenticated_ntlm = FALSE;
+
static void
authenticate (SoupSession *session, SoupMessage *msg,
SoupAuth *auth, gboolean retrying, gpointer user)
{
- if (!retrying)
+ if (!retrying) {
soup_auth_authenticate (auth, user, "password");
+ if (g_str_equal (soup_auth_get_scheme_name (auth), "NTLM"))
+ authenticated_ntlm = TRUE;
+ }
}
typedef struct {
@@ -238,82 +250,65 @@ do_message (SoupSession *session, SoupURI *base_uri, const char *path,
if (state.got_ntlm_prompt) {
debug_printf (1, " NTLM_PROMPT");
- if (!get_ntlm_prompt) {
+ if (!get_ntlm_prompt)
debug_printf (1, "???");
- errors++;
- }
- } else if (get_ntlm_prompt) {
+ } else if (get_ntlm_prompt)
debug_printf (1, " no-ntlm-prompt???");
- errors++;
- }
if (state.got_basic_prompt) {
debug_printf (1, " BASIC_PROMPT");
- if (!get_basic_prompt) {
+ if (!get_basic_prompt)
debug_printf (1, "???");
- errors++;
- }
- } else if (get_basic_prompt) {
+ } else if (get_basic_prompt)
debug_printf (1, " no-basic-prompt???");
- errors++;
- }
if (state.sent_ntlm_request) {
debug_printf (1, " REQUEST");
- if (!do_ntlm) {
+ if (!do_ntlm)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_ntlm) {
+ } else if (do_ntlm)
debug_printf (1, " no-request???");
- errors++;
- }
if (state.got_ntlm_challenge) {
debug_printf (1, " CHALLENGE");
- if (!do_ntlm) {
+ if (!do_ntlm)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_ntlm) {
+ } else if (do_ntlm)
debug_printf (1, " no-challenge???");
- errors++;
- }
if (state.sent_ntlm_response) {
debug_printf (1, " NTLM_RESPONSE");
- if (!do_ntlm) {
+ if (!do_ntlm)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_ntlm) {
+ } else if (do_ntlm)
debug_printf (1, " no-ntlm-response???");
- errors++;
- }
if (state.sent_basic_response) {
debug_printf (1, " BASIC_RESPONSE");
- if (!do_basic) {
+ if (!do_basic)
debug_printf (1, "???");
- errors++;
- }
- } else if (do_basic) {
+ } else if (do_basic)
debug_printf (1, " no-basic-response???");
- errors++;
- }
debug_printf (1, " -> %s", msg->reason_phrase);
- if (msg->status_code != status_code) {
+ if (msg->status_code != status_code)
debug_printf (1, "???");
- errors++;
- }
debug_printf (1, "\n");
+ g_assert_true (state.got_ntlm_prompt == get_ntlm_prompt);
+ g_assert_true (state.got_basic_prompt == get_basic_prompt);
+ g_assert_true (state.sent_ntlm_request == do_ntlm);
+ g_assert_true (state.got_ntlm_challenge == do_ntlm);
+ g_assert_true (state.sent_ntlm_response == do_ntlm);
+ g_assert_true (state.sent_basic_response == do_basic);
+ soup_test_assert_message_status (msg, status_code);
+
g_object_unref (msg);
}
static void
-do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
+do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm,
+ const char *user, gboolean use_builtin_ntlm)
{
SoupSession *session;
gboolean alice = !g_strcmp0 (user, "alice");
@@ -322,13 +317,23 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
gboolean bob_via_ntlm = use_ntlm && bob;
gboolean alice_via_basic = !use_ntlm && alice;
- session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
- if (use_ntlm)
- soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+ session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
if (user) {
g_signal_connect (session, "authenticate",
G_CALLBACK (authenticate), (char *)user);
+ if (use_ntlm && !use_builtin_ntlm)
+ g_setenv ("NTLMUSER", user, TRUE);
+ }
+ if (use_ntlm) {
+ SoupAuthManager *auth_manager;
+ SoupAuth *ntlm;
+
+ soup_session_add_feature_by_type (session, SOUP_TYPE_AUTH_NTLM);
+ auth_manager = SOUP_AUTH_MANAGER (soup_session_get_feature (session, SOUP_TYPE_AUTH_MANAGER));
+ ntlm = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL);
+ soup_auth_manager_use_auth (auth_manager, base_uri, ntlm);
+ g_object_unref (ntlm);
}
/* 1. Server doesn't request auth, so both get_ntlm_prompt and
@@ -336,11 +341,17 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
* if we're using NTLM we'll try that even without the server
* asking.
*/
+ authenticated_ntlm = FALSE;
do_message (session, base_uri, "/noauth",
FALSE, use_ntlm,
FALSE, FALSE,
SOUP_STATUS_OK);
+ soup_test_assert (authenticated_ntlm == (use_ntlm && use_builtin_ntlm),
+ "%s built-in NTLM support, but authenticate signal %s emitted\n",
+ use_builtin_ntlm ? "Using" : "Not using",
+ authenticated_ntlm ? "was" : "wasn't");
+
/* 2. Server requires auth as Alice, so it will request that
* if we didn't already authenticate the connection to her in
* the previous step. If we authenticated as Bob in the
@@ -421,46 +432,190 @@ do_ntlm_round (SoupURI *base_uri, gboolean use_ntlm, const char *user)
soup_test_session_abort_unref (session);
}
+typedef enum {
+ BUILTIN,
+ WINBIND,
+ FALLBACK
+} NtlmType;
+
+typedef struct {
+ const char *name, *user;
+ gboolean conn_uses_ntlm;
+ NtlmType ntlm_type;
+} NtlmTest;
+
+static const NtlmTest ntlm_tests[] = {
+ { "/ntlm/builtin/none", NULL, FALSE, BUILTIN },
+ { "/ntlm/builtin/alice", "alice", TRUE, BUILTIN },
+ { "/ntlm/builtin/bob", "bob", TRUE, BUILTIN },
+ { "/ntlm/builtin/basic", "alice", FALSE, BUILTIN },
+
+ { "/ntlm/winbind/none", NULL, FALSE, WINBIND },
+ { "/ntlm/winbind/alice", "alice", TRUE, WINBIND },
+ { "/ntlm/winbind/bob", "bob", TRUE, WINBIND },
+ { "/ntlm/winbind/basic", "alice", FALSE, WINBIND },
+
+ { "/ntlm/fallback/none", NULL, FALSE, FALLBACK },
+ { "/ntlm/fallback/alice", "alice", TRUE, FALLBACK },
+ { "/ntlm/fallback/bob", "bob", TRUE, FALLBACK },
+ { "/ntlm/fallback/basic", "alice", FALSE, FALLBACK }
+};
+
static void
-do_ntlm_tests (SoupURI *base_uri)
+do_ntlm_test (gconstpointer data)
{
- debug_printf (1, "Round 1: Non-NTLM Connection, no auth\n");
- do_ntlm_round (base_uri, FALSE, NULL);
- debug_printf (1, "Round 2: NTLM Connection, user=alice\n");
- do_ntlm_round (base_uri, TRUE, "alice");
- debug_printf (1, "Round 3: NTLM Connection, user=bob\n");
- do_ntlm_round (base_uri, TRUE, "bob");
- debug_printf (1, "Round 4: Non-NTLM Connection, user=alice\n");
- do_ntlm_round (base_uri, FALSE, "alice");
+ const NtlmTest *test = data;
+ gboolean use_builtin_ntlm = TRUE;
+
+ switch (test->ntlm_type) {
+ case BUILTIN:
+ /* Built-in NTLM auth support. (We set SOUP_NTLM_AUTH_DEBUG to
+ * an empty string to ensure that the built-in support is
+ * being used, even if /usr/bin/ntlm_auth is available.)
+ */
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE);
+ break;
+
+ case WINBIND:
+#ifndef USE_NTLM_AUTH
+ g_test_skip ("/usr/bin/ntlm_auth is not available");
+ return;
+#endif
+
+ /* Samba winbind /usr/bin/ntlm_auth helper support (via a
+ * helper program that emulates its interface).
+ */
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG",
+ g_test_get_filename (G_TEST_BUILT, "ntlm-test-helper", NULL),
+ TRUE);
+ g_unsetenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS");
+ use_builtin_ntlm = FALSE;
+ break;
+
+ case FALLBACK:
+#ifndef USE_NTLM_AUTH
+ g_test_skip ("/usr/bin/ntlm_auth is not available");
+ return;
+#endif
+
+ /* Support for when ntlm_auth is installed, but the user has
+ * no cached credentials (and thus we have to fall back to
+ * libsoup's built-in NTLM support).
+ */
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG",
+ g_test_get_filename (G_TEST_BUILT, "ntlm-test-helper", NULL),
+ TRUE);
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG_NOCREDS", "1", TRUE);
+ break;
+ }
+
+ do_ntlm_round (uri, test->conn_uses_ntlm, test->user, use_builtin_ntlm);
+}
+
+static void
+retry_test_authenticate (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying,
+ gpointer user_data)
+{
+ gboolean *retried = user_data;
+
+ if (!retrying) {
+ /* server_callback doesn't actually verify the password,
+ * only the username. So we pass an incorrect username
+ * rather than an incorrect password.
+ */
+ soup_auth_authenticate (auth, "wrong", "password");
+ } else if (!*retried) {
+ soup_auth_authenticate (auth, "alice", "password");
+ *retried = TRUE;
+ }
+}
+
+static void
+do_retrying_test (gconstpointer data)
+{
+ SoupURI *base_uri = (SoupURI *)data;
+ SoupSession *session;
+ SoupMessage *msg;
+ SoupURI *uri;
+ gboolean retried = FALSE;
+
+ g_test_bug ("693222");
+
+ g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE);
+
+ debug_printf (1, " /alice\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM,
+ NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (retry_test_authenticate), &retried);
+
+ uri = soup_uri_new_with_base (base_uri, "/alice");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ soup_session_send_message (session, msg);
+
+ g_assert_true (retried);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
+
+ debug_printf (1, " /bob\n");
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION,
+ SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NTLM,
+ NULL);
+ g_signal_connect (session, "authenticate",
+ G_CALLBACK (retry_test_authenticate), &retried);
+ retried = FALSE;
+
+ uri = soup_uri_new_with_base (base_uri, "/bob");
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_uri_free (uri);
+
+ soup_session_send_message (session, msg);
+
+ g_assert_true (retried);
+ soup_test_assert_message_status (msg, SOUP_STATUS_UNAUTHORIZED);
+
+ g_object_unref (msg);
+
+ soup_test_session_abort_unref (session);
}
int
main (int argc, char **argv)
{
- GMainLoop *loop;
SoupServer *server;
GHashTable *connections;
- SoupURI *uri;
+ int i, ret;
test_init (argc, argv, NULL);
- server = soup_test_server_new (FALSE);
+ server = soup_test_server_new (TRUE);
connections = g_hash_table_new (NULL, NULL);
soup_server_add_handler (server, NULL,
server_callback, connections, NULL);
- loop = g_main_loop_new (NULL, TRUE);
-
uri = soup_uri_new ("http://127.0.0.1/");
soup_uri_set_port (uri, soup_server_get_port (server));
- do_ntlm_tests (uri);
- soup_uri_free (uri);
- g_main_loop_unref (loop);
+ for (i = 0; i < G_N_ELEMENTS (ntlm_tests); i++)
+ g_test_add_data_func (ntlm_tests[i].name, &ntlm_tests[i], do_ntlm_test);
+ g_test_add_data_func ("/ntlm/retry", uri, do_retrying_test);
+
+ ret = g_test_run ();
+
+ soup_uri_free (uri);
soup_test_server_quit_unref (server);
test_cleanup ();
g_hash_table_destroy (connections);
- return errors != 0;
+ return ret;
}