From d54ae9c40e506c1b1d4755f15301664794853c74 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 | 286 ++++++++++++++++++++++ dbus/dbus-sysdeps-win.c | 637 ++++++++++++++++++++++++++++++++++++++++++++++-- dbus/dbus-sysdeps-win.h | 63 ++++- 4 files changed, 961 insertions(+), 27 deletions(-) diff --git a/configure.ac b/configure.ac index 460b32b..8968546 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 ea43ce7..ac14a76 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -177,6 +177,13 @@ 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. */ + unsigned int inbound_message_count; /**< Number of inbound messages. */ + unsigned int outbound_message_count; /**< Number of outbound messages. */ +#endif char **allowed_mechs; /**< Mechanisms we're allowed to use, * or #NULL if we can use any @@ -389,9 +396,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 +1329,272 @@ 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) +#define NTLM_CLIENT_OUTBOUND_COUNT 2 +#define NTLM_SERVER_OUTBOUND_COUNT 1 + +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 retval = FALSE; + dbus_bool_t local_error = TRUE; + + auth->inbound_message_count += 1; + + if (auth->inbound_message_count > NTLM_CLIENT_OUTBOUND_COUNT) + goto ntlm_server_message_number_mismatch; + + _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 (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&error); + + return FALSE; + } + + if (local_error) + { + _dbus_verbose ("%s: Error getting next challenge: %s\n", + DBUS_AUTH_NAME (auth), error.message); + if (send_error (auth, "Failed to get next challenge due to internal error")) + retval = TRUE; + } + else + { + if (send_error (auth, error.message)) + retval = TRUE; + } + + dbus_error_free (&error); + + return retval; + } + + if (_dbus_string_get_length (&auth->challenge) > 0) + { + auth->outbound_message_count += 1; + + if (auth->outbound_message_count > NTLM_SERVER_OUTBOUND_COUNT) + goto ntlm_server_message_number_mismatch; + + if (send_data (auth, &auth->challenge)) + retval = TRUE; + } + + if (!done) + { + if (_dbus_string_get_length (&auth->challenge) == 0) + { + if (send_error (auth, "SSPI produced 0-length challenge, but authentication is not done yet")) + retval = TRUE; + + return retval; + } + + goto_state (auth, &server_state_waiting_for_data); + + return retval; + } + + 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 (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&error); + + return FALSE; + } + + if (local_error) + { + _dbus_verbose ("%s: Error fetching NTLM-negotiated credentials: %s\n", + DBUS_AUTH_NAME (auth), error.message); + if (send_error (auth, "Failed to fetch credentials from the token due to internal error")) + retval = TRUE; + } + else + { + if (send_error (auth, error.message)) + retval = TRUE; + } + + dbus_error_free (&error); + + return retval; + } + + if (!send_ok (auth)) + return FALSE; + + _dbus_verbose ("%s: authenticated client using DBUS_WINDOWS_SSPI_NTLM\n", + DBUS_AUTH_NAME (auth)); + + return TRUE; + +ntlm_server_message_number_mismatch: + dbus_set_error (&error, DBUS_ERROR_FAILED, + "Server expected to send/receive %u/%u SSPI buffers, but sent/received %u/%u instead", + NTLM_SERVER_OUTBOUND_COUNT, NTLM_CLIENT_OUTBOUND_COUNT, + auth->outbound_message_count, auth->inbound_message_count); + if (send_error (auth, error.message)) + retval = TRUE; + dbus_error_free (&error); + + return retval; +} + +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; + dbus_bool_t retval = FALSE; + + if (!_dbus_sspi_ntlm_initial_response (&auth->sspi_ntlm, response, &local_error, &error)) + { + _DBUS_ASSERT_ERROR_IS_SET (&error); + + if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&error); + + return FALSE; + } + + if (local_error) + { + _dbus_verbose ("%s: Error getting initial response: %s\n", + DBUS_AUTH_NAME (auth), error.message); + if (send_error (auth, "Failed to get initial challenge due to internal error")) + retval = TRUE; + } + else + { + if (send_error (auth, error.message)) + retval = TRUE; + } + + dbus_error_free (&error); + + return retval; + } + else + { + /* This is the first message we send, so no message count check here */ + auth->outbound_message_count += 1; + retval = TRUE; + } + + return retval; +} + +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_bool_t retval = FALSE; + + auth->inbound_message_count += 1; + + if (auth->inbound_message_count > NTLM_SERVER_OUTBOUND_COUNT) + goto ntlm_client_message_number_mismatch; + + _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 (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&error); + + return FALSE; + } + + if (local_error) + { + _dbus_verbose ("%s: Error getting response: %s\n", + DBUS_AUTH_NAME (auth), error.message); + if (send_error (auth, "Failed to get next response due to internal error")) + retval = TRUE; + } + else + { + if (send_error (auth, error.message)) + retval = TRUE; + } + + dbus_error_free (&error); + + return retval; + } + + if (_dbus_string_get_length (&auth->response) > 0) + { + auth->outbound_message_count += 1; + + if (auth->outbound_message_count > NTLM_CLIENT_OUTBOUND_COUNT) + goto ntlm_client_message_number_mismatch; + + if (send_data (auth, &auth->response)) + retval = TRUE; + } + else + { + goto_state (auth, &client_state_waiting_for_ok); + } + + return retval; + +ntlm_client_message_number_mismatch: + dbus_set_error (&error, DBUS_ERROR_FAILED, + "Client expected to send/receive %u/%u SSPI buffers, but sent/received %u/%u instead", + NTLM_CLIENT_OUTBOUND_COUNT, NTLM_SERVER_OUTBOUND_COUNT, + auth->outbound_message_count, auth->inbound_message_count); + if (send_error (auth, error.message)) + retval = TRUE; + dbus_error_free (&error); + + return retval; +} + +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 +1605,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 +2688,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 178aaa4..878bbd9 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -56,8 +56,10 @@ #include /* 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 #include @@ -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); } @@ -3576,6 +3610,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 * @@ -3693,5 +3874,411 @@ _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); + /* SSPI accepts non-const pointer, even though it doesn't write there */ + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_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); + /* SSPI accepts non-const pointer, even though it doesn't write there */ + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_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 0b5d8f0..2a667ad 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 #undef interface +#ifndef DBUS_WINCE +#include +#include +#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 */ -- 2.4.0