Support using smartcard for gateway authentication

This commit is contained in:
fifthdegree 2022-10-16 15:54:59 -04:00 committed by David Fort
parent e847f159a6
commit eb04eb0008
8 changed files with 108 additions and 42 deletions

View File

@ -518,7 +518,8 @@ BOOL client_cli_authenticate_ex(freerdp* instance, char** username, char** passw
return client_cli_authenticate_raw(instance, reason, username, password, domain);
}
BOOL client_cli_choose_smartcard(SmartcardCertInfo** cert_list, DWORD count, DWORD* choice)
BOOL client_cli_choose_smartcard(SmartcardCertInfo** cert_list, DWORD count, DWORD* choice,
BOOL gateway)
{
unsigned long answer;
char* p = NULL;
@ -545,7 +546,8 @@ BOOL client_cli_choose_smartcard(SmartcardCertInfo** cert_list, DWORD count, DWO
{
char input[10] = { 0 };
printf("\nChoose a smartcard to use (0 - %" PRIu32 "): ", count - 1);
printf("\nChoose a smartcard to use for %s (0 - %" PRIu32 "): ",
gateway ? "gateway authentication" : "logon", count - 1);
fflush(stdout);
if (!fgets(input, 10, stdin))
{

View File

@ -27,7 +27,7 @@ BOOL freerdp_smartcard_list(const rdpSettings* settings)
SmartcardCertInfo** certs = NULL;
DWORD i, count;
if (!smartcard_enumerateCerts(settings, &certs, &count))
if (!smartcard_enumerateCerts(settings, &certs, &count, FALSE))
return FALSE;
for (i = 0; i < count; i++)

View File

@ -139,7 +139,7 @@ extern "C"
char** domain, rdp_auth_reason reason);
FREERDP_API BOOL client_cli_choose_smartcard(SmartcardCertInfo** cert_list, DWORD count,
DWORD* choice);
DWORD* choice, BOOL gateway);
FREERDP_API void
freerdp_client_OnChannelConnectedEventHandler(void* context,

View File

@ -118,7 +118,8 @@ extern "C"
char** domain);
typedef BOOL (*pAuthenticateEx)(freerdp* instance, char** username, char** password,
char** domain, rdp_auth_reason reason);
typedef BOOL (*pChooseSmartcard)(SmartcardCertInfo** cert_list, DWORD count, DWORD* choice);
typedef BOOL (*pChooseSmartcard)(SmartcardCertInfo** cert_list, DWORD count, DWORD* choice,
BOOL gateway);
/** @brief Callback used if user interaction is required to accept
* an unknown certificate.

View File

@ -43,8 +43,9 @@ typedef struct SmartcardCertInfo_st
} SmartcardCertInfo;
FREERDP_API BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
DWORD* retCount);
FREERDP_API BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert);
DWORD* retCount, BOOL gateway);
FREERDP_API BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert,
BOOL gateway);
FREERDP_API void smartcardCertInfo_Free(SmartcardCertInfo* pscCert);
FREERDP_API void smartcardCertList_Free(SmartcardCertInfo** pscCert, DWORD count);

View File

@ -26,10 +26,12 @@
#include <winpr/print.h>
#include <winpr/stream.h>
#include <winpr/winsock.h>
#include <winpr/cred.h>
#include <freerdp/log.h>
#include <freerdp/error.h>
#include <freerdp/utils/ringbuffer.h>
#include <freerdp/utils/smartcardlogon.h>
#include "rdg.h"
#include "../credssp_auth.h"
@ -193,6 +195,8 @@ struct rdp_rdg
UINT16 extAuth;
UINT16 reserved2;
rdg_http_encoding_context transferEncoding;
SmartcardCertInfo* smartcard;
};
enum
@ -1550,11 +1554,11 @@ DWORD rdg_get_event_handles(rdpRdg* rdg, HANDLE* events, DWORD count)
return nCount;
}
static BOOL rdg_get_gateway_credentials(rdpContext* context)
static BOOL rdg_get_gateway_credentials(rdpContext* context, rdp_auth_reason reason)
{
freerdp* instance = context->instance;
auth_status rc = utils_authenticate_gateway(instance, GW_AUTH_RDG);
auth_status rc = utils_authenticate_gateway(instance, reason);
switch (rc)
{
case AUTH_SUCCESS:
@ -1581,17 +1585,54 @@ static BOOL rdg_auth_init(rdpRdg* rdg, rdpTls* tls)
if (!rdg->auth)
return FALSE;
if (!rdg_get_gateway_credentials(context))
return FALSE;
if (!credssp_auth_init(rdg->auth, AUTH_PKG, tls->Bindings))
return FALSE;
if (sspi_SetAuthIdentityA(&identity, settings->GatewayUsername, settings->GatewayDomain,
settings->GatewayPassword) < 0)
return FALSE;
if (freerdp_settings_get_bool(settings, FreeRDP_SmartcardLogon))
{
if (!smartcard_getCert(context, &rdg->smartcard, TRUE))
return FALSE;
if (!credssp_auth_setup_client(rdg->auth, "HTTP", settings->GatewayHostname, &identity, NULL))
if (!rdg_get_gateway_credentials(context, AUTH_SMARTCARD_PIN))
return FALSE;
#ifdef _WIN32
{
CERT_CREDENTIAL_INFO certInfo = { sizeof(CERT_CREDENTIAL_INFO), { 0 } };
LPSTR marshalledCredentials;
memcpy(certInfo.rgbHashOfCert, rdg->smartcard->sha1Hash,
sizeof(certInfo.rgbHashOfCert));
if (!CredMarshalCredentialA(CertCredential, &certInfo, &marshalledCredentials))
{
WLog_ERR(TAG, "error marshalling cert credentials");
return FALSE;
}
if (sspi_SetAuthIdentityA(&identity, marshalledCredentials, NULL,
settings->GatewayPassword) < 0)
return FALSE;
CredFree(marshalledCredentials);
}
#else
if (sspi_SetAuthIdentityA(&identity, settings->GatewayUsername, settings->GatewayDomain,
settings->GatewayPassword) < 0)
return FALSE;
#endif
}
else
{
if (!rdg_get_gateway_credentials(context, GW_AUTH_RDG))
return FALSE;
if (sspi_SetAuthIdentityA(&identity, settings->GatewayUsername, settings->GatewayDomain,
settings->GatewayPassword) < 0)
return FALSE;
}
if (!credssp_auth_setup_client(rdg->auth, "HTTP", settings->GatewayHostname, &identity,
rdg->smartcard ? rdg->smartcard->pkinitArgs : NULL))
{
sspi_FreeAuthIdentity(&identity);
return FALSE;
@ -2626,6 +2667,8 @@ void rdg_free(rdpRdg* rdg)
Stream_Free(rdg->transferEncoding.context.websocket.responseStreamBuffer, TRUE);
}
smartcardCertInfo_Free(rdg->smartcard);
free(rdg);
}

View File

@ -200,7 +200,7 @@ static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla)
smartcardCertInfo_Free(nla->smartcardCert);
if (!smartcard_getCert(nla->rdpcontext, &nla->smartcardCert))
if (!smartcard_getCert(nla->rdpcontext, &nla->smartcardCert, FALSE))
{
WLog_ERR(TAG, "unable to get smartcard certificate for logon");
return FALSE;
@ -221,24 +221,6 @@ static BOOL nla_adjust_settings_from_smartcard(rdpNla* nla)
}
}
if (!settings->Username && nla->smartcardCert->userHint)
{
if (!freerdp_settings_set_string(settings, FreeRDP_Username, nla->smartcardCert->userHint))
{
WLog_ERR(TAG, "unable to copy certificate username");
goto out;
}
}
if (!settings->Domain && nla->smartcardCert->domainHint)
{
if (!freerdp_settings_set_string(settings, FreeRDP_Domain, nla->smartcardCert->domainHint))
{
WLog_ERR(TAG, "unable to copy certificate domain");
goto out;
}
}
if (!settings->ReaderName && nla->smartcardCert->reader)
{
if (ConvertFromUnicode(CP_UTF8, 0, nla->smartcardCert->reader, -1, &settings->ReaderName, 0,

View File

@ -607,14 +607,25 @@ out_error:
}
BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo*** scCerts,
DWORD* retCount)
DWORD* retCount, BOOL gateway)
{
BOOL ret;
LPWSTR csp = NULL;
const char* ReaderName = freerdp_settings_get_string(settings, FreeRDP_ReaderName);
const char* Username = freerdp_settings_get_string(settings, FreeRDP_Username);
const char* Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
const char* CspName = freerdp_settings_get_string(settings, FreeRDP_CspName);
const char* Username;
const char* Domain;
if (gateway)
{
Username = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername);
Domain = freerdp_settings_get_string(settings, FreeRDP_GatewayDomain);
}
else
{
Username = freerdp_settings_get_string(settings, FreeRDP_Username);
Domain = freerdp_settings_get_string(settings, FreeRDP_Domain);
}
WINPR_ASSERT(settings);
WINPR_ASSERT(scCerts);
@ -638,19 +649,32 @@ BOOL smartcard_enumerateCerts(const rdpSettings* settings, SmartcardCertInfo***
return ret;
}
BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert)
static BOOL set_settings_from_smartcard(rdpSettings* settings, size_t id, const char* value)
{
WINPR_ASSERT(settings);
if (!freerdp_settings_get_string(settings, id) && value)
if (!freerdp_settings_set_string(settings, id, value))
return FALSE;
return TRUE;
}
BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert, BOOL gateway)
{
WINPR_ASSERT(context);
const freerdp* instance = context->instance;
const rdpSettings* settings = context->settings;
rdpSettings* settings = context->settings;
SmartcardCertInfo** cert_list;
DWORD count;
size_t username_setting;
size_t domain_setting;
WINPR_ASSERT(instance);
WINPR_ASSERT(settings);
if (!smartcard_enumerateCerts(settings, &cert_list, &count))
if (!smartcard_enumerateCerts(settings, &cert_list, &count, gateway))
return FALSE;
if (count < 1)
@ -663,7 +687,8 @@ BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert)
{
DWORD index;
if (!instance->ChooseSmartcard || !instance->ChooseSmartcard(cert_list, count, &index))
if (!instance->ChooseSmartcard ||
!instance->ChooseSmartcard(cert_list, count, &index, gateway))
{
WLog_ERR(TAG, "more than one suitable smartcard certificate was found");
smartcardCertList_Free(cert_list, count);
@ -679,6 +704,18 @@ BOOL smartcard_getCert(const rdpContext* context, SmartcardCertInfo** cert)
else
*cert = cert_list[0];
username_setting = gateway ? FreeRDP_GatewayUsername : FreeRDP_Username;
domain_setting = gateway ? FreeRDP_GatewayDomain : FreeRDP_Domain;
free(cert_list);
if (!set_settings_from_smartcard(settings, username_setting, (*cert)->userHint) ||
!set_settings_from_smartcard(settings, domain_setting, (*cert)->domainHint))
{
WLog_ERR(TAG, "unable to set settings from smartcard!");
smartcardCertInfo_Free(*cert);
return FALSE;
}
return TRUE;
}