mirror of
https://gitlab.freedesktop.org/dbus/dbus.git
synced 2026-02-11 21:10:39 +01:00
Merge branch 'sspi-ntlm-auth' into 'main'
Draft: Support SSPI NTLM authentication mechanism Closes #150 See merge request dbus/dbus!548
This commit is contained in:
commit
9c09315e11
15 changed files with 2199 additions and 34 deletions
|
|
@ -306,7 +306,6 @@ else()
|
|||
strict-aliasing
|
||||
strict-prototypes
|
||||
switch-default
|
||||
switch-enum
|
||||
undef
|
||||
unused-but-set-variable
|
||||
write-strings
|
||||
|
|
@ -325,6 +324,7 @@ else()
|
|||
unused-parameter
|
||||
)
|
||||
set(WARNINGS_ERRORS
|
||||
switch-enum
|
||||
)
|
||||
if(ENABLE_WERROR)
|
||||
list(APPEND WARNINGS error)
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ if(WIN32)
|
|||
if(WINCE)
|
||||
target_link_libraries(dbus-1 ws2)
|
||||
else(WINCE)
|
||||
target_link_libraries(dbus-1 ws2_32 advapi32 netapi32 iphlpapi dbghelp)
|
||||
target_link_libraries(dbus-1 ws2_32 advapi32 netapi32 iphlpapi dbghelp secur32)
|
||||
endif()
|
||||
else(WIN32)
|
||||
if(DEFINED DBUS_LIBRARY_REVISION)
|
||||
|
|
@ -325,7 +325,7 @@ if(WIN32)
|
|||
if(WINCE)
|
||||
target_link_libraries(dbus-internal ws2)
|
||||
else(WINCE)
|
||||
target_link_libraries(dbus-internal ws2_32 advapi32 netapi32 iphlpapi)
|
||||
target_link_libraries(dbus-internal ws2_32 advapi32 netapi32 iphlpapi secur32)
|
||||
endif()
|
||||
else(WIN32)
|
||||
target_link_libraries(dbus-internal ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
|
|
|||
468
dbus/dbus-auth.c
468
dbus/dbus-auth.c
|
|
@ -179,6 +179,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
|
||||
|
|
@ -391,9 +401,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);
|
||||
|
|
@ -1382,9 +1397,445 @@ 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_REJECTED:
|
||||
if (!send_rejected (auth))
|
||||
return FALSE;
|
||||
|
||||
auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN;
|
||||
|
||||
return TRUE;
|
||||
|
||||
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;
|
||||
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_GET_NEXT_RESPONSE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_MAYBE_SEND_RESPONSE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_INVALID:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_CHECK_IF_DONE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_GET_NEXT_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_MAYBE_SEND_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_FETCH_CREDENTIALS:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_SEND_OK:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_RESPONSE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (auth->sspi_ntlm_state)
|
||||
{
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN:
|
||||
auth->outbound_message_count = 0;
|
||||
auth->inbound_message_count = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
_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);
|
||||
|
||||
auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_REJECTED;
|
||||
|
||||
if (!send_rejected (auth))
|
||||
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_verbose ("%s: SSPI produced 0-length challenge, but authentication is not done yet\n",
|
||||
DBUS_AUTH_NAME (auth));
|
||||
|
||||
auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_REJECTED;
|
||||
|
||||
if (!send_rejected (auth))
|
||||
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);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_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);
|
||||
|
||||
auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_REJECTED;
|
||||
|
||||
if (!send_rejected (auth))
|
||||
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;
|
||||
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_GET_NEXT_RESPONSE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_MAYBE_SEND_RESPONSE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_INVALID:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_REJECTED:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_RESPONSE:
|
||||
default:
|
||||
_dbus_verbose ("%s: Wrong SSPI NTLM state\n",
|
||||
DBUS_AUTH_NAME (auth));
|
||||
|
||||
auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_REJECTED;
|
||||
|
||||
if (!send_rejected (auth))
|
||||
return FALSE;
|
||||
|
||||
auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ntlm_server_message_number_mismatch:
|
||||
_dbus_verbose ("%s: Server expected to send/receive %u/%u SSPI buffers, but sent/received %u/%u instead",
|
||||
DBUS_AUTH_NAME (auth),
|
||||
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_REJECTED;
|
||||
|
||||
if (send_rejected (auth))
|
||||
{
|
||||
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)
|
||||
{
|
||||
auth->outbound_message_count = 0;
|
||||
auth->inbound_message_count = 0;
|
||||
|
||||
if (_dbus_sspi_ntlm_initial_response (&auth->sspi_ntlm,
|
||||
&auth->sspi_ntlm_state,
|
||||
response,
|
||||
&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_verbose ("%s: Error getting initial response: %s\n",
|
||||
DBUS_AUTH_NAME (auth), auth->sspi_ntlm_error.message);
|
||||
|
||||
dbus_error_free (&auth->sspi_ntlm_error);
|
||||
/* initial_response is stateless, so this is always OK */
|
||||
auth->sspi_ntlm_state = DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN;
|
||||
|
||||
/* This is not an OOM error, but we can't return TRUE */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLEAN:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_GET_NEXT_RESPONSE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_CLIENT_DATA_MAYBE_SEND_RESPONSE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_INVALID:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_REJECTED:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_CHECK_IF_DONE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_GET_NEXT_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_MAYBE_SEND_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_FETCH_CREDENTIALS:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_SEND_OK:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (auth->sspi_ntlm_state)
|
||||
{
|
||||
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;
|
||||
|
||||
dbus_error_free (&auth->sspi_ntlm_error);
|
||||
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;
|
||||
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_ERROR:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_REJECTED:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_CHECK_IF_DONE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_GET_NEXT_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_MAYBE_SEND_CHALLENGE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_FETCH_CREDENTIALS:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SERVER_DATA_SEND_OK:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_SEND_RESPONSE:
|
||||
case DBUS_SSPI_NTLM_INTERNAL_STATE_INVALID:
|
||||
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;
|
||||
|
||||
dbus_error_free (&auth->sspi_ntlm_error);
|
||||
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;
|
||||
|
||||
dbus_error_free (&auth->sspi_ntlm_error);
|
||||
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
|
||||
|
|
@ -1395,6 +1846,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,
|
||||
|
|
@ -2486,6 +2947,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);
|
||||
|
|
@ -2496,7 +2958,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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,8 +54,10 @@
|
|||
#endif
|
||||
|
||||
/* 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>
|
||||
|
|
@ -981,6 +983,64 @@ 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)
|
||||
|
|
@ -989,13 +1049,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())
|
||||
|
|
@ -1006,31 +1064,15 @@ _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("invalid sid\n");
|
||||
goto failed;
|
||||
}
|
||||
if (!ConvertSidToStringSidA (psid, sid))
|
||||
{
|
||||
_dbus_verbose("invalid sid\n");
|
||||
goto failed;
|
||||
}
|
||||
//okay:
|
||||
retval = TRUE;
|
||||
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);
|
||||
|
||||
failed:
|
||||
end:
|
||||
CloseHandle (process_handle);
|
||||
if (process_token != INVALID_HANDLE_VALUE)
|
||||
CloseHandle (process_token);
|
||||
|
|
@ -3952,6 +3994,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
|
||||
*
|
||||
|
|
@ -4517,5 +4706,492 @@ _dbus_listen_unix_socket (const char *path,
|
|||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 - 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,
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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))
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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;
|
||||
_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 */
|
||||
|
|
|
|||
|
|
@ -28,6 +28,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
|
||||
|
||||
|
|
@ -39,6 +41,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/"
|
||||
|
||||
|
||||
|
|
@ -88,7 +95,93 @@ DBUS_PRIVATE_EXPORT
|
|||
dbus_bool_t _dbus_get_install_root (DBusString *str);
|
||||
|
||||
DBUS_PRIVATE_EXPORT
|
||||
dbus_bool_t _dbus_getsid(char **sid, dbus_pid_t process_id);
|
||||
dbus_bool_t _dbus_getsid (char **sid,
|
||||
dbus_pid_t process_id);
|
||||
|
||||
DBUS_PRIVATE_EXPORT
|
||||
dbus_bool_t _dbus_get_token_sid (char **sid,
|
||||
HANDLE token,
|
||||
DBusError *error);
|
||||
|
||||
/** 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_REJECTED,
|
||||
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,
|
||||
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);
|
||||
|
||||
|
||||
HANDLE _dbus_spawn_program (const char *name,
|
||||
char **argv,
|
||||
|
|
|
|||
|
|
@ -661,6 +661,7 @@ elif platform_windows
|
|||
cc.find_library('ws2_32'),
|
||||
cc.find_library('iphlpapi'),
|
||||
cc.find_library('dbghelp'),
|
||||
cc.find_library('secur32'),
|
||||
]
|
||||
else
|
||||
network_libs = []
|
||||
|
|
|
|||
|
|
@ -218,6 +218,13 @@ if(DBUS_WITH_GLIB)
|
|||
add_test_executable(test-userdb internals/userdb.c ${TEST_LIBRARIES})
|
||||
add_helper_executable(manual-authz manual-authz.c ${TEST_LIBRARIES})
|
||||
add_helper_executable(manual-test-thread-blocking thread-blocking.c ${TEST_LIBRARIES})
|
||||
if(WIN32)
|
||||
set(auth_windows_sspi_corrupt_SOURCES
|
||||
test-auth-windows-sspi-corrupt.c
|
||||
test-utils-auth.c
|
||||
)
|
||||
add_test_executable(test-auth-windows-sspi-corrupt "${auth_windows_sspi_corrupt_SOURCES}" ${TEST_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
### keep these in creation order, i.e. uppermost dirs first
|
||||
|
|
|
|||
15
test/data/valid-config-files/debug-auth-sspi.conf.in
Normal file
15
test/data/valid-config-files/debug-auth-sspi.conf.in
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!-- Bus that listens on a debug pipe and requires SSPI auth, used to test SSPI -->
|
||||
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<listen>@TEST_LISTEN@</listen>
|
||||
<servicedir>@DBUS_TEST_DATA@/valid-service-files</servicedir>
|
||||
<auth>DBUS_WINDOWS_SSPI_NTLM</auth>
|
||||
<policy context="default">
|
||||
<allow send_interface="*"/>
|
||||
<allow receive_interface="*"/>
|
||||
<allow own="*"/>
|
||||
<allow user="*"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
|
|
@ -440,7 +440,17 @@ if platform_windows
|
|||
'srcs': [ 'manual-paths.c' ],
|
||||
'link': [ libdbus_testutils, ],
|
||||
'test': false,
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'test-auth-windows-sspi-corrupt',
|
||||
'srcs': [
|
||||
'test-auth-windows-sspi-corrupt.c',
|
||||
'test-utils-auth.c'
|
||||
],
|
||||
'link': [ libdbus_testutils, ],
|
||||
'deps': [ glib, gio, ],
|
||||
'test': true,
|
||||
},
|
||||
]
|
||||
endif
|
||||
|
||||
|
|
|
|||
704
test/test-auth-windows-sspi-corrupt.c
Executable file
704
test/test-auth-windows-sspi-corrupt.c
Executable file
|
|
@ -0,0 +1,704 @@
|
|||
#include <config.h>
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "test-utils-glib.h"
|
||||
#include "test-utils-auth.h"
|
||||
|
||||
typedef struct {
|
||||
gboolean skip;
|
||||
DBusError e;
|
||||
TestMainContext *ctx;
|
||||
|
||||
GPid daemon_pid;
|
||||
DBusServer *server;
|
||||
|
||||
gchar *address;
|
||||
|
||||
DBusSocket sock;
|
||||
|
||||
size_t message_limit;
|
||||
CredHandle credentials;
|
||||
DBusString plaintext;
|
||||
CtxtHandle context;
|
||||
int free_context;
|
||||
|
||||
/* The moment when we start to screw with the data being sent.
|
||||
* phase 0 - don't do anything, just a successful auth
|
||||
* phase 1 - corrupt the initial client->server AUTH message
|
||||
* phase 2 - corrupt the first server->client DATA message
|
||||
* phase 3 - corrupt the first client->server DATA message
|
||||
* There's no phase 4 (server responds with OK, but SSPI is not playing any part in that)
|
||||
*/
|
||||
int phase;
|
||||
/* offset of the byte to corrupt. Increases by 1 with each iteration.
|
||||
* Once it goes past the end of the data, advance to the next phase.
|
||||
*/
|
||||
unsigned int corrupt_offset;
|
||||
|
||||
DBusString outgoing;
|
||||
int free_outgoing;
|
||||
DBusString incoming;
|
||||
int free_incoming;
|
||||
DBusString decoded;
|
||||
int free_decoded;
|
||||
} Fixture;
|
||||
|
||||
typedef struct {
|
||||
const char *bug_ref;
|
||||
guint min_messages;
|
||||
const char *config_file;
|
||||
enum { SPECIFY_ADDRESS = 0, RELY_ON_DEFAULT } connect_mode;
|
||||
} Config;
|
||||
|
||||
static void
|
||||
setup (Fixture *f,
|
||||
gconstpointer context)
|
||||
{
|
||||
const Config *config = context;
|
||||
SECURITY_STATUS ss;
|
||||
PSecPkgInfoW sec_package_info;
|
||||
TimeStamp lifetime;
|
||||
wchar_t ntlm_ssp_name[] = L"NTLM";
|
||||
|
||||
ss = QuerySecurityPackageInfoW (ntlm_ssp_name, &sec_package_info);
|
||||
g_assert (ss == SEC_E_OK);
|
||||
f->message_limit = sec_package_info->cbMaxToken;
|
||||
ss = FreeContextBuffer (sec_package_info);
|
||||
g_assert (ss == SEC_E_OK);
|
||||
ss = AcquireCredentialsHandleW (NULL, ntlm_ssp_name, SECPKG_CRED_OUTBOUND, 0, 0, 0, 0,
|
||||
&f->credentials, &lifetime);
|
||||
g_assert (ss == SEC_E_OK);
|
||||
g_assert (_dbus_string_init (&f->plaintext));
|
||||
_dbus_string_set_length (&f->plaintext, f->message_limit);
|
||||
|
||||
f->ctx = test_main_context_get ();
|
||||
dbus_error_init (&f->e);
|
||||
|
||||
f->address = test_get_dbus_daemon (config ? config->config_file : NULL,
|
||||
TEST_USER_ME, NULL,
|
||||
&f->daemon_pid);
|
||||
|
||||
if (f->address == NULL)
|
||||
{
|
||||
f->skip = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
f->sock.sock = INVALID_SOCKET;
|
||||
|
||||
f->phase = 0;
|
||||
f->corrupt_offset = 0;
|
||||
|
||||
f->free_context = 0;
|
||||
f->free_outgoing = 0;
|
||||
f->free_incoming = 0;
|
||||
f->free_decoded = 0;
|
||||
}
|
||||
|
||||
/* Returns TRUE to mean "keep calling me, i still have other ways
|
||||
* to corrupt the data".
|
||||
* Returns FALSE to mean "i'm done, don't call me again".
|
||||
*/
|
||||
static dbus_bool_t
|
||||
do_corrupt_sspi_auth (Fixture *f,
|
||||
gconstpointer context)
|
||||
{
|
||||
SECURITY_STATUS ss;
|
||||
TimeStamp lifetime;
|
||||
SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL};
|
||||
SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer };
|
||||
ULONG attributes;
|
||||
volatile int bytes_written;
|
||||
volatile int bytes_read;
|
||||
char *buffer;
|
||||
SecBuffer input_sec_buffer = { 0, SECBUFFER_TOKEN, NULL};
|
||||
SecBufferDesc input_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &input_sec_buffer };
|
||||
int end;
|
||||
int to_read, total_read;
|
||||
#define PHASES 4
|
||||
char expect_error_in_phase[PHASES];
|
||||
unsigned long bytes_available;
|
||||
char zero;
|
||||
int old_offset, old_phase;
|
||||
|
||||
old_offset = f->corrupt_offset;
|
||||
old_phase = f->phase;
|
||||
|
||||
if (_dbus_socket_is_valid (f->sock))
|
||||
{
|
||||
/* Ensure that there's no leftover incoming data in the socket */
|
||||
do
|
||||
{
|
||||
#define HUNDRED 100
|
||||
char buf[HUNDRED];
|
||||
bytes_available = 0;
|
||||
ioctlsocket (f->sock.sock, FIONREAD, &bytes_available);
|
||||
bytes_read = 0;
|
||||
if (bytes_available > 0)
|
||||
bytes_read = recv (f->sock.sock, buf, bytes_available > HUNDRED ? HUNDRED : bytes_available, 0);
|
||||
#undef HUNDRED
|
||||
} while (bytes_read > 0);
|
||||
}
|
||||
|
||||
if (!_dbus_socket_is_valid (f->sock))
|
||||
{
|
||||
f->sock = test_connect_to_bus_simple (f->ctx, f->address);
|
||||
g_assert (_dbus_socket_is_valid (f->sock));
|
||||
zero = 0;
|
||||
g_assert (send (f->sock.sock, &zero, 1, 0) == 1);
|
||||
}
|
||||
|
||||
#define HANDLE_BAD_RECV \
|
||||
if (bytes_read < 0) \
|
||||
{ \
|
||||
g_print ("disconnected unexpectedly\n"); \
|
||||
f->corrupt_offset = old_offset; \
|
||||
f->phase = old_phase; \
|
||||
_dbus_close_socket (&f->sock, NULL); \
|
||||
_dbus_socket_invalidate (&f->sock); \
|
||||
\
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
#define HANDLE_BAD_SEND \
|
||||
if (bytes_written < 0) \
|
||||
{ \
|
||||
g_print ("disconnected unexpectedly\n"); \
|
||||
f->corrupt_offset = old_offset; \
|
||||
f->phase = old_phase; \
|
||||
_dbus_close_socket (&f->sock, NULL); \
|
||||
_dbus_socket_invalidate (&f->sock); \
|
||||
\
|
||||
return TRUE; \
|
||||
}
|
||||
|
||||
g_assert (_dbus_string_init (&f->outgoing));
|
||||
f->free_outgoing = 1;
|
||||
g_assert (_dbus_string_append (&f->outgoing, "AUTH DBUS_WINDOWS_SSPI_NTLM "));
|
||||
|
||||
output_sec_buffer.cbBuffer = _dbus_string_get_length (&f->plaintext);
|
||||
output_sec_buffer.pvBuffer = _dbus_string_get_data (&f->plaintext);
|
||||
|
||||
g_print ("\nInit security context\n");
|
||||
ss = InitializeSecurityContextW (&f->credentials,
|
||||
NULL,
|
||||
NULL,
|
||||
ISC_REQ_IDENTIFY,
|
||||
0,
|
||||
SECURITY_NATIVE_DREP,
|
||||
NULL,
|
||||
0,
|
||||
&f->context,
|
||||
&output_sec_buffers_descriptor,
|
||||
&attributes,
|
||||
&lifetime);
|
||||
|
||||
f->free_context = 1;
|
||||
|
||||
g_assert (ss == SEC_E_OK ||
|
||||
ss == SEC_I_COMPLETE_AND_CONTINUE ||
|
||||
ss == SEC_I_COMPLETE_NEEDED ||
|
||||
ss == SEC_I_CONTINUE_NEEDED);
|
||||
|
||||
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))
|
||||
{
|
||||
ss = CompleteAuthToken (&f->context,
|
||||
&output_sec_buffers_descriptor);
|
||||
g_assert (ss == SEC_E_OK);
|
||||
}
|
||||
|
||||
_dbus_string_set_length (&f->plaintext, output_sec_buffer.cbBuffer);
|
||||
|
||||
memset (expect_error_in_phase, 0, PHASES);
|
||||
|
||||
g_print ("phase 1\n");
|
||||
if (f->phase == 1)
|
||||
{
|
||||
if (f->corrupt_offset < output_sec_buffer.cbBuffer && f->corrupt_offset < 32)
|
||||
{
|
||||
BYTE x = ((BYTE *) output_sec_buffer.pvBuffer)[f->corrupt_offset];
|
||||
x = (x == 255 ? 0 : x + 1);
|
||||
((BYTE *) output_sec_buffer.pvBuffer)[f->corrupt_offset] = x;
|
||||
|
||||
switch (f->corrupt_offset)
|
||||
{
|
||||
/* 4 bytes (12, 13, 14, 15) are flags, changing some of them makes no difference */
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
/* 2 bytes (16, 17) is domain SB lengh */
|
||||
/* 2 bytes (18, 19) is domain SB allocated space, changing it makes no difference */
|
||||
case 18:
|
||||
case 19:
|
||||
g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase);
|
||||
break;
|
||||
/* 2 bytes (24, 25) is workstation SB length, changing it causes an error later on */
|
||||
case 24:
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[3] = TRUE;
|
||||
break;
|
||||
/* 2 bytes (26, 27) is workstation SB allocated space, changing it makes no difference */
|
||||
case 26:
|
||||
case 27:
|
||||
g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase);
|
||||
break;
|
||||
/* 4 bytes (28, 29, 30, 31) is workstation SB offset, changing it causes an error later on */
|
||||
case 28:
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[3] = TRUE;
|
||||
break;
|
||||
/* 1 byte (32) is OS major version, changing it makes no difference */
|
||||
case 32:
|
||||
/* 1 byte (33) is OS minor version, changing it makes no difference */
|
||||
case 33:
|
||||
/* 2 byte (34, 35) is OS build number, changing it makes no difference */
|
||||
case 34:
|
||||
case 35:
|
||||
/* 4 bytes (36, 37, 38, 39) are unknown/reserved, changing them makes no difference */
|
||||
case 36:
|
||||
case 37:
|
||||
case 38:
|
||||
case 39:
|
||||
g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase);
|
||||
break;
|
||||
default:
|
||||
if (f->corrupt_offset >= 40)
|
||||
{
|
||||
/* Data section starts at byte 40, its length depends on the argument we used, and changing it causes an error later on */
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[3] = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 1\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[1] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
f->corrupt_offset += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->phase += 1;
|
||||
f->corrupt_offset = 0;
|
||||
g_print ("will begin corrupting phase %d\n", f->phase);
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (_dbus_string_hex_encode (&f->plaintext, 0, &f->outgoing, _dbus_string_get_length (&f->outgoing)));
|
||||
g_assert (_dbus_string_append (&f->outgoing, "\r\n"));
|
||||
|
||||
_dbus_verbose ("sending `%.*s`\n", _dbus_string_get_length (&f->outgoing), _dbus_string_get_data (&f->outgoing));
|
||||
bytes_written = send (f->sock.sock,
|
||||
_dbus_string_get_data (&f->outgoing), _dbus_string_get_length (&f->outgoing),
|
||||
0);
|
||||
HANDLE_BAD_SEND
|
||||
g_assert (bytes_written == _dbus_string_get_length (&f->outgoing));
|
||||
|
||||
#define DATASTR "DATA "
|
||||
#define DATASTRLEN strlen (DATASTR)
|
||||
#define REJSTR "REJECTED"
|
||||
#define REJSTRLEN strlen (REJSTR)
|
||||
g_assert (_dbus_string_init (&f->incoming));
|
||||
f->free_incoming = 1;
|
||||
|
||||
g_print ("receiving in phase 1\n");
|
||||
g_assert (_dbus_string_set_length (&f->incoming, 100));
|
||||
buffer = _dbus_string_get_data (&f->incoming);
|
||||
bytes_read = recv (f->sock.sock, buffer, 100, MSG_PEEK);
|
||||
HANDLE_BAD_RECV
|
||||
|
||||
g_print ("received %.*s\n", bytes_read, buffer);
|
||||
|
||||
if (f->phase <= 1 && (expect_error_in_phase[1] != FALSE))
|
||||
{
|
||||
g_assert (bytes_read >= (int) REJSTRLEN);
|
||||
g_assert (strncmp (buffer, REJSTR, REJSTRLEN) == 0);
|
||||
|
||||
g_print ("auth failed in phase 1\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_assert (bytes_read >= (int) DATASTRLEN);
|
||||
g_assert (strncmp (buffer, DATASTR, DATASTRLEN) == 0);
|
||||
|
||||
_dbus_string_free (&f->outgoing);
|
||||
f->free_outgoing = 0;
|
||||
|
||||
to_read = 0;
|
||||
total_read = 0;
|
||||
do
|
||||
{
|
||||
int i, should_break;
|
||||
for (i = 0, should_break = FALSE; i < total_read - 1; i++)
|
||||
{
|
||||
if (buffer[i] == '\r' && buffer[i + 1] == '\n')
|
||||
{
|
||||
should_break = TRUE;
|
||||
total_read = i + 2;
|
||||
}
|
||||
}
|
||||
if (should_break)
|
||||
break;
|
||||
to_read += 100;
|
||||
g_assert (_dbus_string_set_length (&f->incoming, to_read));
|
||||
buffer = _dbus_string_get_data (&f->incoming);
|
||||
bytes_read = recv (f->sock.sock, &buffer[total_read], 100, 0);
|
||||
HANDLE_BAD_RECV
|
||||
g_assert (bytes_read > 0);
|
||||
total_read += bytes_read;
|
||||
} while (TRUE);
|
||||
g_assert (buffer[total_read - 2] == '\r' &&
|
||||
buffer[total_read - 1] == '\n');
|
||||
buffer[total_read - 1] = buffer[total_read - 2] = '\0';
|
||||
|
||||
g_assert (_dbus_string_init (&f->decoded));
|
||||
f->free_decoded = 1;
|
||||
g_assert (_dbus_string_hex_decode (&f->incoming, DATASTRLEN, &end, &f->decoded, 0));
|
||||
g_assert (end == total_read - 2);
|
||||
_dbus_string_free (&f->incoming);
|
||||
f->free_incoming = 0;
|
||||
|
||||
input_sec_buffer.cbBuffer = _dbus_string_get_length (&f->decoded);
|
||||
input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_data (&f->decoded);
|
||||
|
||||
g_assert (input_sec_buffer.cbBuffer > 0);
|
||||
|
||||
g_print ("phase 2\n");
|
||||
if (f->phase == 2)
|
||||
{
|
||||
/* 2 bytes (40, 41) is target info SB length, changing it makes no difference */
|
||||
/* 2 bytes (42, 43) is target info SB allocated space, changing it makes no difference */
|
||||
/* 4 bytes (44, 45, 46, 47) is target info SB offset, changing it makes no difference */
|
||||
/* Data section starts at byte 48, its length depends on the argument we used, and changing it makes no difference
|
||||
* because of local authentication.
|
||||
*/
|
||||
if (f->corrupt_offset < input_sec_buffer.cbBuffer && f->corrupt_offset < 40)
|
||||
{
|
||||
BYTE x = ((BYTE *) input_sec_buffer.pvBuffer)[f->corrupt_offset];
|
||||
x = (x == 255 ? 0 : x + 1);
|
||||
((BYTE *) input_sec_buffer.pvBuffer)[f->corrupt_offset] = x;
|
||||
|
||||
switch (f->corrupt_offset)
|
||||
{
|
||||
/* 8 bytes (32, 33, 34, 35, 36, 37, 38, 39) is the context, changing it causes an error later on */
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
case 36:
|
||||
case 37:
|
||||
case 38:
|
||||
case 39:
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[3] = TRUE;
|
||||
break;
|
||||
default:
|
||||
if (f->corrupt_offset >= 12 && f->corrupt_offset < 32)
|
||||
{
|
||||
/* 2 bytes (12, 13) is target name SB length, changing it makes no difference */
|
||||
/* 2 bytes (14, 15) is target name SB allocated space, changing it makes no difference */
|
||||
/* 4 bytes (16, 17, 18, 19) is target name SB offset, changing it makes no difference */
|
||||
/* 4 bytes (20, 21, 22, 23) are flags, changing some of them makes no difference */
|
||||
/* 8 bytes (24, 25, 26, 27, 28, 29, 30, 31) is the challenge, changing it makes no difference (yes! Because of local authentication) */
|
||||
g_print ("corrupted byte %d in phase %d, auth will succeed anyway. Skipping to offset 31\n", f->corrupt_offset, f->phase);
|
||||
f->corrupt_offset = 31;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 2\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[2] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
f->corrupt_offset += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->phase += 1;
|
||||
f->corrupt_offset = 0;
|
||||
g_print ("win begin corrupting phase %d", f->phase);
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (_dbus_string_init (&f->outgoing));
|
||||
f->free_outgoing = 1;
|
||||
g_assert (_dbus_string_append (&f->outgoing, "DATA "));
|
||||
|
||||
_dbus_string_set_length (&f->plaintext, f->message_limit);
|
||||
output_sec_buffer.cbBuffer = _dbus_string_get_length (&f->plaintext);
|
||||
output_sec_buffer.pvBuffer = _dbus_string_get_data (&f->plaintext);
|
||||
|
||||
ss = InitializeSecurityContextW (&f->credentials,
|
||||
&f->context,
|
||||
NULL,
|
||||
ISC_REQ_IDENTIFY,
|
||||
0,
|
||||
SECURITY_NATIVE_DREP,
|
||||
&input_sec_buffers_descriptor,
|
||||
0,
|
||||
&f->context,
|
||||
&output_sec_buffers_descriptor,
|
||||
&attributes,
|
||||
&lifetime);
|
||||
|
||||
g_print ("getting ISC result\n");
|
||||
if (f->phase <= 2 && expect_error_in_phase[2] != FALSE)
|
||||
{
|
||||
g_assert (ss != SEC_E_OK &&
|
||||
ss != SEC_I_COMPLETE_AND_CONTINUE &&
|
||||
ss != SEC_I_COMPLETE_NEEDED &&
|
||||
ss != SEC_I_CONTINUE_NEEDED);
|
||||
|
||||
g_print ("ISC failed, aborting auth\n");
|
||||
/* Server still thinks we're doing AUTH, make sure it fails */
|
||||
#define FAKEAUTHDATA "DATA baadf00d\r\n"
|
||||
#define FAKEAUTHDATALEN strlen (FAKEAUTHDATA)
|
||||
bytes_written = send (f->sock.sock, FAKEAUTHDATA, FAKEAUTHDATALEN, 0);
|
||||
HANDLE_BAD_SEND
|
||||
g_assert (bytes_written == FAKEAUTHDATALEN);
|
||||
|
||||
g_assert (_dbus_string_init (&f->incoming));
|
||||
f->free_incoming = 1;
|
||||
g_assert (_dbus_string_set_length (&f->incoming, 100));
|
||||
buffer = _dbus_string_get_data (&f->incoming);
|
||||
bytes_read = recv (f->sock.sock, buffer, 100, MSG_PEEK);
|
||||
HANDLE_BAD_RECV
|
||||
g_print ("received %.*s\n", bytes_read, buffer);
|
||||
g_assert (bytes_read >= (int) REJSTRLEN);
|
||||
g_assert (strncmp (buffer, REJSTR, REJSTRLEN) == 0);
|
||||
|
||||
g_print ("auth failed in phase 2\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_assert (ss == SEC_E_OK ||
|
||||
ss == SEC_I_COMPLETE_AND_CONTINUE ||
|
||||
ss == SEC_I_COMPLETE_NEEDED ||
|
||||
ss == SEC_I_CONTINUE_NEEDED);
|
||||
|
||||
if ((SEC_I_COMPLETE_NEEDED == ss) || (SEC_I_COMPLETE_AND_CONTINUE == ss))
|
||||
{
|
||||
ss = CompleteAuthToken (&f->context,
|
||||
&output_sec_buffers_descriptor);
|
||||
g_assert (ss == SEC_E_OK);
|
||||
}
|
||||
|
||||
_dbus_string_set_length (&f->plaintext, output_sec_buffer.cbBuffer);
|
||||
|
||||
g_print ("phase 3\n");
|
||||
if (f->phase == 3)
|
||||
{
|
||||
/* Session key starts at byte 52, everything from that point is irrelevant */
|
||||
if (f->corrupt_offset < output_sec_buffer.cbBuffer && f->corrupt_offset < 52)
|
||||
{
|
||||
BYTE x = ((BYTE *)output_sec_buffer.pvBuffer)[f->corrupt_offset];
|
||||
x = (x == 255 ? 0 : x + 1);
|
||||
((BYTE *)output_sec_buffer.pvBuffer)[f->corrupt_offset] = x;
|
||||
|
||||
switch (f->corrupt_offset)
|
||||
{
|
||||
/* 2 bytes (12, 13) is LM Response SB length, changing it causes an error */
|
||||
case 12:
|
||||
case 13:
|
||||
/* 2 bytes (20, 21) is NTLM Response SB length, changing it causes an error */
|
||||
case 20:
|
||||
case 21:
|
||||
/* 2 bytes (28, 29) is Target Name SB length, changing it causes an error */
|
||||
case 28:
|
||||
case 29:
|
||||
/* 2 bytes (36, 37) is User Name SB length, changing it causes an error */
|
||||
case 36:
|
||||
case 37:
|
||||
/* 2 bytes (44, 45) is Workstation Name SB length, changing it causes an error */
|
||||
case 44:
|
||||
case 45:
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[3] = TRUE;
|
||||
break;
|
||||
default:
|
||||
if ((f->corrupt_offset > 13 && f->corrupt_offset < 20) ||
|
||||
(f->corrupt_offset > 21 && f->corrupt_offset < 28) ||
|
||||
(f->corrupt_offset > 29 && f->corrupt_offset < 36) ||
|
||||
(f->corrupt_offset > 37 && f->corrupt_offset < 44) ||
|
||||
(f->corrupt_offset > 45 && f->corrupt_offset < 52))
|
||||
{
|
||||
/* 2 bytes (14, 15) is LM Response SB allocated space, changing it makes no difference */
|
||||
/* 4 bytes (16, 17, 18, 19) is LM Response SB offset, changing it makes no difference */
|
||||
/* 2 bytes (22, 23) is NTLM Response SB allocated space, changing it makes no difference */
|
||||
/* 4 bytes (24, 25, 26, 27) is NTLM Response SB offset, changing it makes no difference */
|
||||
/* and so on for Target Name, User name and Workstation Name */
|
||||
g_print ("corrupted byte %d in phase %d, auth will succeed anyway\n", f->corrupt_offset, f->phase);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_print ("corrupted byte %d in phase %d, auth will fail in phase 3\n", f->corrupt_offset, f->phase);
|
||||
expect_error_in_phase[3] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
f->corrupt_offset += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->phase += 1;
|
||||
f->corrupt_offset = 0;
|
||||
g_print ("stopping corruption\n");
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (_dbus_string_hex_encode (&f->plaintext, 0, &f->outgoing,
|
||||
_dbus_string_get_length (&f->outgoing)));
|
||||
|
||||
g_assert (_dbus_string_append (&f->outgoing, "\r\n"));
|
||||
|
||||
bytes_written = send (f->sock.sock,
|
||||
_dbus_string_get_data (&f->outgoing), _dbus_string_get_length (&f->outgoing),
|
||||
0);
|
||||
HANDLE_BAD_SEND
|
||||
g_assert (bytes_written == _dbus_string_get_length (&f->outgoing));
|
||||
|
||||
#undef DATASTR
|
||||
#undef DATASTRLEN
|
||||
g_print ("receiving in phase 3\n");
|
||||
|
||||
g_assert (_dbus_string_init (&f->incoming));
|
||||
f->free_incoming = 1;
|
||||
g_assert (_dbus_string_set_length (&f->incoming, 100));
|
||||
buffer = _dbus_string_get_data (&f->incoming);
|
||||
bytes_read = recv (f->sock.sock, buffer, 100, MSG_PEEK);
|
||||
HANDLE_BAD_RECV
|
||||
g_print ("received %.*s\n", bytes_read, buffer);
|
||||
|
||||
if (f->phase <= 3 && expect_error_in_phase[3] != FALSE)
|
||||
{
|
||||
g_assert (bytes_read >= (int) REJSTRLEN);
|
||||
g_assert (strncmp (buffer, REJSTR, REJSTRLEN) == 0);
|
||||
|
||||
g_print ("auth failed in phase 3\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#undef REJSTR
|
||||
#undef REJSTRLEN
|
||||
|
||||
#define OKSTR "OK "
|
||||
#define OKSTRLEN strlen (OKSTR)
|
||||
|
||||
g_assert (bytes_read >= (int) OKSTRLEN);
|
||||
g_assert (strncmp (buffer, OKSTR, OKSTRLEN) == 0);
|
||||
|
||||
g_print ("auth succeeded\n");
|
||||
|
||||
#undef OKSTR
|
||||
#undef OKSTRLEN
|
||||
|
||||
/* Can't re-attempt AUTH after OK, must tear the connection down */
|
||||
_dbus_close_socket (&f->sock, NULL);
|
||||
_dbus_socket_invalidate (&f->sock);
|
||||
|
||||
_dbus_string_free (&f->outgoing);
|
||||
f->free_outgoing = 0;
|
||||
|
||||
_dbus_string_free (&f->incoming);
|
||||
f->free_incoming = 0;
|
||||
|
||||
DeleteSecurityContext (&f->context);
|
||||
f->free_context = 0;
|
||||
|
||||
if (f->phase == 0)
|
||||
{
|
||||
f->phase += 1;
|
||||
g_print ("will begin corrupting phase %d\n", f->phase);
|
||||
}
|
||||
else if (f->phase >= 4)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_sspi_corrupt (Fixture *f,
|
||||
gconstpointer context)
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
/* clean up after previous iteration */
|
||||
if (f->free_context)
|
||||
{
|
||||
DeleteSecurityContext (&f->context);
|
||||
f->free_context = 0;
|
||||
}
|
||||
|
||||
if (f->free_outgoing)
|
||||
{
|
||||
f->free_outgoing = 0;
|
||||
_dbus_string_free (&f->outgoing);
|
||||
}
|
||||
if (f->free_incoming)
|
||||
{
|
||||
f->free_incoming = 0;
|
||||
_dbus_string_free (&f->incoming);
|
||||
}
|
||||
if (f->free_decoded)
|
||||
{
|
||||
f->free_decoded = 0;
|
||||
_dbus_string_free (&f->decoded);
|
||||
}
|
||||
|
||||
if (!do_corrupt_sspi_auth (f, context))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
teardown (Fixture *f,
|
||||
gconstpointer context G_GNUC_UNUSED)
|
||||
{
|
||||
SECURITY_STATUS ss;
|
||||
|
||||
if (_dbus_socket_is_valid (f->sock))
|
||||
{
|
||||
_dbus_close_socket (&f->sock, NULL);
|
||||
_dbus_socket_invalidate (&f->sock);
|
||||
}
|
||||
|
||||
ss = FreeCredentialsHandle (&f->credentials);
|
||||
g_assert (ss == SEC_E_OK);
|
||||
_dbus_string_free (&f->plaintext);
|
||||
|
||||
dbus_error_free (&f->e);
|
||||
|
||||
if (f->daemon_pid != 0)
|
||||
{
|
||||
test_kill_pid (f->daemon_pid);
|
||||
g_spawn_close_pid (f->daemon_pid);
|
||||
f->daemon_pid = 0;
|
||||
}
|
||||
|
||||
test_main_context_unref (f->ctx);
|
||||
g_free (f->address);
|
||||
}
|
||||
|
||||
static Config sspi_corrupt_config = {
|
||||
"96577", 1, "valid-config-files/debug-auth-sspi.conf",
|
||||
SPECIFY_ADDRESS
|
||||
};
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char **argv)
|
||||
{
|
||||
test_init (&argc, &argv);
|
||||
|
||||
g_test_add ("/auth/sspi-corrupt", Fixture, &sspi_corrupt_config, setup, test_sspi_corrupt, teardown);
|
||||
|
||||
return g_test_run ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
159
test/test-utils-auth.c
Executable file
159
test/test-utils-auth.c
Executable file
|
|
@ -0,0 +1,159 @@
|
|||
#include <config.h>
|
||||
#include "test-utils-auth.h"
|
||||
#include "test-utils-glib.h"
|
||||
|
||||
static dbus_bool_t
|
||||
_dbus_win_simple_startup_winsock (void)
|
||||
{
|
||||
/* Straight from MSDN, deuglified */
|
||||
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int err;
|
||||
|
||||
wVersionRequested = MAKEWORD (2, 0);
|
||||
|
||||
err = WSAStartup (wVersionRequested, &wsaData);
|
||||
if (err != 0)
|
||||
{
|
||||
_dbus_assert_not_reached ("Could not initialize WinSock");
|
||||
_dbus_abort ();
|
||||
}
|
||||
|
||||
/* Confirm that the WinSock DLL supports 2.0. Note that if the DLL
|
||||
* supports versions greater than 2.0 in addition to 2.0, it will
|
||||
* still return 2.0 in wVersion since that is the version we
|
||||
* requested.
|
||||
*/
|
||||
if (LOBYTE (wsaData.wVersion) != 2 ||
|
||||
HIBYTE (wsaData.wVersion) != 0)
|
||||
{
|
||||
_dbus_assert_not_reached ("No usable WinSock found");
|
||||
_dbus_abort ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static DBusSocket
|
||||
_dbus_connect_simple_tcp_socket (const char *host,
|
||||
const char *port,
|
||||
const char *family)
|
||||
{
|
||||
DBusSocket fd = DBUS_SOCKET_INIT;
|
||||
int res;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *ai, *tmp;
|
||||
|
||||
if (!_dbus_win_simple_startup_winsock ())
|
||||
return _dbus_socket_get_invalid ();
|
||||
|
||||
_DBUS_ZERO (hints);
|
||||
|
||||
if (!family)
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
else if (!strcmp(family, "ipv4"))
|
||||
hints.ai_family = AF_INET;
|
||||
else if (!strcmp(family, "ipv6"))
|
||||
hints.ai_family = AF_INET6;
|
||||
else
|
||||
return _dbus_socket_get_invalid ();
|
||||
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
#else
|
||||
hints.ai_flags = 0;
|
||||
#endif
|
||||
|
||||
if ((res = getaddrinfo (host, port, &hints, &ai)) != 0 || !ai)
|
||||
return _dbus_socket_get_invalid ();
|
||||
|
||||
tmp = ai;
|
||||
while (tmp)
|
||||
{
|
||||
if ((fd.sock = socket (tmp->ai_family, SOCK_STREAM, 0)) == INVALID_SOCKET)
|
||||
{
|
||||
freeaddrinfo(ai);
|
||||
return _dbus_socket_get_invalid ();
|
||||
}
|
||||
|
||||
if (connect (fd.sock, (struct sockaddr*) tmp->ai_addr, tmp->ai_addrlen) == SOCKET_ERROR)
|
||||
{
|
||||
closesocket (fd.sock);
|
||||
fd.sock = INVALID_SOCKET;
|
||||
tmp = tmp->ai_next;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
freeaddrinfo (ai);
|
||||
|
||||
if (!_dbus_socket_is_valid (fd))
|
||||
return _dbus_socket_get_invalid ();
|
||||
|
||||
/* Every SOCKET is also a HANDLE. */
|
||||
SetHandleInformation((HANDLE) fd.sock,
|
||||
HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE,
|
||||
0 /*disable both flags*/ );
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static DBusSocket
|
||||
_dbus_simple_connection_open_internal (const char *address,
|
||||
DBusError *error)
|
||||
{
|
||||
DBusSocket s = DBUS_SOCKET_INIT;
|
||||
DBusAddressEntry **entries;
|
||||
int len, i;
|
||||
|
||||
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
|
||||
|
||||
_dbus_verbose ("opening simple connection to: %s\n", address);
|
||||
|
||||
if (!dbus_parse_address (address, &entries, &len, error))
|
||||
return s;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
const char *host = dbus_address_entry_get_value (entries[i], "host");
|
||||
const char *port = dbus_address_entry_get_value (entries[i], "port");
|
||||
const char *family = dbus_address_entry_get_value (entries[i], "family");
|
||||
|
||||
if (port == NULL)
|
||||
continue;
|
||||
|
||||
if (host == NULL)
|
||||
host = "localhost";
|
||||
|
||||
s = _dbus_connect_simple_tcp_socket (host, port, family);
|
||||
if (!_dbus_socket_is_valid (s))
|
||||
continue;
|
||||
|
||||
_dbus_verbose ("Successfully connected to tcp socket %s:%s\n",
|
||||
host, port);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
dbus_address_entries_free (entries);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
DBusSocket
|
||||
test_connect_to_bus_simple (TestMainContext *ctx,
|
||||
const gchar *address)
|
||||
{
|
||||
DBusSocket sock;
|
||||
DBusError error = DBUS_ERROR_INIT;
|
||||
|
||||
sock = _dbus_simple_connection_open_internal (address, &error);
|
||||
test_assert_no_error (&error);
|
||||
g_assert (_dbus_socket_is_valid (sock));
|
||||
|
||||
return sock;
|
||||
}
|
||||
13
test/test-utils-auth.h
Executable file
13
test/test-utils-auth.h
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef TEST_UTILS_AUTH_H
|
||||
#define TEST_UTILS_AUTH_H
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include "test-utils.h"
|
||||
#include <dbus/dbus-transport.h>
|
||||
#include <glib.h>
|
||||
|
||||
DBusSocket
|
||||
test_connect_to_bus_simple (TestMainContext *ctx,
|
||||
const gchar *address);
|
||||
|
||||
#endif
|
||||
|
|
@ -259,6 +259,9 @@ spawn_dbus_daemon (const gchar *binary,
|
|||
|
||||
g_ptr_array_add (argv, NULL);
|
||||
|
||||
test_dump_args(argv);
|
||||
test_dump_env(envp);
|
||||
|
||||
g_spawn_async_with_pipes (NULL, /* working directory */
|
||||
(gchar **) argv->pdata,
|
||||
envp,
|
||||
|
|
@ -1047,3 +1050,18 @@ test_get_helper_executable (const gchar *exe)
|
|||
|
||||
return g_build_filename (dbus_test_exec, exe, NULL);
|
||||
}
|
||||
|
||||
void test_dump_args (const GPtrArray *argv)
|
||||
{
|
||||
for (guint i = 0; i < argv->len; i++)
|
||||
{
|
||||
gchar *str = g_ptr_array_index(argv, i);
|
||||
g_print ("argv[%d] = %s\n", i, str);
|
||||
}
|
||||
}
|
||||
|
||||
void test_dump_env (gchar **env)
|
||||
{
|
||||
for (gint i = 0; env[i] != NULL; i++)
|
||||
g_print ("env: %s\n", env[i]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,4 +154,7 @@ void test_incomplete (const gchar *message);
|
|||
|
||||
gchar *test_get_helper_executable (const gchar *exe);
|
||||
|
||||
void test_dump_args (const GPtrArray *argv);
|
||||
void test_dump_env (gchar **env);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue