From 29c6d3e16a57ada9126b10053648099cb1460069 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 | 428 +++++++++++++++++++++++++++- dbus/dbus-sysdeps-win.c | 744 ++++++++++++++++++++++++++++++++++++++++++++++-- dbus/dbus-sysdeps-win.h | 94 +++++- 4 files changed, 1236 insertions(+), 32 deletions(-) diff --git a/configure.ac b/configure.ac index abc4ad1..4727dc9 100644 --- a/configure.ac +++ b/configure.ac @@ -1275,7 +1275,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..d1f0a4d 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -177,6 +177,16 @@ 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.c. */ + unsigned int inbound_message_count; /**< Number of inbound messages. */ + unsigned int outbound_message_count; /**< Number of outbound messages. */ + DBusSSPINTLMInternalState sspi_ntlm_state; /**< defined in sysdeps-win.h */ + DBusError sspi_ntlm_error; /**< error (might need to be sent repeatedly) */ + dbus_bool_t sspi_ntlm_done; /**< whether SSPI finished the auth or not */ +#endif char **allowed_mechs; /**< Mechanisms we're allowed to use, * or #NULL if we can use any @@ -389,9 +399,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 +1332,405 @@ 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) +{ + dbus_bool_t error_is_local = TRUE; + + switch (auth->sspi_ntlm_state) + { + case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR: + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + dbus_error_free (&auth->sspi_ntlm_error); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + + case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_CHALLENGE: + if (!send_data (auth, &auth->challenge)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_CHECK_IF_DONE; + + return TRUE; + + default: + break; + } + + switch (auth->sspi_ntlm_state) + { + default: + dbus_error_free (&auth->sspi_ntlm_error); + dbus_set_error_const (&auth->sspi_ntlm_error, DBUS_ERROR_FAILED, "Wrong SSPI NTLM state"); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + + case DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN: + 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); + auth->sspi_ntlm_done = FALSE; + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_GET_NEXT_CHALLENGE; + /* fallthrough */ + + case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_GET_NEXT_CHALLENGE: + if (!_dbus_sspi_ntlm_next_challenge (&auth->sspi_ntlm, + &auth->sspi_ntlm_state, + data, + &auth->challenge, + &auth->sspi_ntlm_done, + &error_is_local, + &auth->sspi_ntlm_error)) + { + _DBUS_ASSERT_ERROR_IS_SET (&auth->sspi_ntlm_error); + + if (dbus_error_has_name (&auth->sspi_ntlm_error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&auth->sspi_ntlm_error); + /* sspi_ntlm_state was modified by the next_challenge(), don't touch it */ + + return FALSE; + } + + if (error_is_local) + { + _dbus_verbose ("%s: Error getting next challenge: %s\n", + DBUS_AUTH_NAME (auth), auth->sspi_ntlm_error.message); + dbus_error_free (&auth->sspi_ntlm_error); + dbus_set_error_const (&auth->sspi_ntlm_error, DBUS_ERROR_FAILED, "Failed to get next challenge due to internal error"); + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_MAYBE_SEND_CHALLENGE; + /* fallthrough */ + + case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_MAYBE_SEND_CHALLENGE: + 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; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_CHALLENGE; + + if (!send_data (auth, &auth->challenge)) + return FALSE; + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_CHECK_IF_DONE; + /* fallthrough */ + + case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_CHECK_IF_DONE: + if (!auth->sspi_ntlm_done) + { + if (_dbus_string_get_length (&auth->challenge) == 0) + { + dbus_set_error_const (&auth->sspi_ntlm_error, DBUS_ERROR_FAILED, "SSPI produced 0-length challenge, but authentication is not done yet"); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + } + + goto_state (auth, &server_state_waiting_for_data); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_FETCH_CREDENTIALS; + /* fallthrough */ + + case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_FETCH_CREDENTIALS: + error_is_local = TRUE; + + if (!_dbus_sspi_ntlm_fetch_credentials (&auth->sspi_ntlm, &auth->sspi_ntlm_state, auth->authorized_identity, &error_is_local, &auth->sspi_ntlm_error)) + { + _DBUS_ASSERT_ERROR_IS_SET (&auth->sspi_ntlm_error); + + if (dbus_error_has_name (&auth->sspi_ntlm_error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&auth->sspi_ntlm_error); + /* sspi_ntlm_state was modified by the next_challenge(), don't touch it */ + + return FALSE; + } + + if (error_is_local) + { + _dbus_verbose ("%s: Error fetching NTLM-negotiated credentials: %s\n", + DBUS_AUTH_NAME (auth), auth->sspi_ntlm_error.message); + dbus_error_free (&auth->sspi_ntlm_error); + dbus_set_error_const (&auth->sspi_ntlm_error, DBUS_ERROR_FAILED, "Failed to fetch credentials from the token due to internal error"); + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_SEND_OK; + /* fallthrough */ + + case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_SEND_OK: + if (!send_ok (auth)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + _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 (&auth->sspi_ntlm_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); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (send_error (auth, auth->sspi_ntlm_error.message)) + { + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + } + + return FALSE; +} + +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) +{ + dbus_bool_t error_is_local = TRUE; + + if (_dbus_sspi_ntlm_initial_response (&auth->sspi_ntlm, + &auth->sspi_ntlm_state, + response, + &error_is_local, + &auth->sspi_ntlm_error)) + { + /* This is the first message we send, so no message count check here */ + auth->outbound_message_count += 1; + + return TRUE; + } + + _DBUS_ASSERT_ERROR_IS_SET (&auth->sspi_ntlm_error); + + if (dbus_error_has_name (&auth->sspi_ntlm_error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&auth->sspi_ntlm_error); + /* sspi_ntlm_state was modified by the initial_response(), don't touch it */ + + return FALSE; + } + + if (error_is_local) + { + _dbus_verbose ("%s: Error getting initial response: %s\n", + DBUS_AUTH_NAME (auth), auth->sspi_ntlm_error.message); + dbus_error_free (&auth->sspi_ntlm_error); + dbus_set_error_const (&auth->sspi_ntlm_error, DBUS_ERROR_FAILED, "Failed to get initial challenge due to internal error"); + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; +} + +static dbus_bool_t +handle_client_data_windows_sspi_ntlm_mech (DBusAuth *auth, + const DBusString *data) +{ + dbus_bool_t error_is_local = TRUE; + + switch (auth->sspi_ntlm_state) + { + case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR: + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + dbus_error_free (&auth->sspi_ntlm_error); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + + case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_RESPONSE: + if (!send_data (auth, &auth->response)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + + default: + break; + } + + switch (auth->sspi_ntlm_state) + { + default: + dbus_error_free (&auth->sspi_ntlm_error); + dbus_set_error_const (&auth->sspi_ntlm_error, DBUS_ERROR_FAILED, "Wrong SSPI NTLM state"); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + + case DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN: + 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); + + auth->sspi_ntlm_done = FALSE; + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_GET_NEXT_RESPONSE; + /* fallthrough */ + + case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_GET_NEXT_RESPONSE: + + if (!_dbus_sspi_ntlm_next_response (&auth->sspi_ntlm, &auth->sspi_ntlm_state, data, &auth->response, &error_is_local, &auth->sspi_ntlm_error)) + { + _DBUS_ASSERT_ERROR_IS_SET (&auth->sspi_ntlm_error); + + if (dbus_error_has_name (&auth->sspi_ntlm_error, DBUS_ERROR_NO_MEMORY)) + { + dbus_error_free (&auth->sspi_ntlm_error); + /* sspi_ntlm_state was modified by the next_response(), don't touch it */ + + return FALSE; + } + + if (error_is_local) + { + _dbus_verbose ("%s: Error getting response: %s\n", + DBUS_AUTH_NAME (auth), auth->sspi_ntlm_error.message); + dbus_error_free (&auth->sspi_ntlm_error); + dbus_set_error_const (&auth->sspi_ntlm_error, DBUS_ERROR_FAILED, "Failed to get next response due to internal error"); + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + return TRUE; + } + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_MAYBE_SEND_RESPONSE; + /* fallthrough */ + + case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_MAYBE_SEND_RESPONSE: + 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)) + return FALSE; + } + else + { + /* SSPI gave us nothing to send to the server, so assume + * we're done and wait for OK. + */ + goto_state (auth, &client_state_waiting_for_ok); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + } + + return TRUE; + } + +ntlm_client_message_number_mismatch: + dbus_set_error (&auth->sspi_ntlm_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); + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR; + + if (!send_error (auth, auth->sspi_ntlm_error.message)) + return FALSE; + + auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN; + + 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 +1741,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 +2824,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); @@ -2413,7 +2835,11 @@ _dbus_auth_unref (DBusAuth *auth) _dbus_credentials_unref (auth->credentials); _dbus_credentials_unref (auth->authorized_identity); _dbus_credentials_unref (auth->desired_identity); - + +#if defined (DBUS_WIN) && !defined (DBUS_WINCE) + dbus_error_free (&auth->sspi_ntlm_error); +#endif + dbus_free (auth); } } diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index ebf5bfe..de9c7f2 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 @@ -973,6 +975,63 @@ is_winxp_sp3_or_lower (void) dwlConditionMask); } +/** Gets SID from a token + * @param sid points to sid buffer, need to be freed with LocalFree() + * @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_get_token_sid (char **sid, + HANDLE token, + DBusError *error) +{ + TOKEN_USER *token_user = NULL; + DWORD n; + PSID psid; + int retval = FALSE; + + if (!GetTokenInformation (token, TokenUser, NULL, 0, &n) + && GetLastError () != ERROR_INSUFFICIENT_BUFFER) + { + _dbus_win_set_error_from_win_error (error, GetLastError ()); + return FALSE; + } + + token_user = alloca (n); + + if (token_user == NULL) + { + /* Ran out of stack space. Try to treat it as being out of heap space. */ + _DBUS_SET_OOM (error); + return FALSE; + } + + if (!GetTokenInformation (token, TokenUser, token_user, n, &n)) + { + _dbus_win_set_error_from_win_error (error, GetLastError ()); + return FALSE; + } + + psid = token_user->User.Sid; + + if (!IsValidSid (psid)) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Invalid token SID"); + return FALSE; + } + + if (!ConvertSidToStringSidA (psid, sid)) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Can't convert token SID to string"); + return FALSE; + } + + _dbus_verbose ("_dbus_get_token_sid() got '%s' and returns %d\n", *sid, retval); + + return TRUE; +} + /** 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) @@ -981,13 +1040,11 @@ is_winxp_sp3_or_lower (void) dbus_bool_t _dbus_getsid(char **sid, dbus_pid_t process_id) { + HANDLE process_handle; HANDLE process_token = INVALID_HANDLE_VALUE; - TOKEN_USER *token_user = NULL; - DWORD n; - PSID psid; + DBusError error; int retval = FALSE; - HANDLE process_handle; if (process_id == 0) process_handle = GetCurrentProcess(); else if (is_winxp_sp3_or_lower()) @@ -998,36 +1055,22 @@ _dbus_getsid(char **sid, dbus_pid_t process_id) if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token)) { _dbus_win_warn_win_error ("OpenProcessToken failed", GetLastError ()); - goto failed; + goto end; } - if ((!GetTokenInformation (process_token, TokenUser, NULL, 0, &n) - && GetLastError () != ERROR_INSUFFICIENT_BUFFER) - || (token_user = alloca (n)) == NULL - || !GetTokenInformation (process_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_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 @@ -2521,7 +2564,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); } @@ -3580,6 +3623,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 * @@ -3721,5 +3911,501 @@ _dbus_logv (DBusSystemLogSeverity severity, exit (1); } +/* + * DBUS_WINDOWS_SSPI_NTLM mechanism + */ +#ifndef DBUS_WINCE + +/* This is needed because SSPI function prototypes claim that they take + * non-const provider name, even though there's no reason to modify it. + */ +wchar_t ntlm_ssp_name[] = L"NTLM"; + +/** + * Feeds client response to SSPI, producing the next challenge. + * + * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth + * @param state - SSPI NTLM state, for recovering from OOM errors + * @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_is_local - boolean to set when @error is not to be shared + * @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, + DBusSSPINTLMInternalState *state, + 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 (*state != DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_GET_NEXT_CHALLENGE) + { + *error_is_local = TRUE; + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Wrong SSPI NTLM state for getting next challenge"); + + return FALSE; + } + + 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 (ntlm_ssp_name, &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, ntlm_ssp_name, 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; + } + + if (!_dbus_string_set_length (challenge, sspi_ntlm->message_limit)) + return FALSE; + + 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; + } + } + + /* Only shortens the string */ + _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 state - SSPI NTLM state, for recovering from OOM errors + * @param credentials - credentials object to modify + * @param error_is_local - boolean to set when @error is not to be shared + * @param error - an error to send back (optional) + * + * @returns #TRUE if everything is OK and @credentials were modified, #FALSE otherwise + */ +dbus_bool_t +_dbus_sspi_ntlm_fetch_credentials (DBusSSPINTLMInfo *sspi_ntlm, + DBusSSPINTLMInternalState *state, + DBusCredentials *credentials, + dbus_bool_t *error_is_local, + DBusError *error) +{ + SECURITY_STATUS ss; + HANDLE client_token; + char *sid; + dbus_bool_t result; + + if (*state != DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_FETCH_CREDENTIALS) + { + *error_is_local = TRUE; + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Wrong SSPI NTLM state for fetching credentials"); + + return FALSE; + } + + /* We might lose a bit of performance by re-querying and re-getting the token on every + * OOM-induced retry, but there's no state to worry about. + */ + + 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 state - SSPI NTLM state, for recovering from OOM errors + * @param initial_response - outbound data to append the response to + * @param error_is_local - boolean to set when @error is not to be shared + * @param error - an error to send back (optional) + * + * @returns #TRUE if everything is OK and @initial_response was modified, #FALSE otherwise + */ +dbus_bool_t +_dbus_sspi_ntlm_initial_response (DBusSSPINTLMInfo *sspi_ntlm, + DBusSSPINTLMInternalState *state, + 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; + + if (*state != DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN) + { + *error_is_local = TRUE; + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Wrong SSPI NTLM state for getting initial challenge"); + + return FALSE; + } + + if (sspi_ntlm->message_limit == 0) + { + ss = QuerySecurityPackageInfoW (ntlm_ssp_name, &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); + } + + if (!sspi_ntlm->free_credentials) + { + ss = AcquireCredentialsHandleW (NULL, ntlm_ssp_name, 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; + } + + if (!_dbus_string_init_preallocated (&plaintext, sspi_ntlm->message_limit)) + { + *error_is_local = TRUE; + _DBUS_SET_OOM (error); + + return FALSE; + } + + _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); + + /* We might lose some performance by re-initializing and the context on every + * OOM-induced retry, but if we clean it up on OOM, this function becomes stateless, + * which is nice. + */ + + 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); + DeleteSecurityContext (&sspi_ntlm->context); + sspi_ntlm->free_context = FALSE; + + return FALSE; + } + } + + /* Only shortens the string */ + _dbus_string_set_length (&plaintext, output_sec_buffer.cbBuffer); + + result = _dbus_string_hex_encode (&plaintext, 0, initial_response, + _dbus_string_get_length (initial_response)); + + if (!result) + { + DeleteSecurityContext (&sspi_ntlm->context); + sspi_ntlm->free_context = FALSE; + *error_is_local = TRUE; + _DBUS_SET_OOM (error); + } + + _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 state - SSPI NTLM state, for recovering from OOM errors + * @param data - inbound server challenge + * @param response - string to fill with the next response + * @param error_is_local - boolean to set when @error is not to be shared + * @param error - an error to send back (optional) + * + * @returns #TRUE if everything is OK and @response are valid, #FALSE otherwise + */ +dbus_bool_t +_dbus_sspi_ntlm_next_response (DBusSSPINTLMInfo *sspi_ntlm, + DBusSSPINTLMInternalState *state, + 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 (*state != DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_GET_NEXT_RESPONSE) + { + *error_is_local = TRUE; + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Wrong SSPI NTLM state for getting next response"); + + return FALSE; + } + + 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 (!_dbus_string_set_length (response, sspi_ntlm->message_limit)) + return FALSE; + + 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 b8861a1..3007b8f 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/" @@ -89,7 +96,92 @@ 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 +}; + +/** + * Representation of the internal state of SSPI NTLM implementation. + * Because we have no control over the internal state of NTLM SSP, + * we can't just return FALSE in case of out-of-memory errors and + * then expect to re-run the function later. We need to remember + * which state the SSP is in, to avoid calling SSPI functions + * multiple times with the same arguments, which will lead to an error. + */ +typedef enum +{ + DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN, + DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR, + DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_CHALLENGE, + DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_CHECK_IF_DONE, + DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_GET_NEXT_CHALLENGE, + DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_MAYBE_SEND_CHALLENGE, + DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_FETCH_CREDENTIALS, + DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_SEND_OK, + DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_RESPONSE, + DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_GET_NEXT_RESPONSE, + DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_MAYBE_SEND_RESPONSE, + DBUS_SSPI_NTLM_INTERNAL_STATE_INVALID = -1 +} DBusSSPINTLMInternalState; + + +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, + DBusSSPINTLMInternalState *state, + 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, + DBusSSPINTLMInternalState *state, + DBusCredentials *credentials, + dbus_bool_t *error_is_local, + DBusError *error); + +dbus_bool_t _dbus_sspi_ntlm_initial_response (DBusSSPINTLMInfo *sspi_ntlm, + DBusSSPINTLMInternalState *state, + DBusString *initial_response, + dbus_bool_t *error_is_local, + DBusError *error); + +dbus_bool_t _dbus_sspi_ntlm_next_response (DBusSSPINTLMInfo *sspi_ntlm, + DBusSSPINTLMInternalState *state, + 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