From 50e9d3adf90fe73c0c95818ad835794e3cc207bb Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Thu, 29 Jul 2021 12:33:22 +0200 Subject: [PATCH] Implemented new AuthenticateEx callbacks. --- include/freerdp/freerdp.h | 19 ++- libfreerdp/core/CMakeLists.txt | 2 + libfreerdp/core/gateway/ncacn_http.c | 62 ++-------- libfreerdp/core/gateway/rdg.c | 61 ++------- libfreerdp/core/gateway/rpc_bind.c | 47 ++----- libfreerdp/core/nla.c | 39 ++---- libfreerdp/core/transport.c | 51 +++----- libfreerdp/core/utils.c | 178 +++++++++++++++++++++++++++ libfreerdp/core/utils.h | 41 ++++++ 9 files changed, 302 insertions(+), 198 deletions(-) create mode 100644 libfreerdp/core/utils.c create mode 100644 libfreerdp/core/utils.h diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 98e8c650f..445cd486a 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -76,6 +76,16 @@ extern "C" #define GATEWAY_MESSAGE_CONSENT 1 #define GATEWAY_MESSAGE_SERVICE 2 + typedef enum + { + AUTH_NLA, + AUTH_TLS, + AUTH_RDP, + GW_AUTH_HTTP, + GW_AUTH_RDG, + GW_AUTH_RPC + } rdp_auth_reason; + typedef BOOL (*pContextNew)(freerdp* instance, rdpContext* context); typedef void (*pContextFree)(freerdp* instance, rdpContext* context); @@ -84,6 +94,8 @@ extern "C" typedef void (*pPostDisconnect)(freerdp* instance); typedef BOOL (*pAuthenticate)(freerdp* instance, char** username, char** password, char** domain); + typedef BOOL (*pAuthenticateEx)(freerdp* instance, char** username, char** password, + char** domain, rdp_auth_reason reason); /** @brief Callback used if user interaction is required to accept * an unknown certificate. @@ -431,14 +443,17 @@ fingerprint. DEPRECATED: Use VerifyChangedCertificateEx */ VerifyChangedCertificateEx; /**< (offset 67) Callback for changed certificate validation. Used when a certificate differs from stored fingerprint. */ - ALIGN64 pSendChannelPacket SendChannelPacket; /* (offset 68) * Callback for sending RAW data to a channel. In contrast to * SendChannelData data fragmentation is up to the user and this * function sends data as is with the provided flags. */ - UINT64 paddingE[80 - 69]; /* 69 */ + ALIGN64 pAuthenticateEx AuthenticateEx; /**< (offset 69) + Callback for authentication. + It is used to get the username/password. The reason + argument tells why it was called. */ + UINT64 paddingE[80 - 70]; /* 70 */ }; struct rdp_channel_handles diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index f9f830064..b934e94ee 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -50,6 +50,8 @@ set(${MODULE_PREFIX}_GATEWAY_SRCS ${${MODULE_PREFIX}_GATEWAY_DIR}/ncacn_http.h) set(${MODULE_PREFIX}_SRCS + utils.c + utils.h bulk.c bulk.h activation.c diff --git a/libfreerdp/core/gateway/ncacn_http.c b/libfreerdp/core/gateway/ncacn_http.c index 445401102..6a54a25dd 100644 --- a/libfreerdp/core/gateway/ncacn_http.c +++ b/libfreerdp/core/gateway/ncacn_http.c @@ -29,6 +29,8 @@ #include #include +#include "../utils.h" + #define TAG FREERDP_TAG("core.gateway.ntlm") static wStream* rpc_ntlm_http_request(HttpContext* http, const char* method, int contentLength, @@ -131,6 +133,7 @@ BOOL rpc_ncacn_http_ntlm_init(rdpContext* context, RpcChannel* channel) rdpNtlm* ntlm; rdpSettings* settings; freerdp* instance; + auth_status rc; if (!context || !channel) return FALSE; @@ -143,57 +146,18 @@ BOOL rpc_ncacn_http_ntlm_init(rdpContext* context, RpcChannel* channel) if (!tls || !ntlm || !instance || !settings) return FALSE; - if (!settings->GatewayPassword || !settings->GatewayUsername || - !strlen(settings->GatewayPassword) || !strlen(settings->GatewayUsername)) + rc = utils_authenticate_gateway(instance, GW_AUTH_HTTP); + switch (rc) { - if (freerdp_shall_disconnect(instance)) - return FALSE; - - if (!instance->GatewayAuthenticate) - { - freerdp_set_last_error_log(context, FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); + case AUTH_SUCCESS: + case AUTH_SKIP: + break; + case AUTH_NO_CREDENTIALS: + freerdp_set_last_error_log(instance->context, + FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); return TRUE; - } - else - { - BOOL proceed = - instance->GatewayAuthenticate(instance, &settings->GatewayUsername, - &settings->GatewayPassword, &settings->GatewayDomain); - - if (!proceed) - { - freerdp_set_last_error_log(context, - FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); - return TRUE; - } - - if (settings->GatewayUseSameCredentials) - { - if (settings->GatewayUsername) - { - free(settings->Username); - - if (!(settings->Username = _strdup(settings->GatewayUsername))) - return FALSE; - } - - if (settings->GatewayDomain) - { - free(settings->Domain); - - if (!(settings->Domain = _strdup(settings->GatewayDomain))) - return FALSE; - } - - if (settings->GatewayPassword) - { - free(settings->Password); - - if (!(settings->Password = _strdup(settings->GatewayPassword))) - return FALSE; - } - } - } + default: + return FALSE; } if (!ntlm_client_init(ntlm, TRUE, settings->GatewayUsername, settings->GatewayDomain, diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c index b165d9345..5e6fb48a8 100644 --- a/libfreerdp/core/gateway/rdg.c +++ b/libfreerdp/core/gateway/rdg.c @@ -38,6 +38,7 @@ #include "../rdp.h" #include "../../crypto/opensslcompat.h" #include "rpc_fault.h" +#include "../utils.h" #define TAG FREERDP_TAG("core.gateway.rdg") @@ -1559,63 +1560,21 @@ DWORD rdg_get_event_handles(rdpRdg* rdg, HANDLE* events, DWORD count) static BOOL rdg_get_gateway_credentials(rdpContext* context) { - rdpSettings* settings = context->settings; freerdp* instance = context->instance; - if (!settings->GatewayPassword || !settings->GatewayUsername || - !strlen(settings->GatewayPassword) || !strlen(settings->GatewayUsername)) + auth_status rc = utils_authenticate_gateway(instance, GW_AUTH_RDG); + switch (rc) { - if (freerdp_shall_disconnect(instance)) + case AUTH_SUCCESS: + case AUTH_SKIP: + return TRUE; + case AUTH_NO_CREDENTIALS: + freerdp_set_last_error_log(instance->context, + FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); return FALSE; - - if (!instance->GatewayAuthenticate) - { - freerdp_set_last_error_log(context, FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); + default: return FALSE; - } - else - { - BOOL proceed = - instance->GatewayAuthenticate(instance, &settings->GatewayUsername, - &settings->GatewayPassword, &settings->GatewayDomain); - - if (!proceed) - { - freerdp_set_last_error_log(context, - FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); - return FALSE; - } - - if (settings->GatewayUseSameCredentials) - { - if (settings->GatewayUsername) - { - free(settings->Username); - - if (!(settings->Username = _strdup(settings->GatewayUsername))) - return FALSE; - } - - if (settings->GatewayDomain) - { - free(settings->Domain); - - if (!(settings->Domain = _strdup(settings->GatewayDomain))) - return FALSE; - } - - if (settings->GatewayPassword) - { - free(settings->Password); - - if (!(settings->Password = _strdup(settings->GatewayPassword))) - return FALSE; - } - } - } } - - return TRUE; } static BOOL rdg_ntlm_init(rdpRdg* rdg, rdpTls* tls) diff --git a/libfreerdp/core/gateway/rpc_bind.c b/libfreerdp/core/gateway/rpc_bind.c index 517516f98..484098b9a 100644 --- a/libfreerdp/core/gateway/rpc_bind.c +++ b/libfreerdp/core/gateway/rpc_bind.c @@ -28,6 +28,7 @@ #include "rpc_client.h" #include "rpc_bind.h" +#include "../utils.h" #define TAG FREERDP_TAG("core.gateway.rpc") @@ -104,6 +105,7 @@ const p_uuid_t BTFN_UUID = { int rpc_send_bind_pdu(rdpRpc* rpc) { + auth_status rc; BOOL continueNeeded = FALSE; int status = -1; BYTE* buffer = NULL; @@ -112,7 +114,6 @@ int rpc_send_bind_pdu(rdpRpc* rpc) RpcClientCall* clientCall; p_cont_elem_t* p_cont_elem; rpcconn_bind_hdr_t* bind_pdu = NULL; - BOOL promptPassword = FALSE; rdpSettings* settings = rpc->settings; freerdp* instance = (freerdp*)settings->instance; RpcVirtualConnection* connection = rpc->VirtualConnection; @@ -125,46 +126,18 @@ int rpc_send_bind_pdu(rdpRpc* rpc) if (!rpc->ntlm) goto fail; - if ((!settings->GatewayPassword) || (!settings->GatewayUsername) || - (!strlen(settings->GatewayPassword)) || (!strlen(settings->GatewayUsername))) + rc = utils_authenticate_gateway(instance, GW_AUTH_RPC); + switch (rc) { - promptPassword = TRUE; - } - - if (promptPassword) - { - if (freerdp_shall_disconnect(instance)) - return -1; - - if (!instance->GatewayAuthenticate) - { + case AUTH_SUCCESS: + case AUTH_SKIP: + break; + case AUTH_NO_CREDENTIALS: freerdp_set_last_error_log(instance->context, FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); return 0; - } - else - { - BOOL proceed = - instance->GatewayAuthenticate(instance, &settings->GatewayUsername, - &settings->GatewayPassword, &settings->GatewayDomain); - - if (!proceed) - { - freerdp_set_last_error_log(instance->context, - FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); - return 0; - } - - if (settings->GatewayUseSameCredentials) - { - settings->Username = _strdup(settings->GatewayUsername); - settings->Domain = _strdup(settings->GatewayDomain); - settings->Password = _strdup(settings->GatewayPassword); - - if (!settings->Username || !settings->Domain || settings->Password) - goto fail; - } - } + default: + return -1; } if (!ntlm_client_init(rpc->ntlm, FALSE, settings->GatewayUsername, settings->GatewayDomain, diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 4d52b77a5..75cd25e4b 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -44,6 +44,7 @@ #include #include "nla.h" +#include "utils.h" #define TAG FREERDP_TAG("core.nla") @@ -530,15 +531,6 @@ void nla_identity_free(SEC_WINNT_AUTH_IDENTITY* identity) * @param credssp */ -static BOOL is_empty(const char* str) -{ - if (!str) - return TRUE; - if (strlen(str) == 0) - return TRUE; - return FALSE; -} - static int nla_client_init(rdpNla* nla) { char* spn; @@ -554,13 +546,14 @@ static int nla_client_init(rdpNla* nla) if (settings->RestrictedAdminModeRequired) settings->DisableCredentialsDelegation = TRUE; - if (is_empty(settings->Username) || - (is_empty(settings->Password) && is_empty((const char*)settings->RedirectionPassword))) + if (utils_str_is_empty(settings->Username) || + (utils_str_is_empty(settings->Password) && + utils_str_is_empty((const char*)settings->RedirectionPassword))) { PromptPassword = TRUE; } - if (PromptPassword && !is_empty(settings->Username)) + if (PromptPassword && !utils_str_is_empty(settings->Username)) { sam = SamOpen(NULL, TRUE); @@ -597,25 +590,17 @@ static int nla_client_init(rdpNla* nla) if (PromptPassword) { - if (freerdp_shall_disconnect(instance)) - return 0; - if (!instance->Authenticate) + switch (utils_authenticate(instance, AUTH_NLA, TRUE)) { - freerdp_set_last_error_log(instance->context, - FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); - return 0; - } - else - { - BOOL proceed = instance->Authenticate(instance, &settings->Username, - &settings->Password, &settings->Domain); - - if (!proceed) - { + case AUTH_SKIP: + case AUTH_SUCCESS: + break; + case AUTH_NO_CREDENTIALS: freerdp_set_last_error_log(instance->context, FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); return 0; - } + default: + return 0; } } diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 6dedf7a2e..73be5371b 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -53,6 +53,7 @@ #include "transport.h" #include "rdp.h" #include "proxy.h" +#include "utils.h" #define TAG FREERDP_TAG("core.transport") @@ -157,41 +158,20 @@ fail: return FALSE; } -static BOOL transport_prompt_for_password(rdpTransport* transport) -{ - rdpSettings* settings = transport->settings; - freerdp* instance = transport->context->instance; - - /* Ask for auth data if no or an empty username was specified or no password was given */ - if ((settings->Username == NULL || strlen(settings->Username) == 0) || - (settings->Password == NULL && settings->RedirectionPassword == NULL)) - { - /* If no callback is specified still continue connection */ - if (!instance->Authenticate) - return TRUE; - - if (!instance->Authenticate(instance, &settings->Username, &settings->Password, - &settings->Domain)) - { - freerdp_set_last_error_log(instance->context, - FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); - return FALSE; - } - } - - return TRUE; -} - BOOL transport_connect_rdp(rdpTransport* transport) { if (!transport) return FALSE; - if (!transport_prompt_for_password(transport)) - return FALSE; - - /* RDP encryption */ - return TRUE; + switch (utils_authenticate(transport->context->instance, AUTH_RDP, FALSE)) + { + case AUTH_SKIP: + case AUTH_SUCCESS: + case AUTH_NO_CREDENTIALS: + return TRUE; + default: + return FALSE; + } } BOOL transport_connect_tls(rdpTransport* transport) @@ -202,8 +182,15 @@ BOOL transport_connect_tls(rdpTransport* transport) /* Only prompt for password if we use TLS (NLA also calls this function) */ if (transport->settings->SelectedProtocol == PROTOCOL_SSL) { - if (!transport_prompt_for_password(transport)) - return FALSE; + switch (utils_authenticate(transport->context->instance, AUTH_TLS, FALSE)) + { + case AUTH_SKIP: + case AUTH_SUCCESS: + case AUTH_NO_CREDENTIALS: + break; + default: + return FALSE; + } } return IFCALLRESULT(FALSE, transport->io.TLSConnect, transport); diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c new file mode 100644 index 000000000..8b8579070 --- /dev/null +++ b/libfreerdp/core/utils.c @@ -0,0 +1,178 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Terminal Server Gateway (utils) + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include +#define TAG FREERDP_TAG("core.gateway.utils") + +#include "utils.h" + +BOOL utils_str_copy(const char* value, char** dst) +{ + WINPR_ASSERT(dst); + + free(*dst); + *dst = NULL; + if (!value) + return TRUE; + + (*dst) = _strdup(value); + return (*dst) != NULL; +} + +auth_status utils_authenticate_gateway(freerdp* instance, rdp_auth_reason reason) +{ + rdpSettings* settings; + rdpContext* context; + BOOL prompt = FALSE; + BOOL proceed; + + WINPR_ASSERT(instance); + WINPR_ASSERT(instance->context); + WINPR_ASSERT(instance->settings); + + settings = instance->settings; + context = instance->context; + + if (freerdp_shall_disconnect(instance)) + return AUTH_FAILED; + + if (!settings->GatewayPassword || !settings->GatewayUsername || + !strlen(settings->GatewayPassword) || !strlen(settings->GatewayUsername)) + prompt = TRUE; + + if (!prompt) + return AUTH_SKIP; + + if (!instance->GatewayAuthenticate && !instance->AuthenticateEx) + return AUTH_NO_CREDENTIALS; + + if (instance->AuthenticateEx) + proceed = + instance->AuthenticateEx(instance, &settings->GatewayUsername, + &settings->GatewayPassword, &settings->GatewayDomain, reason); + else + proceed = + instance->GatewayAuthenticate(instance, &settings->GatewayUsername, + &settings->GatewayPassword, &settings->GatewayDomain); + + if (!proceed) + return AUTH_NO_CREDENTIALS; + + if (!utils_sync_credentials(settings, FALSE)) + return AUTH_FAILED; + return AUTH_SUCCESS; +} + +auth_status utils_authenticate(freerdp* instance, rdp_auth_reason reason, BOOL override) +{ + rdpSettings* settings; + rdpContext* context; + BOOL prompt = !override; + BOOL proceed; + + WINPR_ASSERT(instance); + WINPR_ASSERT(instance->context); + WINPR_ASSERT(instance->settings); + + settings = instance->settings; + context = instance->context; + + if (freerdp_shall_disconnect(instance)) + return AUTH_FAILED; + + /* Ask for auth data if no or an empty username was specified or no password was given */ + if (utils_str_is_empty(settings->Username) || + (settings->Password == NULL && settings->RedirectionPassword == NULL)) + prompt = TRUE; + + if (!prompt) + return AUTH_SKIP; + + /* If no callback is specified still continue connection */ + if (!instance->Authenticate && !instance->AuthenticateEx) + return AUTH_NO_CREDENTIALS; + + if (instance->AuthenticateEx) + proceed = + instance->AuthenticateEx(instance, &settings->GatewayUsername, + &settings->GatewayPassword, &settings->GatewayDomain, reason); + else + proceed = instance->Authenticate(instance, &settings->GatewayUsername, + &settings->GatewayPassword, &settings->GatewayDomain); + + if (!proceed) + return AUTH_NO_CREDENTIALS; + + if (!instance->Authenticate(instance, &settings->Username, &settings->Password, + &settings->Domain)) + { + freerdp_set_last_error_log(instance->context, + FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS); + return FALSE; + } + + if (!utils_sync_credentials(settings, TRUE)) + return AUTH_FAILED; + return AUTH_SUCCESS; +} + +BOOL utils_sync_credentials(rdpSettings* settings, BOOL toGateway) +{ + WINPR_ASSERT(settings); + if (!settings->GatewayUseSameCredentials) + return TRUE; + + if (toGateway) + { + if (!utils_str_copy(settings->Username, &settings->GatewayUsername)) + return FALSE; + if (!utils_str_copy(settings->Domain, &settings->GatewayDomain)) + return FALSE; + if (!utils_str_copy(settings->Password, &settings->GatewayPassword)) + return FALSE; + } + else + { + if (!utils_str_copy(settings->GatewayUsername, &settings->Username)) + return FALSE; + if (!utils_str_copy(settings->GatewayDomain, &settings->Domain)) + return FALSE; + if (!utils_str_copy(settings->GatewayPassword, &settings->Password)) + return FALSE; + } + return TRUE; +} + +BOOL utils_str_is_empty(const char* str) +{ + if (!str) + return TRUE; + if (strlen(str) == 0) + return TRUE; + return FALSE; +} diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h new file mode 100644 index 000000000..0397f72b5 --- /dev/null +++ b/libfreerdp/core/utils.h @@ -0,0 +1,41 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Terminal Server Gateway (utils) + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_LIB_CORE_UTILS_H +#define FREERDP_LIB_CORE_UTILS_H + +#include +#include + +typedef enum +{ + AUTH_SUCCESS, + AUTH_SKIP, + AUTH_NO_CREDENTIALS, + AUTH_FAILED +} auth_status; +auth_status utils_authenticate_gateway(freerdp* instance, rdp_auth_reason reason); +auth_status utils_authenticate(freerdp* instance, rdp_auth_reason reason, BOOL override); +BOOL utils_sync_credentials(rdpSettings* settings, BOOL toGateway); + +BOOL utils_str_is_empty(const char* str); +BOOL utils_str_copy(const char* value, char** dst); + +#endif /* FREERDP_LIB_CORE_UTILS_H */