Index: libsoup2.4-2.46.0/libsoup/soup-auth-ntlm.c
===================================================================
--- libsoup2.4-2.46.0.orig/libsoup/soup-auth-ntlm.c
+++ libsoup2.4-2.46.0/libsoup/soup-auth-ntlm.c
@@ -11,6 +11,8 @@
 
 #include <errno.h>
 #include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
 #include <string.h>
 #include <glib.h>
 
@@ -25,13 +27,15 @@ static void        soup_ntlm_nt_hash
 static char       *soup_ntlm_request           (void);
 static gboolean    soup_ntlm_parse_challenge   (const char  *challenge,
 						char       **nonce,
-						char       **default_domain);
+						char       **default_domain,
+						gboolean    *ntlmv2_session);
 static char       *soup_ntlm_response          (const char  *nonce, 
 						const char  *user,
 						guchar       nt_hash[21],
 						guchar       lm_hash[21],
 						const char  *host, 
-						const char  *domain);
+						const char  *domain,
+						gboolean     ntlmv2_session);
 
 typedef enum {
 	SOUP_NTLM_NEW,
@@ -46,6 +50,7 @@ typedef struct {
 	SoupNTLMState state;
 	char *nonce;
 	char *response_header;
+	gboolean ntlmv2_session;
 } SoupNTLMConnectionState;
 
 typedef enum {
@@ -327,7 +332,8 @@ soup_auth_ntlm_update_connection (SoupCo
 		return TRUE;
 
 	if (!soup_ntlm_parse_challenge (auth_header + 5, &conn->nonce,
-					priv->domain ? NULL : &priv->domain)) {
+					priv->domain ? NULL : &priv->domain,
+					&conn->ntlmv2_session)) {
 		conn->state = SOUP_NTLM_FAILED;
 		return FALSE;
 	}
@@ -502,7 +508,8 @@ soup_auth_ntlm_get_connection_authorizat
 						     priv->nt_hash,
 						     priv->lm_hash,
 						     NULL,
-						     priv->domain);
+						     priv->domain,
+						     conn->ntlmv2_session);
 		}
 		g_clear_pointer (&conn->nonce, g_free);
 		conn->state = SOUP_NTLM_SENT_RESPONSE;
