Implemented new AuthenticateEx callbacks.

This commit is contained in:
Armin Novak 2021-07-29 12:33:22 +02:00 committed by akallabeth
parent bc9ef8327e
commit 50e9d3adf9
9 changed files with 302 additions and 198 deletions

View File

@ -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

View File

@ -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

View File

@ -29,6 +29,8 @@
#include <winpr/dsparse.h>
#include <winpr/winhttp.h>
#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,

View File

@ -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)

View File

@ -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,

View File

@ -44,6 +44,7 @@
#include <winpr/registry.h>
#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;
}
}

View File

@ -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);

178
libfreerdp/core/utils.c Normal file
View File

@ -0,0 +1,178 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Terminal Server Gateway (utils)
*
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
* 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 <winpr/assert.h>
#include <freerdp/freerdp.h>
#include <freerdp/log.h>
#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;
}

41
libfreerdp/core/utils.h Normal file
View File

@ -0,0 +1,41 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Terminal Server Gateway (utils)
*
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
* 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 <winpr/winpr.h>
#include <freerdp/freerdp.h>
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 */