summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorРуслан Ижбулатов <lrn1986@gmail.com>2016-06-18 11:50:00 +0000
committerRalf Habacker <ralf.habacker@freenet.de>2016-08-17 08:25:44 +0200
commit67b06cce20b432798252bcd17cc73de5aa36abfc (patch)
tree081d1974ffd970efa64530f496175c9c243b3af9
parentae6f83033500ab039609561273ba029ffc2044e4 (diff)
Support SSPI NTLM authentication mechanismsspi
https://bugs.freedesktop.org/show_bug.cgi?id=96577
-rw-r--r--configure.ac2
-rw-r--r--dbus/dbus-auth.c165
-rw-r--r--dbus/dbus-sysdeps-win.c635
-rw-r--r--dbus/dbus-sysdeps-win.h63
4 files changed, 838 insertions, 27 deletions
diff --git a/configure.ac b/configure.ac
index 460b32b30..8968546dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1239,7 +1239,7 @@ if test x$dbus_win = xyes ; then
if test x$dbus_wince = xyes ; then
NETWORK_libs="-lws2"
else
- NETWORK_libs="-lws2_32 -liphlpapi -ldbghelp"
+ NETWORK_libs="-lws2_32 -liphlpapi -ldbghelp -lsecur32"
fi
fi
diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c
index ea43ce72a..18c12b4b3 100644
--- a/dbus/dbus-auth.c
+++ b/dbus/dbus-auth.c
@@ -177,6 +177,11 @@ struct DBusAuth
DBusKeyring *keyring; /**< Keyring for cookie mechanism. */
int cookie_id; /**< ID of cookie to use */
DBusString challenge; /**< Challenge sent to client */
+ DBusString response; /**< Response sent to server */
+
+#if defined (DBUS_WIN) && !defined (DBUS_WINCE)
+ DBusSSPINTLMInfo sspi_ntlm; /**< defined in sysdeps-win. */
+#endif
char **allowed_mechs; /**< Mechanisms we're allowed to use,
* or #NULL if we can use any
@@ -389,9 +394,14 @@ _dbus_auth_new (int size)
auth->desired_identity = _dbus_credentials_new ();
if (auth->desired_identity == NULL)
goto enomem_8;
+
+ if (!_dbus_string_init (&auth->response))
+ goto enomem_10;
return auth;
+ enomem_10:
+ _dbus_string_free (&auth->response);
#if 0
enomem_9:
_dbus_credentials_unref (auth->desired_identity);
@@ -1317,9 +1327,153 @@ handle_client_shutdown_anonymous_mech (DBusAuth *auth)
}
+/*
+ * DBUS_WINDOWS_SSPI_NTLM mechanism
+ * Implementation is mostly in dbus-systeps-win.c
+ */
+#if defined (DBUS_WIN) && !defined (DBUS_WINCE)
+static dbus_bool_t
+handle_server_data_windows_sspi_ntlm_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ dbus_bool_t done = FALSE;
+ dbus_bool_t local_error = TRUE;
+
+ _dbus_string_set_length (&auth->challenge, 0);
+
+ if (!_dbus_sspi_ntlm_next_challenge (&auth->sspi_ntlm, data, &auth->challenge, &done, &local_error, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (local_error)
+ _dbus_verbose ("%s: Error getting next challenge: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ else
+ send_error (auth, error.message);
+
+ dbus_error_free (&error);
+
+ return FALSE;
+ }
+
+ if (_dbus_string_get_length (&auth->challenge) > 0)
+ send_data (auth, &auth->challenge);
+
+ if (!done)
+ {
+ if (_dbus_string_get_length (&auth->challenge) == 0)
+ {
+ send_error (auth, "SSPI produced 0-length challenge, but authentication is not done yet");
+
+ return FALSE;
+ }
+
+ goto_state (auth, &server_state_waiting_for_data);
+
+ return TRUE;
+ }
+
+ local_error = TRUE;
+
+ if (!_dbus_sspi_ntlm_fetch_credentials (&auth->sspi_ntlm, auth->authorized_identity, &local_error, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (local_error)
+ _dbus_verbose ("%s: Error fetching NTLM-negotiated credentials: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ else
+ send_error (auth, error.message);
+
+ dbus_error_free (&error);
+
+ return FALSE;
+ }
+
+ if (!send_ok (auth))
+ return FALSE;
+
+ _dbus_verbose ("%s: authenticated client using DBUS_WINDOWS_SSPI_NTLM\n",
+ DBUS_AUTH_NAME (auth));
+
+ return TRUE;
+}
+
+static void
+handle_server_shutdown_windows_sspi_ntlm_mech (DBusAuth *auth)
+{
+ _dbus_sspi_ntlm_free_info (&auth->sspi_ntlm);
+}
+
+static dbus_bool_t
+handle_client_initial_response_windows_sspi_ntlm_mech (DBusAuth *auth,
+ DBusString *response)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ dbus_bool_t local_error = TRUE;
+
+ if (!_dbus_sspi_ntlm_initial_response (&auth->sspi_ntlm, response, &local_error, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (local_error)
+ _dbus_verbose ("%s: Error getting initial response: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ else
+ send_error (auth, error.message);
+
+ dbus_error_free (&error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+handle_client_data_windows_sspi_ntlm_mech (DBusAuth *auth,
+ const DBusString *data)
+{
+ DBusError error = DBUS_ERROR_INIT;
+ dbus_bool_t local_error = TRUE;
+
+ _dbus_string_set_length (&auth->response, 0);
+
+ if (!_dbus_sspi_ntlm_next_response (&auth->sspi_ntlm, data, &auth->response, &local_error, &error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (&error);
+
+ if (local_error)
+ _dbus_verbose ("%s: Error getting response: %s\n",
+ DBUS_AUTH_NAME (auth), error.message);
+ else
+ send_error (auth, error.message);
+
+ dbus_error_free (&error);
+
+ return FALSE;
+ }
+
+ if (_dbus_string_get_length (&auth->response) > 0)
+ send_data (auth, &auth->response);
+ else
+ goto_state (auth, &client_state_waiting_for_ok);
+
+ return TRUE;
+}
+
+static void
+handle_client_shutdown_windows_sspi_ntlm_mech (DBusAuth *auth)
+{
+ _dbus_sspi_ntlm_free_info (&auth->sspi_ntlm);
+}
+#endif
+
/* Put mechanisms here in order of preference.
* Right now we have:
*
+ * - DBUS_WINDOWS_SSPI_NTLM uses Windows SSPI API to perform NTLM authentication
* - EXTERNAL checks socket credentials (or in the future, other info from the OS)
* - DBUS_COOKIE_SHA1 uses a cookie in the home directory, like xauth or ICE
* - ANONYMOUS checks nothing but doesn't auth the person as a user
@@ -1330,6 +1484,16 @@ handle_client_shutdown_anonymous_mech (DBusAuth *auth)
*/
static const DBusAuthMechanismHandler
all_mechanisms[] = {
+#if defined (DBUS_WIN) && !defined (DBUS_WINCE)
+ { "DBUS_WINDOWS_SSPI_NTLM",
+ handle_server_data_windows_sspi_ntlm_mech,
+ NULL, NULL,
+ handle_server_shutdown_windows_sspi_ntlm_mech,
+ handle_client_initial_response_windows_sspi_ntlm_mech,
+ handle_client_data_windows_sspi_ntlm_mech,
+ NULL, NULL,
+ handle_client_shutdown_windows_sspi_ntlm_mech },
+#endif
{ "EXTERNAL",
handle_server_data_external_mech,
NULL, NULL,
@@ -2403,6 +2567,7 @@ _dbus_auth_unref (DBusAuth *auth)
_dbus_keyring_unref (auth->keyring);
_dbus_string_free (&auth->context);
+ _dbus_string_free (&auth->response);
_dbus_string_free (&auth->challenge);
_dbus_string_free (&auth->identity);
_dbus_string_free (&auth->incoming);
diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c
index 32b49b821..0af000ed0 100644
--- a/dbus/dbus-sysdeps-win.c
+++ b/dbus/dbus-sysdeps-win.c
@@ -56,8 +56,10 @@
#include <iphlpapi.h>
/* Declarations missing in mingw's and windows sdk 7.0 headers */
+#ifndef __MINGW64_VERSION_MAJOR
extern BOOL WINAPI ConvertStringSidToSidA (LPCSTR StringSid, PSID *Sid);
extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid);
+#endif
#include <stdio.h>
#include <stdlib.h>
@@ -972,61 +974,93 @@ static BOOL is_winxp_sp3_or_lower()
dwlConditionMask);
}
-/** Gets our SID
+/** Gets SID from a token
* @param sid points to sid buffer, need to be freed with LocalFree()
- * @param process_id the process id for which the sid should be returned (use 0 for current process)
- * @returns process sid
+ * @param token a token to get the SID from
+ * @param error an error to set in case of failure
+ * @returns TRUE on success, FALSE otherwise
*/
dbus_bool_t
-_dbus_getsid(char **sid, dbus_pid_t process_id)
+_dbus_get_token_sid (char **sid,
+ HANDLE token,
+ DBusError *error)
{
- HANDLE process_token = INVALID_HANDLE_VALUE;
TOKEN_USER *token_user = NULL;
DWORD n;
PSID psid;
int retval = FALSE;
- HANDLE process_handle;
- if (process_id == 0)
- process_handle = GetCurrentProcess();
- else if (is_winxp_sp3_or_lower())
- process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
- else
- process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
-
- if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token))
- {
- _dbus_win_warn_win_error ("OpenProcessToken failed", GetLastError ());
- goto failed;
- }
- if ((!GetTokenInformation (process_token, TokenUser, NULL, 0, &n)
+ if ((!GetTokenInformation (token, TokenUser, NULL, 0, &n)
&& GetLastError () != ERROR_INSUFFICIENT_BUFFER)
|| (token_user = alloca (n)) == NULL
- || !GetTokenInformation (process_token, TokenUser, token_user, n, &n))
+ || !GetTokenInformation (token, TokenUser, token_user, n, &n))
{
- _dbus_win_warn_win_error ("GetTokenInformation failed", GetLastError ());
+ _dbus_win_set_error_from_win_error (error, GetLastError ());
goto failed;
}
+
psid = token_user->User.Sid;
+
if (!IsValidSid (psid))
{
- _dbus_verbose("%s invalid sid\n",__FUNCTION__);
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid token SID");
goto failed;
}
+
if (!ConvertSidToStringSidA (psid, sid))
{
- _dbus_verbose("%s invalid sid\n",__FUNCTION__);
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Can't convert token SID to string");
goto failed;
}
+
//okay:
retval = TRUE;
failed:
+
+ _dbus_verbose("_dbus_get_token_sid() got '%s' and returns %d\n", *sid, retval);
+ return retval;
+}
+
+/** Gets our SID
+ * @param sid points to sid buffer, need to be freed with LocalFree()
+ * @param process_id the process id for which the sid should be returned (use 0 for current process)
+ * @returns process sid
+ */
+dbus_bool_t
+_dbus_getsid(char **sid, dbus_pid_t process_id)
+{
+ HANDLE process_handle;
+ HANDLE process_token = INVALID_HANDLE_VALUE;
+ DBusError error;
+ int retval = FALSE;
+
+ if (process_id == 0)
+ process_handle = GetCurrentProcess();
+ else if (is_winxp_sp3_or_lower())
+ process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
+ else
+ process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
+
+ if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token))
+ {
+ _dbus_win_warn_win_error ("OpenProcessToken failed", GetLastError ());
+ goto end;
+ }
+
+ dbus_error_init (&error);
+ retval = _dbus_get_token_sid (sid, process_token, &error);
+
+ if (!retval)
+ _dbus_warn ("Failed to get process token SID: %s\n", error.message);
+
+ dbus_error_free (&error);
+
+end:
CloseHandle (process_handle);
if (process_token != INVALID_HANDLE_VALUE)
CloseHandle (process_token);
- _dbus_verbose("_dbus_getsid() got '%s' and returns %d\n", *sid, retval);
return retval;
}
#endif
@@ -2518,7 +2552,7 @@ static void dump_backtrace_for_thread (HANDLE hThread)
if (SymFromAddr (GetCurrentProcess (), sf.AddrPC.Offset, &displacement, pSymbol))
{
if (displacement)
- DPRINTF ("%3d %s+0x%I64x", i++, pSymbol->Name, displacement);
+ DPRINTF ("%3d %s+0x%" PRIx64, i++, pSymbol->Name, displacement);
else
DPRINTF ("%3d %s", i++, pSymbol->Name);
}
@@ -3569,6 +3603,153 @@ _dbus_win_warn_win_error (const char *message,
dbus_error_free (&error);
}
+typedef struct DBusSSPIErrorEntry DBusSSPIErrorEntry;
+
+struct DBusSSPIErrorEntry
+{
+ SECURITY_STATUS value;
+ const char *literal;
+};
+
+#define def_sspi_err(literal) { literal, #literal },
+
+static DBusSSPIErrorEntry dbus_sspi_error_map[] =
+{
+ def_sspi_err (SEC_E_ALGORITHM_MISMATCH)
+ def_sspi_err (SEC_E_BAD_BINDINGS)
+ def_sspi_err (SEC_E_BAD_PKGID)
+ def_sspi_err (SEC_E_BUFFER_TOO_SMALL)
+ def_sspi_err (SEC_E_CANNOT_INSTALL)
+ def_sspi_err (SEC_E_CANNOT_PACK)
+ def_sspi_err (SEC_E_CERT_EXPIRED)
+ def_sspi_err (SEC_E_CERT_UNKNOWN)
+ def_sspi_err (SEC_E_CERT_WRONG_USAGE)
+ def_sspi_err (SEC_E_CONTEXT_EXPIRED)
+ def_sspi_err (SEC_E_CROSSREALM_DELEGATION_FAILURE)
+ def_sspi_err (SEC_E_CRYPTO_SYSTEM_INVALID)
+ def_sspi_err (SEC_E_DECRYPT_FAILURE)
+ def_sspi_err (SEC_E_DELEGATION_REQUIRED)
+ def_sspi_err (SEC_E_DOWNGRADE_DETECTED)
+ def_sspi_err (SEC_E_ENCRYPT_FAILURE)
+ def_sspi_err (SEC_E_ILLEGAL_MESSAGE)
+ def_sspi_err (SEC_E_INCOMPLETE_CREDENTIALS)
+ def_sspi_err (SEC_E_INCOMPLETE_MESSAGE)
+ def_sspi_err (SEC_E_INSUFFICIENT_MEMORY)
+ def_sspi_err (SEC_E_INTERNAL_ERROR)
+ def_sspi_err (SEC_E_INVALID_HANDLE)
+ def_sspi_err (SEC_E_INVALID_TOKEN)
+ def_sspi_err (SEC_E_ISSUING_CA_UNTRUSTED)
+ def_sspi_err (SEC_E_ISSUING_CA_UNTRUSTED_KDC)
+ def_sspi_err (SEC_E_KDC_CERT_EXPIRED)
+ def_sspi_err (SEC_E_KDC_CERT_REVOKED)
+ def_sspi_err (SEC_E_KDC_INVALID_REQUEST)
+ def_sspi_err (SEC_E_KDC_UNABLE_TO_REFER)
+ def_sspi_err (SEC_E_KDC_UNKNOWN_ETYPE)
+ def_sspi_err (SEC_E_LOGON_DENIED)
+ def_sspi_err (SEC_E_MAX_REFERRALS_EXCEEDED)
+ def_sspi_err (SEC_E_MESSAGE_ALTERED)
+ def_sspi_err (SEC_E_MULTIPLE_ACCOUNTS)
+ def_sspi_err (SEC_E_MUST_BE_KDC)
+ def_sspi_err (SEC_E_NO_AUTHENTICATING_AUTHORITY)
+ def_sspi_err (SEC_E_NO_CREDENTIALS)
+ def_sspi_err (SEC_E_NO_IMPERSONATION)
+ def_sspi_err (SEC_E_NO_IP_ADDRESSES)
+ def_sspi_err (SEC_E_NO_KERB_KEY)
+ def_sspi_err (SEC_E_NO_PA_DATA)
+ def_sspi_err (SEC_E_NO_S4U_PROT_SUPPORT)
+ def_sspi_err (SEC_E_NO_TGT_REPLY)
+ def_sspi_err (SEC_E_NOT_OWNER)
+ def_sspi_err (SEC_E_NOT_SUPPORTED)
+ def_sspi_err (SEC_E_OK)
+ def_sspi_err (SEC_E_OUT_OF_SEQUENCE)
+ def_sspi_err (SEC_E_PKINIT_CLIENT_FAILURE)
+ def_sspi_err (SEC_E_PKINIT_NAME_MISMATCH)
+ def_sspi_err (SEC_E_QOP_NOT_SUPPORTED)
+ def_sspi_err (SEC_E_REVOCATION_OFFLINE_C)
+ def_sspi_err (SEC_E_REVOCATION_OFFLINE_KDC)
+ def_sspi_err (SEC_E_SECPKG_NOT_FOUND)
+ def_sspi_err (SEC_E_SECURITY_QOS_FAILED)
+ def_sspi_err (SEC_E_SHUTDOWN_IN_PROGRESS)
+ def_sspi_err (SEC_E_SMARTCARD_CERT_EXPIRED)
+ def_sspi_err (SEC_E_SMARTCARD_CERT_REVOKED)
+ def_sspi_err (SEC_E_SMARTCARD_LOGON_REQUIRED)
+ def_sspi_err (SEC_E_STRONG_CRYPTO_NOT_SUPPORTED)
+ def_sspi_err (SEC_E_TARGET_UNKNOWN)
+ def_sspi_err (SEC_E_TIME_SKEW)
+ def_sspi_err (SEC_E_TOO_MANY_PRINCIPALS)
+ def_sspi_err (SEC_E_UNFINISHED_CONTEXT_DELETED)
+ def_sspi_err (SEC_E_UNKNOWN_CREDENTIALS)
+ def_sspi_err (SEC_E_UNSUPPORTED_FUNCTION)
+ def_sspi_err (SEC_E_UNSUPPORTED_PREAUTH)
+ def_sspi_err (SEC_E_UNTRUSTED_ROOT)
+ def_sspi_err (SEC_E_WRONG_CREDENTIAL_HANDLE)
+ def_sspi_err (SEC_E_WRONG_PRINCIPAL)
+ def_sspi_err (SEC_I_COMPLETE_AND_CONTINUE)
+ def_sspi_err (SEC_I_COMPLETE_NEEDED)
+ def_sspi_err (SEC_I_CONTEXT_EXPIRED)
+ def_sspi_err (SEC_I_CONTINUE_NEEDED)
+ def_sspi_err (SEC_I_INCOMPLETE_CREDENTIALS)
+ def_sspi_err (SEC_I_LOCAL_LOGON)
+ def_sspi_err (SEC_I_NO_LSA_CONTEXT)
+ def_sspi_err (SEC_I_RENEGOTIATE)
+ def_sspi_err (SEC_E_OK)
+};
+#undef def_sspi_err
+
+
+/**
+ * Assigns an error name and message corresponding to SSPI return value
+ * code to a DBusError. Does nothing if error is #NULL.
+ * Note that this function can be slow-ish, so only call it when
+ * SSPI returns unexpected status code, which is treated as an error.
+ *
+ * @param error the error.
+ * @param func_call description of the function that produced the status. Can be NULL.
+ * @param status the value returned from a SSPI function
+ */
+void
+_dbus_win_set_error_from_sspi_status (DBusError *error,
+ const char *func_call,
+ SECURITY_STATUS status)
+{
+ int i;
+ const char *literal;
+
+ /* This is obviously suboptimal, but this function is no invoked when
+ * everything is going fine, so no big deal.
+ */
+ for (literal = NULL, i = 0; dbus_sspi_error_map[i].value != SEC_E_OK; i++)
+ {
+ if (dbus_sspi_error_map[i].value != status)
+ continue;
+
+ literal = dbus_sspi_error_map[i].literal;
+ }
+
+ if (literal == NULL)
+ {
+ literal = "UNRECOGNIZED ERROR CODE";
+ }
+
+ if (func_call)
+ dbus_set_error (error, DBUS_ERROR_FAILED, "%s returned 0x%08lx (%s)", func_call, status, literal);
+ else
+ dbus_set_error (error, DBUS_ERROR_FAILED, "0x%08lx (%s)", status, literal);
+}
+
+void
+_dbus_win_warn_sspi_status (const char *message,
+ const char *func_call,
+ SECURITY_STATUS status)
+{
+ DBusError error;
+
+ dbus_error_init (&error);
+ _dbus_win_set_error_from_sspi_status (&error, func_call, status);
+ _dbus_warn ("%s: %s\n", message, error.message);
+ dbus_error_free (&error);
+}
+
/**
* Removes a directory; Directory must be empty
*
@@ -3686,5 +3867,409 @@ _dbus_logv (DBusSystemLogSeverity severity,
exit (1);
}
+/*
+ * DBUS_WINDOWS_SSPI_NTLM mechanism
+ */
+#ifndef DBUS_WINCE
+/**
+ * Feeds client response to SSPI, producing the next challenge.
+ *
+ * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth
+ * @param data - inbound client response
+ * @param challenge - string to fill with the next challenge
+ * @param done - boolean to set when SSPI authentication succeeded
+ * @param error - an error to send back (optional)
+ *
+ * The @challenge is always valid (even if its length ends up being zero),
+ * as long as the function returns TRUE. The @challenge must be sent back
+ * to the client, then freed with _dbus_string_free().
+ *
+ * @returns #TRUE if everything is OK, @challenge and is @response are valid, #FALSE otherwise
+ */
+dbus_bool_t
+_dbus_sspi_ntlm_next_challenge (DBusSSPINTLMInfo *sspi_ntlm,
+ const DBusString *data,
+ DBusString *challenge,
+ dbus_bool_t *done,
+ dbus_bool_t *error_is_local,
+ DBusError *error)
+{
+ SECURITY_STATUS ss;
+ PSecPkgInfoW sec_package_info;
+ TimeStamp lifetime;
+ SecBuffer input_sec_buffer = { 0, SECBUFFER_TOKEN, NULL};
+ SecBufferDesc input_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &input_sec_buffer };
+ SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL};
+ SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer };
+ ULONG attributes;
+ dbus_bool_t is_done;
+
+ input_sec_buffer.cbBuffer = _dbus_string_get_length (data);
+ input_sec_buffer.pvBuffer = _dbus_string_get_data (data);
+
+ if (input_sec_buffer.cbBuffer == 0)
+ {
+ *error_is_local = FALSE;
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Received 0-length DATA, which is unacceptable");
+
+ return FALSE;
+ }
+
+ if (sspi_ntlm->message_limit == 0)
+ {
+ ss = QuerySecurityPackageInfoW (L"NTLM", &sec_package_info);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "QuerySecurityPackageInfoW (NTLM)", ss);
+
+ return FALSE;
+ }
+
+ sspi_ntlm->message_limit = sec_package_info->cbMaxToken;
+
+ ss = FreeContextBuffer (sec_package_info);
+
+ if (ss != SEC_E_OK)
+ _dbus_win_warn_sspi_status ("server", "FreeContextBuffer (PSecPkgInfoW)", ss);
+ }
+
+ if (!sspi_ntlm->free_credentials)
+ {
+ ss = AcquireCredentialsHandleW (NULL, L"NTLM", SECPKG_CRED_INBOUND, 0, 0, 0, 0,
+ &sspi_ntlm->credentials, &lifetime);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "AcquireCredentialsHandleW (NTLM, INBOUND)", ss);
+
+ return FALSE;
+ }
+
+ sspi_ntlm->free_credentials = TRUE;
+ }
+
+ _dbus_string_set_length (challenge, sspi_ntlm->message_limit);
+
+ output_sec_buffer.cbBuffer = _dbus_string_get_length (challenge);
+ output_sec_buffer.pvBuffer = _dbus_string_get_data (challenge);
+
+ ss = AcceptSecurityContext (&sspi_ntlm->credentials,
+ sspi_ntlm->free_context ? &sspi_ntlm->context : NULL,
+ &input_sec_buffers_descriptor,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &sspi_ntlm->context,
+ &output_sec_buffers_descriptor,
+ &attributes,
+ &lifetime);
+
+ if (ss != SEC_E_OK &&
+ ss != SEC_I_COMPLETE_AND_CONTINUE &&
+ ss != SEC_I_COMPLETE_NEEDED &&
+ ss != SEC_I_CONTINUE_NEEDED)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "AcceptSecurityContext ()", ss);
+
+ return FALSE;
+ }
+
+ sspi_ntlm->free_context = TRUE;
+
+ is_done = !((SEC_I_CONTINUE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss));
+
+ if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))
+ {
+ ss = CompleteAuthToken (&sspi_ntlm->context,
+ &output_sec_buffers_descriptor);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "CompleteAuthToken ()", ss);
+
+ return FALSE;
+ }
+ }
+
+ _dbus_string_set_length (challenge, output_sec_buffer.cbBuffer);
+
+ *done = is_done;
+
+ return TRUE;
+}
+
+/**
+ * Converts successfully authenticated SSPI security context
+ * attributes into credentials, adding them to DBusCredentials.
+ *
+ * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth
+ * @param credentials - credentials object to modify
+ *
+ * @returns #TRUE if everything is OK and @credentials were modified, #FALSE otherwise
+ */
+dbus_bool_t
+_dbus_sspi_ntlm_fetch_credentials (DBusSSPINTLMInfo *sspi_ntlm,
+ DBusCredentials *credentials,
+ dbus_bool_t *error_is_local,
+ DBusError *error)
+{
+ SECURITY_STATUS ss;
+ HANDLE client_token;
+ char *sid;
+ dbus_bool_t result;
+
+ ss = QuerySecurityContextToken (&sspi_ntlm->context, &client_token);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "QuerySecurityContextToken ()", ss);
+
+ return FALSE;
+ }
+
+ result = _dbus_get_token_sid (&sid, client_token, error);
+
+ if (!result)
+ {
+ CloseHandle (client_token);
+ *error_is_local = TRUE;
+
+ return FALSE;
+ }
+
+ result = _dbus_credentials_add_windows_sid (credentials, sid);
+
+ if (!result)
+ {
+ *error_is_local = TRUE;
+ /* _dbus_credentials_add_windows_sid() currently only fails when OOM */
+ _DBUS_SET_OOM (error);
+ }
+
+ LocalFree (sid);
+ CloseHandle (client_token);
+
+ return result;
+}
+
+/**
+ * Cleans up any state initialized by _dbus_sspi_ntlm_*() functions.
+ *
+ * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth
+ */
+void
+_dbus_sspi_ntlm_free_info (DBusSSPINTLMInfo *sspi_ntlm)
+{
+ if (sspi_ntlm->free_context)
+ DeleteSecurityContext (&sspi_ntlm->context);
+
+ sspi_ntlm->free_context = FALSE;
+
+ if (sspi_ntlm->free_credentials)
+ FreeCredentialHandle (&sspi_ntlm->credentials);
+
+ sspi_ntlm->free_credentials = FALSE;
+}
+
+/**
+ * Generates initial client response for SSPI NTLM authentication and
+ * appends it to @initial_response.
+ *
+ * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth
+ * @param initial_response - outbound data to append the response to
+ *
+ * @returns #TRUE if everything is OK and @initial_response was modified, #FALSE otherwise
+ */
+dbus_bool_t
+_dbus_sspi_ntlm_initial_response (DBusSSPINTLMInfo *sspi_ntlm,
+ DBusString *initial_response,
+ dbus_bool_t *error_is_local,
+ DBusError *error)
+{
+ SECURITY_STATUS ss;
+ PSecPkgInfoW sec_package_info;
+ TimeStamp lifetime;
+ SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL};
+ SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer };
+ ULONG attributes;
+ DBusString plaintext;
+ dbus_bool_t result;
+
+ ss = QuerySecurityPackageInfoW (L"NTLM", &sec_package_info);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "QuerySecurityPackageInfoW ()", ss);
+
+ return FALSE;
+ }
+
+ sspi_ntlm->message_limit = sec_package_info->cbMaxToken;
+
+ ss = FreeContextBuffer (sec_package_info);
+
+ if (ss != SEC_E_OK)
+ _dbus_win_warn_sspi_status ("client", "FreeContextBuffer (PSecPkgInfoW)", ss);
+
+ ss = AcquireCredentialsHandleW (NULL, L"NTLM", SECPKG_CRED_OUTBOUND, 0, 0, 0, 0,
+ &sspi_ntlm->credentials, &lifetime);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "AcquireCredentialsHandleW (NTLM, OUTBOUND)", ss);
+
+ return FALSE;
+ }
+
+ sspi_ntlm->free_credentials = TRUE;
+ _dbus_string_init (&plaintext);
+ _dbus_string_set_length (&plaintext, sspi_ntlm->message_limit);
+
+ output_sec_buffer.cbBuffer = _dbus_string_get_length (&plaintext);
+ output_sec_buffer.pvBuffer = _dbus_string_get_data (&plaintext);
+
+ ss = InitializeSecurityContextW (&sspi_ntlm->credentials,
+ NULL,
+ NULL,
+ ISC_REQ_IDENTIFY,
+ 0,
+ SECURITY_NATIVE_DREP,
+ NULL,
+ 0,
+ &sspi_ntlm->context,
+ &output_sec_buffers_descriptor,
+ &attributes,
+ &lifetime);
+
+ if (ss != SEC_E_OK &&
+ ss != SEC_I_COMPLETE_AND_CONTINUE &&
+ ss != SEC_I_COMPLETE_NEEDED &&
+ ss != SEC_I_CONTINUE_NEEDED)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "InitializeSecurityContextW ()", ss);
+ _dbus_string_free (&plaintext);
+
+ return FALSE;
+ }
+
+ sspi_ntlm->free_context = TRUE;
+
+ if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))
+ {
+ ss = CompleteAuthToken (&sspi_ntlm->context,
+ &output_sec_buffers_descriptor);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "CompleteAuthToken ()", ss);
+ _dbus_string_free (&plaintext);
+
+ return FALSE;
+ }
+ }
+
+ _dbus_string_set_length (&plaintext, output_sec_buffer.cbBuffer);
+
+ result = _dbus_string_hex_encode (&plaintext, 0, initial_response,
+ _dbus_string_get_length (initial_response));
+
+ _dbus_string_free (&plaintext);
+
+ return result;
+}
+
+/**
+ * Feeds server challenge to SSPI, producing the next response.
+ *
+ * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth
+ * @param data - inbound server challenge
+ * @param response - string to fill with the next response
+ *
+ * @returns #TRUE if everything is OK and @response are valid, #FALSE otherwise
+ */
+dbus_bool_t
+_dbus_sspi_ntlm_next_response (DBusSSPINTLMInfo *sspi_ntlm,
+ const DBusString *data,
+ DBusString *response,
+ dbus_bool_t *error_is_local,
+ DBusError *error)
+{
+ SECURITY_STATUS ss;
+ TimeStamp lifetime;
+ SecBuffer input_sec_buffer = { 0, SECBUFFER_TOKEN, NULL};
+ SecBufferDesc input_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &input_sec_buffer };
+ SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL};
+ SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer };
+ ULONG attributes;
+
+ input_sec_buffer.cbBuffer = _dbus_string_get_length (data);
+ input_sec_buffer.pvBuffer = _dbus_string_get_data (data);
+
+ if (input_sec_buffer.cbBuffer == 0)
+ {
+ *error_is_local = FALSE;
+ dbus_set_error_const (error, DBUS_ERROR_FAILED, "Received 0-length DATA, which is unacceptable");
+
+ return FALSE;
+ }
+
+ _dbus_string_set_length (response, sspi_ntlm->message_limit);
+
+ output_sec_buffer.cbBuffer = _dbus_string_get_length (response);
+ output_sec_buffer.pvBuffer = _dbus_string_get_data (response);
+
+ ss = InitializeSecurityContextW (&sspi_ntlm->credentials,
+ &sspi_ntlm->context,
+ NULL,
+ ISC_REQ_IDENTIFY,
+ 0,
+ SECURITY_NATIVE_DREP,
+ &input_sec_buffers_descriptor,
+ 0,
+ &sspi_ntlm->context,
+ &output_sec_buffers_descriptor,
+ &attributes,
+ &lifetime);
+
+ if (ss != SEC_E_OK &&
+ ss != SEC_I_COMPLETE_AND_CONTINUE &&
+ ss != SEC_I_COMPLETE_NEEDED &&
+ ss != SEC_I_CONTINUE_NEEDED)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "InitializeSecurityContextW ()", ss);
+
+ return FALSE;
+ }
+
+ if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))
+ {
+ ss = CompleteAuthToken (&sspi_ntlm->context,
+ &output_sec_buffers_descriptor);
+
+ if (ss != SEC_E_OK)
+ {
+ *error_is_local = TRUE;
+ _dbus_win_set_error_from_sspi_status (error, "CompleteAuthToken ()", ss);
+
+ return FALSE;
+ }
+ }
+
+ _dbus_string_set_length (response, output_sec_buffer.cbBuffer);
+
+ return TRUE;
+}
+#endif
+
/** @} end of sysdeps-win */
/* tests in dbus-sysdeps-util.c */
diff --git a/dbus/dbus-sysdeps-win.h b/dbus/dbus-sysdeps-win.h
index 0b5d8f030..2a667ad02 100644
--- a/dbus/dbus-sysdeps-win.h
+++ b/dbus/dbus-sysdeps-win.h
@@ -26,6 +26,8 @@
#ifndef DBUS_SYSDEPS_WIN_H
#define DBUS_SYSDEPS_WIN_H
+#define SECURITY_WIN32
+
extern void *_dbus_win_get_dll_hmodule (void);
#define WIN32_LEAN_AND_MEAN
@@ -36,6 +38,11 @@ extern void *_dbus_win_get_dll_hmodule (void);
#include <windows.h>
#undef interface
+#ifndef DBUS_WINCE
+#include <sspi.h>
+#include <sddl.h>
+#endif
+
#define DBUS_CONSOLE_DIR "/var/run/console/"
@@ -91,7 +98,61 @@ void _dbus_threads_windows_init_global (void);
void _dbus_threads_windows_ensure_ctor_linked (void);
DBUS_PRIVATE_EXPORT
-dbus_bool_t _dbus_getsid(char **sid, dbus_pid_t process_id);
+dbus_bool_t _dbus_get_token_sid (char **sid,
+ HANDLE token,
+ DBusError *error);
+DBUS_PRIVATE_EXPORT
+dbus_bool_t _dbus_getsid (char **sid,
+ dbus_pid_t process_id);
+
+/** Information for SSPI NTLM authentication */
+typedef struct DBusSSPINTLMInfo DBusSSPINTLMInfo;
+
+/**
+ * Information for SSPI NTLM authentication
+ */
+struct DBusSSPINTLMInfo
+{
+#ifndef DBUS_WINCE
+ CredHandle credentials; /**< SSPI Credentials. */
+ CtxtHandle context; /**< SSPI Context. */
+ size_t message_limit; /**< Maximum size of an SSPI NTLM message. */
+ char *output_buffer; /**< Pre-allocated buffer for an SSPI NTLM message. */
+ unsigned int free_credentials : 1; /**< Must free credentials on shutdown */
+ unsigned int free_context : 1; /**< Must free context on shutdown */
+#endif
+};
+
+void _dbus_win_set_error_from_sspi_status (DBusError *error,
+ const char *func_call,
+ SECURITY_STATUS status);
+void _dbus_win_warn_sspi_status (const char *message,
+ const char *func_call,
+ SECURITY_STATUS status);
+
+dbus_bool_t _dbus_sspi_ntlm_next_challenge (DBusSSPINTLMInfo *sspi_ntlm,
+ const DBusString *data,
+ DBusString *challenge,
+ dbus_bool_t *done,
+ dbus_bool_t *error_is_local,
+ DBusError *error);
+dbus_bool_t _dbus_sspi_ntlm_fetch_credentials (DBusSSPINTLMInfo *sspi_ntlm,
+ DBusCredentials *credentials,
+ dbus_bool_t *error_is_local,
+ DBusError *error);
+
+dbus_bool_t _dbus_sspi_ntlm_initial_response (DBusSSPINTLMInfo *sspi_ntlm,
+ DBusString *initial_response,
+ dbus_bool_t *error_is_local,
+ DBusError *error);
+dbus_bool_t _dbus_sspi_ntlm_next_response (DBusSSPINTLMInfo *sspi_ntlm,
+ const DBusString *data,
+ DBusString *response,
+ dbus_bool_t *error_is_local,
+ DBusError *error);
+
+void _dbus_sspi_ntlm_free_info (DBusSSPINTLMInfo *sspi_ntlm);
+
#endif
/** @} end of sysdeps-win.h */