@@ -631,8 +638,12 @@ typedef struct {
 #define NTLM_CHALLENGE_NONCE_LENGTH          8
 #define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
 
+#define NTLM_CHALLENGE_FLAGS_OFFSET         20
+#define NTLM_FLAGS_NEGOTIATE_NTLMV2 0x00080000
+
 #define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
 #define NTLM_RESPONSE_FLAGS 0x8201
+#define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 0x00080000
 
 typedef struct {
         guchar     header[12];
@@ -658,17 +669,19 @@ ntlm_set_string (NTLMString *string, int
 static char *
 soup_ntlm_request (void)
 {
-	return g_strdup ("NTLM TlRMTVNTUAABAAAABYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
+	return g_strdup ("NTLM TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAwAAAA");
 }
 
 static gboolean
 soup_ntlm_parse_challenge (const char *challenge,
 			   char      **nonce,
-			   char      **default_domain)
+			   char      **default_domain,
+			   gboolean   *ntlmv2_session)
 {
 	gsize clen;
 	NTLMString domain;
 	guchar *chall;
+	guint32 flags;
 
 	chall = g_base64_decode (challenge, &clen);
 	if (clen < NTLM_CHALLENGE_DOMAIN_STRING_OFFSET ||
@@ -677,6 +690,12 @@ soup_ntlm_parse_challenge (const char *c
 		return FALSE;
 	}
 
+	memcpy(&flags, chall + NTLM_CHALLENGE_FLAGS_OFFSET, sizeof(flags));
+	
+	flags = GUINT_FROM_LE(flags);
+
+	*ntlmv2_session = (flags & NTLM_FLAGS_NEGOTIATE_NTLMV2) ? TRUE : FALSE;
+
 	if (default_domain) {
 		memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
 		domain.length = GUINT16_FROM_LE (domain.length);
@@ -701,13 +720,66 @@ soup_ntlm_parse_challenge (const char *c
 	return TRUE;
 }
 
+static void
+calc_ntlm2_session_response (const char *nonce,
+			     guchar      nt_hash[21],
+			     guchar      lm_hash[21],
+			     guchar     *lm_resp,
+			     gsize       lm_resp_sz,
+			     guchar     *nt_resp)
+{
+	guint32 client_nonce[2];
+	guchar ntlmv2_hash[16];
+	GChecksum *ntlmv2_cksum;
+	gsize ntlmv2_hash_sz = sizeof (ntlmv2_hash);
+
+	/*
+	 * FIXME: if GLib ever gets a more secure
+	 * random number generator, use it here
+	 */
+#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
+	int fd = open("/dev/urandom", O_RDONLY);
+
+	if (sizeof(client_nonce) != read (fd, client_nonce, sizeof(client_nonce))) {
+		g_warning ("Failed to read %" G_GSIZE_FORMAT " random bytes on fd %d for NTLMSSP nonce",
+			   ntlmv2_hash_sz, fd);
+		client_nonce[0] = g_random_int ();
+		client_nonce[1] = g_random_int ();
+	}
+	if (fd != -1 && close (fd)) {
+		g_warning ("Failed to close fd %d after reading random nonce data", fd);
+	}
+#else
+	ntlmv2_hash_sz = sizeof(ntlmv2_hash);
+
+	client_nonce[0] = g_random_int();
+	client_nonce[1] = g_random_int();
+#endif
+
+	ntlmv2_cksum = g_checksum_new(G_CHECKSUM_MD5);
+	g_checksum_update(ntlmv2_cksum, (const guchar*)nonce, 8);
+	g_checksum_update(ntlmv2_cksum, (const guchar*)client_nonce,
+			  sizeof(client_nonce));
+	g_checksum_get_digest(ntlmv2_cksum, ntlmv2_hash, &ntlmv2_hash_sz);
+	g_checksum_free(ntlmv2_cksum);
+	
+	// Send the padded client nonce as a fake lm_resp
+	memset(lm_resp, 0, lm_resp_sz);
+	memcpy(lm_resp, client_nonce, sizeof(client_nonce));
+
+	// Compute nt_hash as usual but with a new nonce
+	calc_response (nt_hash, ntlmv2_hash, nt_resp);
+}
+
+
 static char *
 soup_ntlm_response (const char *nonce, 
 		    const char *user,
 		    guchar      nt_hash[21],
 		    guchar      lm_hash[21],
 		    const char *host, 
-		    const char *domain)
+		    const char *domain,
+		    gboolean    ntlmv2_session)
 {
 	int offset;
 	gsize hlen, dlen, ulen;
@@ -717,12 +789,20 @@ soup_ntlm_response (const char *nonce,
 	char *out, *p;
 	int state, save;
 
-	calc_response (nt_hash, (guchar *)nonce, nt_resp);
-	calc_response (lm_hash, (guchar *)nonce, lm_resp);
+	if (ntlmv2_session) {
+		calc_ntlm2_session_response(nonce, nt_hash, lm_hash,
+					    lm_resp, sizeof(lm_resp), nt_resp);
+	} else {
+		// Compute a regular response
+		calc_response (nt_hash, (guchar *)nonce, nt_resp);
+		calc_response (lm_hash, (guchar *)nonce, lm_resp);
+	}
 
 	memset (&resp, 0, sizeof (resp));
 	memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header));
 	resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
+	if (ntlmv2_session)
+		resp.flags |= GUINT32_TO_LE (NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);
 
 	offset = sizeof (resp);
 
Index: libsoup2.4-2.46.0/tests/ntlm-test.c
===================================================================
--- libsoup2.4-2.46.0.orig/tests/ntlm-test.c
+++ libsoup2.4-2.46.0/tests/ntlm-test.c
@@ -29,6 +29,7 @@ static const char *state_name[] = {
 #define NTLM_RESPONSE_START "TlRMTVNTUAADAAAA"
 
 #define NTLM_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="
+#define NTLMSSP_CHALLENGE "TlRMTVNTUAACAAAADAAMADAAAAABAokAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA="
 
 #define NTLM_RESPONSE_USER(response) ((response)[86] == 'E' ? NTLM_AUTHENTICATED_ALICE : ((response)[86] == 'I' ? NTLM_AUTHENTICATED_BOB : NTLM_UNAUTHENTICATED))
 
@@ -41,7 +42,8 @@ clear_state (gpointer connections, GObje
 static void
 server_callback (SoupServer *server, SoupMessage *msg,
 		 const char *path, GHashTable *query,
-		 SoupClientContext *client, gpointer data)
+		 SoupClientContext *client, gpointer data,
+		 gboolean ntlmssp)
 {
 	GHashTable *connections = data;
 	SoupSocket *socket;
@@ -121,7 +123,7 @@ server_callback (SoupServer *server, Sou
 		if (ntlm_allowed && state == NTLM_RECEIVED_REQUEST) {
 			soup_message_headers_append (msg->response_headers,
 						     "WWW-Authenticate",
-						     "NTLM " NTLM_CHALLENGE);
+						     (ntlmssp == TRUE) ? ("NTLM " NTLMSSP_CHALLENGE) : ("NTLM " NTLM_CHALLENGE));
 			state = NTLM_SENT_CHALLENGE;
 		} else if (ntlm_allowed) {
 			soup_message_headers_append (msg->response_headers,
@@ -148,6 +150,22 @@ server_callback (SoupServer *server, Sou
 static gboolean authenticated_ntlm = FALSE;
 
 static void
+server_callback_ntlm (SoupServer *server, SoupMessage *msg,
+		      const char *path, GHashTable *query,
+		      SoupClientContext *client, gpointer data)
+{
+	server_callback(server, msg, path, query, client, data, FALSE);
+}
+
+static void
+server_callback_ntlmssp (SoupServer *server, SoupMessage *msg,
+			 const char *path, GHashTable *query,
+			 SoupClientContext *client, gpointer data)
+{
+	server_callback(server, msg, path, query, client, data, TRUE);
+}
+
+static void
 authenticate (SoupSession *session, SoupMessage *msg,
 	      SoupAuth *auth, gboolean retrying, gpointer user)
 {
@@ -178,8 +196,10 @@ prompt_check (SoupMessage *msg, gpointer
 	if (header && strstr (header, "Basic "))
 		state->got_basic_prompt = TRUE;
 	if (header && strstr (header, "NTLM") &&
-	    !strstr (header, NTLM_CHALLENGE))
+	    (!strstr (header, NTLM_CHALLENGE) &&
+	     !strstr (header, NTLMSSP_CHALLENGE))) {
 		state->got_ntlm_prompt = TRUE;
+	}
 }
 
 static void
@@ -435,7 +455,8 @@ do_ntlm_round (SoupURI *base_uri, gboole
 typedef enum {
 	BUILTIN,
 	WINBIND,
-	FALLBACK
+	FALLBACK,
+	SSP
 } NtlmType;
 
 typedef struct {
@@ -458,7 +479,12 @@ static const NtlmTest ntlm_tests[] = {
 	{ "/ntlm/fallback/none",  NULL,    FALSE, FALLBACK },
 	{ "/ntlm/fallback/alice", "alice", TRUE,  FALLBACK },
 	{ "/ntlm/fallback/bob",   "bob",   TRUE,  FALLBACK },
-	{ "/ntlm/fallback/basic", "alice", FALSE, FALLBACK }
+	{ "/ntlm/fallback/basic", "alice", FALSE, FALLBACK },
+
+	{ "/ntlm/ssp/none",  NULL,    FALSE, SSP },
+	{ "/ntlm/ssp/alice", "alice", TRUE,  SSP },
+	{ "/ntlm/ssp/bob",   "bob",   TRUE,  SSP },
+	{ "/ntlm/ssp/basic", "alice", FALSE, SSP }
 };
 
 static void
@@ -467,19 +493,33 @@ do_ntlm_test (gconstpointer data)
 	const NtlmTest *test = data;
 	gboolean use_builtin_ntlm = TRUE;
 
+	SoupServer *server;
+	GHashTable *connections;
+
+	server = soup_test_server_new (TRUE);
+	connections = g_hash_table_new (NULL, NULL);
+	soup_server_add_handler (server, NULL,
+				 (test->ntlm_type == SSP ? server_callback_ntlmssp
+				                         : server_callback_ntlm),
+				 connections, NULL);
+
+	uri = soup_uri_new ("http://127.0.0.1/");
+	soup_uri_set_port (uri, soup_server_get_port (server));
+
+
 	switch (test->ntlm_type) {
-	case BUILTIN:
+	case BUILTIN: case SSP:
 		/* 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.)
-		 */
+	 	 * 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;
+		goto done;
 #endif
 
 		/* Samba winbind /usr/bin/ntlm_auth helper support (via a
@@ -495,7 +535,7 @@ do_ntlm_test (gconstpointer data)
 	case FALLBACK:
 #ifndef USE_NTLM_AUTH
 		g_test_skip ("/usr/bin/ntlm_auth is not available");
-		return;
+		goto done;
 #endif
 
 		/* Support for when ntlm_auth is installed, but the user has
@@ -510,6 +550,11 @@ do_ntlm_test (gconstpointer data)
 	}
 
 	do_ntlm_round (uri, test->conn_uses_ntlm, test->user, use_builtin_ntlm);
+
+done:
+	soup_uri_free (uri);
+	soup_test_server_quit_unref (server);
+	g_hash_table_destroy (connections);
 }
 
 static void
@@ -534,14 +579,26 @@ retry_test_authenticate (SoupSession *se
 static void
 do_retrying_test (gconstpointer data)
 {
-	SoupURI *base_uri = (SoupURI *)data;
+	SoupURI *base_uri;
 	SoupSession *session;
 	SoupMessage *msg;
 	SoupURI *uri;
 	gboolean retried = FALSE;
 
+	SoupServer *server;
+	GHashTable *connections;
+
 	g_test_bug ("693222");
 
+	server = soup_test_server_new (TRUE);
+	connections = g_hash_table_new (NULL, NULL);
+	soup_server_add_handler (server, NULL,
+				 server_callback_ntlm,
+				 connections, NULL);
+
+	base_uri = soup_uri_new ("http://127.0.0.1/");
+	soup_uri_set_port (base_uri, soup_server_get_port (server));
+
 	g_setenv ("SOUP_NTLM_AUTH_DEBUG", "", TRUE);
 
 	debug_printf (1, "  /alice\n");
@@ -586,36 +643,29 @@ do_retrying_test (gconstpointer data)
 	g_object_unref (msg);
 
 	soup_test_session_abort_unref (session);
+
+	soup_uri_free (base_uri);
+	soup_test_server_quit_unref (server);
+	g_hash_table_destroy (connections);
 }
 
 int
 main (int argc, char **argv)
 {
-	SoupServer *server;
-	GHashTable *connections;
 	int i, ret;
 
 	test_init (argc, argv, NULL);
 
-	server = soup_test_server_new (TRUE);
-	connections = g_hash_table_new (NULL, NULL);
-	soup_server_add_handler (server, NULL,
-				 server_callback, connections, NULL);
-
-	uri = soup_uri_new ("http://127.0.0.1/");
-	soup_uri_set_port (uri, soup_server_get_port (server));
-
 	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);
+		g_test_add_data_func (ntlm_tests[i].name,
+				      &ntlm_tests[i],
+				      do_ntlm_test);
 
-	ret = g_test_run ();
+	g_test_add_data_func ("/ntlm/retry", NULL, do_retrying_test);
 
-	soup_uri_free (uri);
+	ret = g_test_run ();
 
-	soup_test_server_quit_unref (server);
 	test_cleanup ();
-	g_hash_table_destroy (connections);
 
 	return ret;
 }
