From eec0e8ade30c8c7c7f542bcf12983d90bad91499 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: Sat, 18 Jun 2016 11:50:00 +0000 Subject: [PATCH] Support SSPI NTLM authentication mechanism https://bugs.freedesktop.org/show_bug.cgi?id=96577 --- configure.ac | 2 +- dbus/dbus-auth.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++ dbus/dbus-sysdeps-win.c | 62 ++++--- dbus/dbus-sysdeps-win.h | 6 +- 4 files changed, 471 insertions(+), 23 deletions(-) diff --git a/configure.ac b/configure.ac index 1d08030..98aa403 100644 --- a/configure.ac +++ b/configure.ac @@ -1227,7 +1227,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 f222787..c4a1da0 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -31,6 +31,10 @@ #include "dbus-protocol.h" #include "dbus-credentials.h" +#define SECURITY_WIN32 +#include +#include + /** * @defgroup DBusAuth Authentication * @ingroup DBusInternals @@ -178,6 +182,11 @@ struct DBusAuth int cookie_id; /**< ID of cookie to use */ DBusString challenge; /**< Challenge sent to client */ + CredHandle sspi_ntlm_credentials; /**< SSPI Credentials. */ + CtxtHandle sspi_ntlm_context; /**< SSPI Context. */ + size_t sspi_ntlm_message_limit; /**< Maximum size of an SSPI NTLM message. */ + char *sspi_output_buffer; /**< Pre-allocated buffer for an SSPI NTLM message. */ + char **allowed_mechs; /**< Mechanisms we're allowed to use, * or #NULL if we can use any */ @@ -191,6 +200,9 @@ struct DBusAuth unsigned int unix_fd_possible : 1; /**< This side could do unix fd passing */ unsigned int unix_fd_negotiated : 1; /**< Unix fd was successfully negotiated */ + + unsigned int free_sspi_ntlm_credentials : 1; /**< Must free credentials on shutdown */ + unsigned int free_sspi_ntlm_context : 1; /**< Must free context on shutdown */ }; /** @@ -1314,6 +1326,410 @@ handle_client_shutdown_anonymous_mech (DBusAuth *auth) } +/* + * DBUS_WINDOWS_SSPI_NTLM mechanism + */ + +static dbus_bool_t +handle_server_data_windows_sspi_ntlm_mech (DBusAuth *auth, + const DBusString *data) +{ + 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; + DBusString plaintext; + dbus_bool_t done; + HANDLE client_token; + char *sid; + + input_sec_buffer.cbBuffer = _dbus_string_get_length (data); + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_data (data); + + if (input_sec_buffer.cbBuffer == 0) + { + if (!auth->free_sspi_ntlm_context) + { + _dbus_verbose ("%s: Got 0-length data, before negotiating a context\n", + DBUS_AUTH_NAME (auth)); + + return FALSE; + } + + return TRUE; + } + + if (!auth->sspi_output_buffer) + { + ss = QuerySecurityPackageInfoW (L"NTLM", &sec_package_info); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: QuerySecurityPackageInfoW (NTLM) returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + auth->sspi_ntlm_message_limit = sec_package_info->cbMaxToken; + + ss = FreeContextBuffer (sec_package_info); + + if (ss != SEC_E_OK) + _dbus_verbose ("%s: FreeContextBuffer (PSecPkgInfoW) returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + auth->sspi_output_buffer = dbus_malloc (auth->sspi_ntlm_message_limit); + } + + if (!auth->free_sspi_ntlm_credentials) + { + ss = AcquireCredentialsHandleW (NULL, L"NTLM", SECPKG_CRED_INBOUND, 0, 0, 0, 0, + &auth->sspi_ntlm_credentials, &lifetime); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: AcquireCredentialsHandleW (NTLM, INBOUND) returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + auth->free_sspi_ntlm_credentials = TRUE; + } + + output_sec_buffer.cbBuffer = auth->sspi_ntlm_message_limit; + output_sec_buffer.pvBuffer = auth->sspi_output_buffer; + + ss = AcceptSecurityContext (&auth->sspi_ntlm_credentials, + auth->free_sspi_ntlm_context ? &auth->sspi_ntlm_context : NULL, + &input_sec_buffers_descriptor, + 0, + SECURITY_NATIVE_DREP, + &auth->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) + { + _dbus_verbose ("%s: AcceptSecurityContext () returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + auth->free_sspi_ntlm_context = TRUE; + + 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 (&auth->sspi_ntlm_context, + &output_sec_buffers_descriptor); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: CompleteAuthToken () returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + } + + _dbus_string_init_const_len (&plaintext, + output_sec_buffer.pvBuffer, + output_sec_buffer.cbBuffer); + + send_data (auth, &plaintext); + + _dbus_string_free (&plaintext); + + if (!done) + { + goto_state (auth, &server_state_waiting_for_data); + + return TRUE; + } + + ss = QuerySecurityContextToken (&auth->sspi_ntlm_context, &client_token); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: QuerySecurityContextToken () returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + if (!_dbus_get_token_sid (&sid, client_token)) + { + CloseHandle (client_token); + + return FALSE; + } + + CloseHandle (client_token); + + if (!_dbus_credentials_add_windows_sid (auth->authorized_identity, sid)) + { + LocalFree (sid); + + return FALSE; + } + + if (!send_ok (auth)) + { + LocalFree (sid); + + return FALSE; + } + + _dbus_verbose ("%s: authenticated client as %s using DBUS_WINDOWS_SSPI_NTLM\n", + DBUS_AUTH_NAME (auth), sid); + LocalFree (sid); + + return TRUE; +} + +static void +handle_server_shutdown_windows_sspi_ntlm_mech (DBusAuth *auth) +{ + dbus_free (auth->sspi_output_buffer); + auth->sspi_output_buffer = NULL; + + if (auth->free_sspi_ntlm_context) + DeleteSecurityContext (&auth->sspi_ntlm_context); + + auth->free_sspi_ntlm_context = FALSE; + + if (auth->free_sspi_ntlm_credentials) + FreeCredentialHandle (&auth->sspi_ntlm_credentials); + + auth->free_sspi_ntlm_credentials = FALSE; +} + +static dbus_bool_t +handle_client_initial_response_windows_sspi_ntlm_mech (DBusAuth *auth, + DBusString *response) +{ + 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 done;*/ + + ss = QuerySecurityPackageInfoW (L"NTLM", &sec_package_info); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: QuerySecurityPackageInfoW (NTLM) returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + auth->sspi_ntlm_message_limit = sec_package_info->cbMaxToken; + + ss = FreeContextBuffer (sec_package_info); + + if (ss != SEC_E_OK) + _dbus_verbose ("%s: FreeContextBuffer (PSecPkgInfoW) returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + ss = AcquireCredentialsHandleW (NULL, L"NTLM", SECPKG_CRED_OUTBOUND, 0, 0, 0, 0, + &auth->sspi_ntlm_credentials, &lifetime); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: AcquireCredentialsHandleW (NTLM, OUTBOUND) returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + auth->free_sspi_ntlm_credentials = TRUE; + auth->sspi_output_buffer = dbus_malloc (auth->sspi_ntlm_message_limit); + + output_sec_buffer.cbBuffer = auth->sspi_ntlm_message_limit; + output_sec_buffer.pvBuffer = auth->sspi_output_buffer; + + ss = InitializeSecurityContextW (&auth->sspi_ntlm_credentials, + NULL, + NULL, + ISC_REQ_IDENTIFY, + 0, + SECURITY_NATIVE_DREP, + NULL, + 0, + &auth->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) + { + _dbus_verbose ("%s: InitializeSecurityContextW () returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + auth->free_sspi_ntlm_context = TRUE; + + /*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 (&auth->sspi_ntlm_context, + &output_sec_buffers_descriptor); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: CompleteAuthToken () returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + } + + _dbus_string_init_const_len (&plaintext, output_sec_buffer.pvBuffer, output_sec_buffer.cbBuffer); + + if (!_dbus_string_hex_encode (&plaintext, 0, response, _dbus_string_get_length (response))) + { + _dbus_string_free (&plaintext); + + return FALSE; + } + + _dbus_string_free (&plaintext); + + return TRUE; +} + +static dbus_bool_t +handle_client_data_windows_sspi_ntlm_mech (DBusAuth *auth, + const DBusString *data) +{ + 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; + DBusString plaintext; + dbus_bool_t done; + + input_sec_buffer.cbBuffer = _dbus_string_get_length (data); + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_data (data); + + if (input_sec_buffer.cbBuffer == 0) + { + if (!auth->free_sspi_ntlm_context) + { + _dbus_verbose ("%s: Got 0-length data, before negotiating a context\n", + DBUS_AUTH_NAME (auth)); + + return FALSE; + } + + goto_state (auth, &client_state_waiting_for_ok); + + return TRUE; + } + + output_sec_buffer.cbBuffer = auth->sspi_ntlm_message_limit; + output_sec_buffer.pvBuffer = auth->sspi_output_buffer; + + ss = InitializeSecurityContextW (&auth->sspi_ntlm_credentials, + &auth->sspi_ntlm_context, + NULL, + ISC_REQ_IDENTIFY, + 0, + SECURITY_NATIVE_DREP, + &input_sec_buffers_descriptor, + 0, + &auth->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) + { + _dbus_verbose ("%s: InitializeSecurityContextW () returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + + 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 (&auth->sspi_ntlm_context, + &output_sec_buffers_descriptor); + + if (ss != SEC_E_OK) + { + _dbus_verbose ("%s: CompleteAuthToken () returned 0x%08lx\n", + DBUS_AUTH_NAME (auth), ss); + + return FALSE; + } + } + + _dbus_string_init_const_len (&plaintext, + output_sec_buffer.pvBuffer, + output_sec_buffer.cbBuffer); + + send_data (auth, &plaintext); + + goto_state (auth, &client_state_waiting_for_data); + + if (done) + { + /* TODO: Set the server credentials? */ + } + + _dbus_string_free (&plaintext); + + return TRUE; +} + +static void +handle_client_shutdown_windows_sspi_ntlm_mech (DBusAuth *auth) +{ + dbus_free (auth->sspi_output_buffer); + auth->sspi_output_buffer = NULL; + + if (auth->free_sspi_ntlm_context) + DeleteSecurityContext (&auth->sspi_ntlm_context); + + auth->free_sspi_ntlm_context = FALSE; + + if (auth->free_sspi_ntlm_credentials) + FreeCredentialHandle (&auth->sspi_ntlm_credentials); + + auth->free_sspi_ntlm_credentials = FALSE; +} + /* Put mechanisms here in order of preference. * Right now we have: * @@ -1327,6 +1743,14 @@ handle_client_shutdown_anonymous_mech (DBusAuth *auth) */ static const DBusAuthMechanismHandler all_mechanisms[] = { + { "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 }, { "EXTERNAL", handle_server_data_external_mech, NULL, NULL, diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 0d64bc1..97fa0a9 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -972,61 +972,81 @@ 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 + * @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) { - 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 ()); goto failed; } + psid = token_user->User.Sid; + if (!IsValidSid (psid)) { _dbus_verbose("%s invalid sid\n",__FUNCTION__); goto failed; } + if (!ConvertSidToStringSidA (psid, sid)) { _dbus_verbose("%s invalid sid\n",__FUNCTION__); 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; + 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; + } + retval = _dbus_get_token_sid (sid, process_token); +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 diff --git a/dbus/dbus-sysdeps-win.h b/dbus/dbus-sysdeps-win.h index 0b5d8f0..2222336 100644 --- a/dbus/dbus-sysdeps-win.h +++ b/dbus/dbus-sysdeps-win.h @@ -91,7 +91,11 @@ 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); +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_getsid (char **sid, + dbus_pid_t process_id); #endif /** @} end of sysdeps-win.h */ -- 2.4.0