From 0ceb3b5f6086e0028a71a7abfc50d12536ffe986 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 | 140 ++++++++++++ dbus/dbus-sysdeps-win.c | 595 ++++++++++++++++++++++++++++++++++++++++++++++-- dbus/dbus-sysdeps-win.h | 58 ++++- 4 files changed, 771 insertions(+), 24 deletions(-) diff --git a/configure.ac b/configure.ac index 1d08030..98aa403 100644 --- a/configure.ac +++ b/configure.ac @@ -1227,7 +1227,7 @@ if test x$dbus_win = xyes ; then if test x$dbus_wince = xyes ; then NETWORK_libs="-lws2" else - NETWORK_libs="-lws2_32 -liphlpapi -ldbghelp" + NETWORK_libs="-lws2_32 -liphlpapi -ldbghelp -lsecur32" fi fi diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index f222787..ba259e3 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -178,6 +178,10 @@ struct DBusAuth int cookie_id; /**< ID of cookie to use */ DBusString challenge; /**< Challenge sent to client */ +#if defined (DBUS_WIN) && !defined (DBUS_WINCE) + DBusSSPINTLMInfo sspi_ntlm; /**< defined in sysdeps-win. */ +#endif + char **allowed_mechs; /**< Mechanisms we're allowed to use, * or #NULL if we can use any */ @@ -1314,9 +1318,135 @@ handle_client_shutdown_anonymous_mech (DBusAuth *auth) } +/* + * DBUS_WINDOWS_SSPI_NTLM mechanism + * Implementation is mostly in dbus-systeps-win.c + */ +#if defined (DBUS_WIN) && !defined (DBUS_WINCE) +static dbus_bool_t +handle_server_data_windows_sspi_ntlm_mech (DBusAuth *auth, + const DBusString *data) +{ + DBusError error = DBUS_ERROR_INIT; + dbus_bool_t done = FALSE; + DBusString challenge; + + if (!_dbus_sspi_ntlm_next_challenge (&auth->sspi_ntlm, data, &challenge, &done, &error)) + { + if (dbus_error_is_set (&error)) + { + send_error (auth, error.message); + dbus_error_free (&error); + } + + return FALSE; + } + + if (_dbus_string_get_length (&challenge) > 0) + send_data (auth, &challenge); + + _dbus_string_free (&challenge); + + if (!done) + { + if (_dbus_string_get_length (&challenge) == 0) + { + send_error (auth, "SSPI produced 0-length challenge, but authentication is not done yet"); + _dbus_string_free (&challenge); + + return FALSE; + } + + goto_state (auth, &server_state_waiting_for_data); + + return TRUE; + } + + if (!_dbus_sspi_ntlm_fetch_credentials (&auth->sspi_ntlm, auth->authorized_identity, &error)) + { + if (dbus_error_is_set (&error)) + { + send_error (auth, error.message); + dbus_error_free (&error); + } + + return FALSE; + } + + if (!send_ok (auth)) + return FALSE; + + _dbus_verbose ("%s: authenticated client using DBUS_WINDOWS_SSPI_NTLM\n", + DBUS_AUTH_NAME (auth)); + + return TRUE; +} + +static void +handle_server_shutdown_windows_sspi_ntlm_mech (DBusAuth *auth) +{ + _dbus_sspi_ntlm_free_info (&auth->sspi_ntlm); +} + +static dbus_bool_t +handle_client_initial_response_windows_sspi_ntlm_mech (DBusAuth *auth, + DBusString *response) +{ + DBusError error = DBUS_ERROR_INIT; + + if (!_dbus_sspi_ntlm_initial_response (&auth->sspi_ntlm, response, &error)) + { + if (dbus_error_is_set (&error)) + { + send_error (auth, error.message); + dbus_error_free (&error); + } + + return FALSE; + } + + return TRUE; +} + +static dbus_bool_t +handle_client_data_windows_sspi_ntlm_mech (DBusAuth *auth, + const DBusString *data) +{ + DBusError error = DBUS_ERROR_INIT; + DBusString response; + + if (!_dbus_sspi_ntlm_next_response (&auth->sspi_ntlm, data, &response, &error)) + { + if (dbus_error_is_set (&error)) + { + send_error (auth, error.message); + dbus_error_free (&error); + } + + return FALSE; + } + + if (_dbus_string_get_length (&response) > 0) + send_data (auth, &response); + else + goto_state (auth, &client_state_waiting_for_ok); + + _dbus_string_free (&response); + + 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 @@ -1327,6 +1457,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, diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 0d64bc1..586b8a2 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -56,8 +56,10 @@ #include /* Declarations missing in mingw's and windows sdk 7.0 headers */ +#ifndef __MINGW64_VERSION_MAJOR extern BOOL WINAPI ConvertStringSidToSidA (LPCSTR StringSid, PSID *Sid); extern BOOL WINAPI ConvertSidToStringSidA (PSID Sid, LPSTR *StringSid); +#endif #include #include @@ -972,61 +974,81 @@ static BOOL is_winxp_sp3_or_lower() dwlConditionMask); } -/** Gets our SID +/** Gets SID from a token * @param sid points to sid buffer, need to be freed with LocalFree() - * @param process_id the process id for which the sid should be returned (use 0 for current process) - * @returns process sid + * @param token a token to get the SID from + * @returns TRUE on success, FALSE otherwise */ dbus_bool_t -_dbus_getsid(char **sid, dbus_pid_t process_id) +_dbus_get_token_sid(char **sid, HANDLE token) { - HANDLE process_token = INVALID_HANDLE_VALUE; TOKEN_USER *token_user = NULL; DWORD n; PSID psid; int retval = FALSE; - HANDLE process_handle; - if (process_id == 0) - process_handle = GetCurrentProcess(); - else if (is_winxp_sp3_or_lower()) - process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id); - else - process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id); - - if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token)) - { - _dbus_win_warn_win_error ("OpenProcessToken failed", GetLastError ()); - goto failed; - } - if ((!GetTokenInformation (process_token, TokenUser, NULL, 0, &n) + if ((!GetTokenInformation (token, TokenUser, NULL, 0, &n) && GetLastError () != ERROR_INSUFFICIENT_BUFFER) || (token_user = alloca (n)) == NULL - || !GetTokenInformation (process_token, TokenUser, token_user, n, &n)) + || !GetTokenInformation (token, TokenUser, token_user, n, &n)) { _dbus_win_warn_win_error ("GetTokenInformation failed", GetLastError ()); goto failed; } + psid = token_user->User.Sid; + if (!IsValidSid (psid)) { _dbus_verbose("%s invalid sid\n",__FUNCTION__); goto failed; } + if (!ConvertSidToStringSidA (psid, sid)) { _dbus_verbose("%s invalid sid\n",__FUNCTION__); goto failed; } + //okay: retval = TRUE; failed: + + _dbus_verbose("_dbus_get_token_sid() got '%s' and returns %d\n", *sid, retval); + return retval; +} + +/** Gets our SID + * @param sid points to sid buffer, need to be freed with LocalFree() + * @param process_id the process id for which the sid should be returned (use 0 for current process) + * @returns process sid + */ +dbus_bool_t +_dbus_getsid(char **sid, dbus_pid_t process_id) +{ + HANDLE process_handle; + HANDLE process_token = INVALID_HANDLE_VALUE; + int retval = FALSE; + + if (process_id == 0) + process_handle = GetCurrentProcess(); + else if (is_winxp_sp3_or_lower()) + process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id); + else + process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id); + + if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token)) + { + _dbus_win_warn_win_error ("OpenProcessToken failed", GetLastError ()); + goto end; + } + retval = _dbus_get_token_sid (sid, process_token); +end: CloseHandle (process_handle); if (process_token != INVALID_HANDLE_VALUE) CloseHandle (process_token); - _dbus_verbose("_dbus_getsid() got '%s' and returns %d\n", *sid, retval); return retval; } #endif @@ -2518,7 +2540,7 @@ static void dump_backtrace_for_thread (HANDLE hThread) if (SymFromAddr (GetCurrentProcess (), sf.AddrPC.Offset, &displacement, pSymbol)) { if (displacement) - DPRINTF ("%3d %s+0x%I64x", i++, pSymbol->Name, displacement); + DPRINTF ("%3d %s+0x%" PRIx64, i++, pSymbol->Name, displacement); else DPRINTF ("%3d %s", i++, pSymbol->Name); } @@ -3576,6 +3598,157 @@ _dbus_win_warn_win_error (const char *message, dbus_error_free (&error); } +typedef struct DBusSSPIErrorEntry DBusSSPIErrorEntry; + +struct DBusSSPIErrorEntry +{ + SECURITY_STATUS value; + const char *literal; + const char *meaning; +}; + +#define def_sspi_err(literal,meaning) { literal, #literal, meaning }, + +static DBusSSPIErrorEntry dbus_sspi_error_map[] = +{ + def_sspi_err (SEC_E_ALGORITHM_MISMATCH, "The client and server cannot communicate because they do not possess a common algorithm.") + def_sspi_err (SEC_E_BAD_BINDINGS, "The SSPI channel bindings supplied by the client are incorrect.") + def_sspi_err (SEC_E_BAD_PKGID, "The requested package identifier does not exist.") + def_sspi_err (SEC_E_BUFFER_TOO_SMALL, "The buffers supplied to the function are not large enough to contain the information.") + def_sspi_err (SEC_E_CANNOT_INSTALL, "The security package cannot initialize successfully and should not be installed.") + def_sspi_err (SEC_E_CANNOT_PACK, "The package is unable to pack the context.") + def_sspi_err (SEC_E_CERT_EXPIRED, "The received certificate has expired.") + def_sspi_err (SEC_E_CERT_UNKNOWN, "An unknown error occurred while processing the certificate.") + def_sspi_err (SEC_E_CERT_WRONG_USAGE, "The certificate is not valid for the requested usage.") + def_sspi_err (SEC_E_CONTEXT_EXPIRED, "The application is referencing a context that has already been closed. A properly written application should not receive this error.") + def_sspi_err (SEC_E_CROSSREALM_DELEGATION_FAILURE, "The server attempted to make a Kerberos-constrained delegation request for a target outside the server's realm.") + def_sspi_err (SEC_E_CRYPTO_SYSTEM_INVALID, "The cryptographic system or checksum function is not valid because a required function is unavailable.") + def_sspi_err (SEC_E_DECRYPT_FAILURE, "The specified data could not be decrypted.") + def_sspi_err (SEC_E_DELEGATION_REQUIRED, "The requested operation cannot be completed. The computer must be trusted for delegation, and the current user account must be configured to allow delegation.") + def_sspi_err (SEC_E_DOWNGRADE_DETECTED, "The system detected a possible attempt to compromise security. Verify that the server that authenticated you can be contacted.") + def_sspi_err (SEC_E_ENCRYPT_FAILURE, "The specified data could not be encrypted.") + def_sspi_err (SEC_E_ILLEGAL_MESSAGE, "The message received was unexpected or badly formatted.") + def_sspi_err (SEC_E_INCOMPLETE_CREDENTIALS, "The credentials supplied were not complete and could not be verified. The context could not be initialized.") + def_sspi_err (SEC_E_INCOMPLETE_MESSAGE, "The message supplied was incomplete. The signature was not verified.") + def_sspi_err (SEC_E_INSUFFICIENT_MEMORY, "Not enough memory is available to complete the request.") + def_sspi_err (SEC_E_INTERNAL_ERROR, "An error occurred that did not map to an SSPI error code.") + def_sspi_err (SEC_E_INVALID_HANDLE, "The handle passed to the function is not valid.") + def_sspi_err (SEC_E_INVALID_TOKEN, "The token passed to the function is not valid.") + def_sspi_err (SEC_E_ISSUING_CA_UNTRUSTED, "An untrusted certification authority (CA) was detected while processing the smart card certificate used for authentication.") + def_sspi_err (SEC_E_ISSUING_CA_UNTRUSTED_KDC, "An untrusted CA was detected while processing the domain controller certificate used for authentication. The system event log contains additional information.") + def_sspi_err (SEC_E_KDC_CERT_EXPIRED, "The domain controller certificate used for smart card logon has expired.") + def_sspi_err (SEC_E_KDC_CERT_REVOKED, "The domain controller certificate used for smart card logon has been revoked.") + def_sspi_err (SEC_E_KDC_INVALID_REQUEST, "A request that is not valid was sent to the KDC.") + def_sspi_err (SEC_E_KDC_UNABLE_TO_REFER, "The KDC was unable to generate a referral for the service requested.") + def_sspi_err (SEC_E_KDC_UNKNOWN_ETYPE, "The requested encryption type is not supported by the KDC.") + def_sspi_err (SEC_E_LOGON_DENIED, "This status code is obsolete.") + def_sspi_err (SEC_E_MAX_REFERRALS_EXCEEDED, "The number of maximum ticket referrals has been exceeded.") + def_sspi_err (SEC_E_MESSAGE_ALTERED, "The message supplied for verification has been altered.") + def_sspi_err (SEC_E_MULTIPLE_ACCOUNTS, "The received certificate was mapped to multiple accounts.") + def_sspi_err (SEC_E_MUST_BE_KDC, "The local computer must be a Kerberos domain controller (KDC), but it is not.") + def_sspi_err (SEC_E_NO_AUTHENTICATING_AUTHORITY, "No authority could be contacted for authentication.") + def_sspi_err (SEC_E_NO_CREDENTIALS, "No credentials are available.") + def_sspi_err (SEC_E_NO_IMPERSONATION, "No impersonation is allowed for this context.") + def_sspi_err (SEC_E_NO_IP_ADDRESSES, "Unable to accomplish the requested task because the local computer does not have any IP addresses.") + def_sspi_err (SEC_E_NO_KERB_KEY, "No Kerberos key was found.") + def_sspi_err (SEC_E_NO_PA_DATA, "Policy administrator (PA) data is needed to determine the encryption type, but cannot be found.") + def_sspi_err (SEC_E_NO_S4U_PROT_SUPPORT, "The Kerberos subsystem encountered an error. A service for user protocol request was made against a domain controller which does not support service for a user.") + def_sspi_err (SEC_E_NO_TGT_REPLY, "The client is trying to negotiate a context and the server requires a user-to-user connection, but did not send a TGT reply.") + def_sspi_err (SEC_E_NOT_OWNER, "The caller of the function does not own the credentials.") + def_sspi_err (SEC_E_NOT_SUPPORTED, "The request is not supported.") + def_sspi_err (SEC_E_OK, "The operation completed successfully.") + def_sspi_err (SEC_E_OUT_OF_SEQUENCE, "The message supplied for verification is out of sequence.") + def_sspi_err (SEC_E_PKINIT_CLIENT_FAILURE, "The smart card certificate used for authentication is not trusted.") + def_sspi_err (SEC_E_PKINIT_NAME_MISMATCH, "The client certificate does not contain a valid UPN or does not match the client name in the logon request.") + def_sspi_err (SEC_E_QOP_NOT_SUPPORTED, "The quality of protection attribute is not supported by this package.") + def_sspi_err (SEC_E_REVOCATION_OFFLINE_C, "The revocation status of the smart card certificate used for authentication could not be determined.") + def_sspi_err (SEC_E_REVOCATION_OFFLINE_KDC, "The revocation status of the domain controller certificate used for smart card authentication could not be determined. The system event log contains additional information.") + def_sspi_err (SEC_E_SECPKG_NOT_FOUND, "The security package was not recognized.") + def_sspi_err (SEC_E_SECURITY_QOS_FAILED, "The security context could not be established due to a failure in the requested quality of service (for example, mutual authentication or delegation).") + def_sspi_err (SEC_E_SHUTDOWN_IN_PROGRESS, "A system shutdown is in progress.") + def_sspi_err (SEC_E_SMARTCARD_CERT_EXPIRED, "The smart card certificate used for authentication has expired.") + def_sspi_err (SEC_E_SMARTCARD_CERT_REVOKED, "The smart card certificate used for authentication has been revoked. Additional information may exist in the event log.") + def_sspi_err (SEC_E_SMARTCARD_LOGON_REQUIRED, "Smart card logon is required and was not used.") + def_sspi_err (SEC_E_STRONG_CRYPTO_NOT_SUPPORTED, "The other end of the security negotiation requires strong cryptography, but it is not supported on the local machine.") + def_sspi_err (SEC_E_TARGET_UNKNOWN, "The target was not recognized.") + def_sspi_err (SEC_E_TIME_SKEW, "The clocks on the client and server computers do not match.") + def_sspi_err (SEC_E_TOO_MANY_PRINCIPALS, "The KDC reply contained more than one principal name.") + def_sspi_err (SEC_E_UNFINISHED_CONTEXT_DELETED, "A security context was deleted before the context was completed. This is considered a logon failure.") + def_sspi_err (SEC_E_UNKNOWN_CREDENTIALS, "The credentials provided were not recognized.") + def_sspi_err (SEC_E_UNSUPPORTED_FUNCTION, "The requested function is not supported.") + def_sspi_err (SEC_E_UNSUPPORTED_PREAUTH, "An unsupported preauthentication mechanism was presented to the Kerberos package.") + def_sspi_err (SEC_E_UNTRUSTED_ROOT, "The certificate chain was issued by an authority that is not trusted.") + def_sspi_err (SEC_E_WRONG_CREDENTIAL_HANDLE, "The supplied credential handle does not match the credential associated with the security context.") + def_sspi_err (SEC_E_WRONG_PRINCIPAL, "The target principal name is incorrect.") + def_sspi_err (SEC_I_COMPLETE_AND_CONTINUE, "The function completed successfully, but the application must call both CompleteAuthToken and then either InitializeSecurityContext (General) or AcceptSecurityContext (General) again to complete the context.") + def_sspi_err (SEC_I_COMPLETE_NEEDED, "The function completed successfully, but you must call the CompleteAuthToken function on the final message.") + def_sspi_err (SEC_I_CONTEXT_EXPIRED, "The message sender has finished using the connection and has initiated a shutdown. For information about initiating or recognizing a shutdown, see Shutting Down an Schannel Connection.") + def_sspi_err (SEC_I_CONTINUE_NEEDED, "The function completed successfully, but you must call this function again to complete the context.") + def_sspi_err (SEC_I_INCOMPLETE_CREDENTIALS, "The credentials supplied were not complete and could not be verified. Additional information can be returned from the context.") + def_sspi_err (SEC_I_LOCAL_LOGON, "The logon was completed, but no network authority was available. The logon was made using locally known information.") + def_sspi_err (SEC_I_NO_LSA_CONTEXT, "There is no LSA mode context associated with this context.") + def_sspi_err (SEC_I_RENEGOTIATE, "The context data must be renegotiated with the peer.") + def_sspi_err (SEC_E_OK, "No error occurred.") +}; +#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; + const char *meaning; + + /* 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; + meaning = dbus_sspi_error_map[i].meaning; + } + + if (literal == NULL) + { + literal = "UNRECOGNIZED ERROR CODE"; + meaning = "Meaning unknown"; + } + + if (func_call) + dbus_set_error (error, "win32.error", "%s returned 0x%08lx (%s): %s", func_call, status, literal, meaning); + else + dbus_set_error (error, "win32.error", "0x%08lx (%s): %s", status, literal, meaning); +} + +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 * @@ -3641,5 +3814,383 @@ _dbus_restore_socket_errno (int saved_errno) _dbus_win_set_errno (saved_errno); } +/* + * DBUS_WINDOWS_SSPI_NTLM mechanism + */ +#ifndef DBUS_WINCE +/** + * Feeds client response to SSPI, producing the next challenge. + * + * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth + * @param data - inbound client response + * @param challenge - string to fill with the next challenge + * @param done - boolean to set when SSPI authentication succeeded + * @param error - an error to send back (optional) + * + * The @challenge is always valid (even if its length ends up being zero), + * as long as the function returns TRUE. The @challenge must be sent back + * to the client, then freed with _dbus_string_free(). + * + * @returns #TRUE if everything is OK, @challenge and is @response are valid, #FALSE otherwise + */ +dbus_bool_t +_dbus_sspi_ntlm_next_challenge (DBusSSPINTLMInfo *sspi_ntlm, + const DBusString *data, + DBusString *challenge, + dbus_bool_t *done, + DBusError *error) +{ + SECURITY_STATUS ss; + PSecPkgInfoW sec_package_info; + TimeStamp lifetime; + SecBuffer input_sec_buffer = { 0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc input_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &input_sec_buffer }; + SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer }; + ULONG attributes; + dbus_bool_t is_done; + + input_sec_buffer.cbBuffer = _dbus_string_get_length (data); + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_data (data); + + if (input_sec_buffer.cbBuffer == 0) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Received 0-length DATA, which is unacceptable"); + + return FALSE; + } + + if (!sspi_ntlm->output_buffer) + { + ss = QuerySecurityPackageInfoW (L"NTLM", &sec_package_info); + + if (ss != SEC_E_OK) + { + _dbus_win_warn_sspi_status ("server", "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); + + sspi_ntlm->output_buffer = dbus_malloc (sspi_ntlm->message_limit); + } + + if (!sspi_ntlm->free_credentials) + { + ss = AcquireCredentialsHandleW (NULL, L"NTLM", SECPKG_CRED_INBOUND, 0, 0, 0, 0, + &sspi_ntlm->credentials, &lifetime); + + if (ss != SEC_E_OK) + { + _dbus_win_warn_sspi_status ("server", "AcquireCredentialsHandleW (NTLM, INBOUND)", ss); + + return FALSE; + } + + sspi_ntlm->free_credentials = TRUE; + } + + output_sec_buffer.cbBuffer = sspi_ntlm->message_limit; + output_sec_buffer.pvBuffer = sspi_ntlm->output_buffer; + + 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) + { + _dbus_win_warn_sspi_status ("server", "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) + { + _dbus_win_warn_sspi_status ("server", "CompleteAuthToken ()", ss); + + return FALSE; + } + } + + _dbus_string_init_const_len (challenge, + output_sec_buffer.pvBuffer, + output_sec_buffer.cbBuffer); + + *done = is_done; + + return TRUE; +} + +/** + * Converts successfully authenticated SSPI security context + * attributes into credentials, adding them to DBusCredentials. + * + * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth + * @param credentials - credentials object to modify + * + * @returns #TRUE if everything is OK and @credentials were modified, #FALSE otherwise + */ +dbus_bool_t +_dbus_sspi_ntlm_fetch_credentials (DBusSSPINTLMInfo *sspi_ntlm, + DBusCredentials *credentials, + DBusError *error) +{ + SECURITY_STATUS ss; + HANDLE client_token; + char *sid; + dbus_bool_t result; + + ss = QuerySecurityContextToken (&sspi_ntlm->context, &client_token); + + if (ss != SEC_E_OK) + { + _dbus_win_warn_sspi_status ("server", "QuerySecurityContextToken ()", ss); + + return FALSE; + } + + result = _dbus_get_token_sid (&sid, client_token); + CloseHandle (client_token); + + if (!result) + return result; + + result = _dbus_credentials_add_windows_sid (credentials, sid); + LocalFree (sid); + + 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->output_buffer != NULL) + dbus_free (sspi_ntlm->output_buffer); + + sspi_ntlm->output_buffer = NULL; + + if (sspi_ntlm->free_context) + DeleteSecurityContext (&sspi_ntlm->context); + + sspi_ntlm->free_context = FALSE; + + if (sspi_ntlm->free_credentials) + FreeCredentialHandle (&sspi_ntlm->credentials); + + sspi_ntlm->free_credentials = FALSE; +} + +/** + * Generates initial client response for SSPI NTLM authentication and + * appends it to @initial_response. + * + * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth + * @param initial_response - outbound data to append the response to + * + * @returns #TRUE if everything is OK and @initial_response was modified, #FALSE otherwise + */ +dbus_bool_t +_dbus_sspi_ntlm_initial_response (DBusSSPINTLMInfo *sspi_ntlm, + DBusString *initial_response, + DBusError *error) +{ + SECURITY_STATUS ss; + PSecPkgInfoW sec_package_info; + TimeStamp lifetime; + SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer }; + ULONG attributes; + DBusString plaintext; + dbus_bool_t result; + + ss = QuerySecurityPackageInfoW (L"NTLM", &sec_package_info); + + if (ss != SEC_E_OK) + { + _dbus_win_warn_sspi_status ("client", "QuerySecurityPackageInfoW ()", ss); + + return FALSE; + } + + sspi_ntlm->message_limit = sec_package_info->cbMaxToken; + + ss = FreeContextBuffer (sec_package_info); + + if (ss != SEC_E_OK) + _dbus_win_warn_sspi_status ("client", "FreeContextBuffer (PSecPkgInfoW)", ss); + + ss = AcquireCredentialsHandleW (NULL, L"NTLM", SECPKG_CRED_OUTBOUND, 0, 0, 0, 0, + &sspi_ntlm->credentials, &lifetime); + + if (ss != SEC_E_OK) + { + _dbus_win_warn_sspi_status ("client", "AcquireCredentialsHandleW (NTLM, OUTBOUND)", ss); + + return FALSE; + } + + sspi_ntlm->free_credentials = TRUE; + sspi_ntlm->output_buffer = dbus_malloc (sspi_ntlm->message_limit); + + output_sec_buffer.cbBuffer = sspi_ntlm->message_limit; + output_sec_buffer.pvBuffer = sspi_ntlm->output_buffer; + + 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_warn_sspi_status ("client", "InitializeSecurityContextW ()", ss); + + 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_warn_sspi_status ("client", "CompleteAuthToken ()", ss); + + return FALSE; + } + } + + _dbus_string_init_const_len (&plaintext, output_sec_buffer.pvBuffer, output_sec_buffer.cbBuffer); + + result = _dbus_string_hex_encode (&plaintext, 0, initial_response, + _dbus_string_get_length (initial_response)); + + _dbus_string_free (&plaintext); + + return result; +} + +/** + * Feeds server challenge to SSPI, producing the next response. + * + * @param sspi_ntlm - SSPI NTLM context carried by DBusAuth + * @param data - inbound server challenge + * @param response - string to fill with the next response + * + * @returns #TRUE if everything is OK and @response are valid, #FALSE otherwise + */ +dbus_bool_t +_dbus_sspi_ntlm_next_response (DBusSSPINTLMInfo *sspi_ntlm, + const DBusString *data, + DBusString *response, + DBusError *error) +{ + SECURITY_STATUS ss; + TimeStamp lifetime; + SecBuffer input_sec_buffer = { 0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc input_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &input_sec_buffer }; + SecBuffer output_sec_buffer = { 0, SECBUFFER_TOKEN, NULL}; + SecBufferDesc output_sec_buffers_descriptor = { SECBUFFER_VERSION, 1, &output_sec_buffer }; + ULONG attributes; + + input_sec_buffer.cbBuffer = _dbus_string_get_length (data); + input_sec_buffer.pvBuffer = (void *) _dbus_string_get_const_data (data); + + if (input_sec_buffer.cbBuffer == 0) + { + dbus_set_error_const (error, DBUS_ERROR_FAILED, "Received 0-length DATA, which is unacceptable"); + + return FALSE; + } + + output_sec_buffer.cbBuffer = sspi_ntlm->message_limit; + output_sec_buffer.pvBuffer = sspi_ntlm->output_buffer; + + 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) + { + _dbus_win_warn_sspi_status ("client", "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) + { + _dbus_win_warn_sspi_status ("client", "CompleteAuthToken ()", ss); + + return FALSE; + } + } + + _dbus_string_init_const_len (response, + output_sec_buffer.pvBuffer, + output_sec_buffer.cbBuffer); + + return TRUE; +} +#endif + /** @} end of sysdeps-win */ /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-sysdeps-win.h b/dbus/dbus-sysdeps-win.h index 0b5d8f0..5d273de 100644 --- a/dbus/dbus-sysdeps-win.h +++ b/dbus/dbus-sysdeps-win.h @@ -26,6 +26,8 @@ #ifndef DBUS_SYSDEPS_WIN_H #define DBUS_SYSDEPS_WIN_H +#define SECURITY_WIN32 + extern void *_dbus_win_get_dll_hmodule (void); #define WIN32_LEAN_AND_MEAN @@ -36,6 +38,11 @@ extern void *_dbus_win_get_dll_hmodule (void); #include #undef interface +#ifndef DBUS_WINCE +#include +#include +#endif + #define DBUS_CONSOLE_DIR "/var/run/console/" @@ -91,7 +98,56 @@ void _dbus_threads_windows_init_global (void); void _dbus_threads_windows_ensure_ctor_linked (void); DBUS_PRIVATE_EXPORT -dbus_bool_t _dbus_getsid(char **sid, dbus_pid_t process_id); +dbus_bool_t _dbus_get_token_sid (char **sid, + HANDLE token); +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_getsid (char **sid, + dbus_pid_t process_id); + +/** Information for SSPI NTLM authentication */ +typedef struct DBusSSPINTLMInfo DBusSSPINTLMInfo; + +/** + * Information for SSPI NTLM authentication + */ +struct DBusSSPINTLMInfo +{ +#ifndef DBUS_WINCE + CredHandle credentials; /**< SSPI Credentials. */ + CtxtHandle context; /**< SSPI Context. */ + size_t message_limit; /**< Maximum size of an SSPI NTLM message. */ + char *output_buffer; /**< Pre-allocated buffer for an SSPI NTLM message. */ + unsigned int free_credentials : 1; /**< Must free credentials on shutdown */ + unsigned int free_context : 1; /**< Must free context on shutdown */ +#endif +}; + +void _dbus_win_set_error_from_sspi_status (DBusError *error, + const char *func_call, + SECURITY_STATUS status); +void _dbus_win_warn_sspi_status (const char *message, + const char *func_call, + SECURITY_STATUS status); + +dbus_bool_t _dbus_sspi_ntlm_next_challenge (DBusSSPINTLMInfo *sspi_ntlm, + const DBusString *data, + DBusString *challenge, + dbus_bool_t *done, + DBusError *error); +dbus_bool_t _dbus_sspi_ntlm_fetch_credentials (DBusSSPINTLMInfo *sspi_ntlm, + DBusCredentials *credentials, + DBusError *error); + +dbus_bool_t _dbus_sspi_ntlm_initial_response (DBusSSPINTLMInfo *sspi_ntlm, + DBusString *initial_response, + DBusError *error); +dbus_bool_t _dbus_sspi_ntlm_next_response (DBusSSPINTLMInfo *sspi_ntlm, + const DBusString *data, + DBusString *response, + DBusError *error); + +void _dbus_sspi_ntlm_free_info (DBusSSPINTLMInfo *sspi_ntlm); + #endif /** @} end of sysdeps-win.h */ -- 2.4.0