Merge pull request #5085 from akallabeth/cert_callbacks_update

Refactored Certificate callbacks (but keep compatible)
This commit is contained in:
Martin Fleisz 2018-12-06 10:08:17 +01:00 committed by GitHub
commit 8c7f8eb395
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 565 additions and 326 deletions

View File

@ -107,11 +107,9 @@ static BOOL tf_pre_connect(freerdp* instance)
/* Optional OS identifier sent to server */
settings->OsMajorType = OSMAJORTYPE_UNIX;
settings->OsMinorType = OSMINORTYPE_NATIVE_XSERVER;
/* settings->OrderSupport is initialized at this point.
* Only override it if you plan to implement custom order
* callbacks or deactiveate certain features. */
/* Register the channel listeners.
* They are required to set up / tear down channels if they are loaded. */
PubSub_SubscribeChannelConnected(instance->context->pubSub,
@ -262,8 +260,8 @@ static BOOL tf_client_new(freerdp* instance, rdpContext* context)
instance->PostDisconnect = tf_post_disconnect;
instance->Authenticate = client_cli_authenticate;
instance->GatewayAuthenticate = client_cli_gw_authenticate;
instance->VerifyCertificate = client_cli_verify_certificate;
instance->VerifyChangedCertificate = client_cli_verify_changed_certificate;
instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
instance->LogonErrorInfo = tf_logon_error_info;
/* TODO: Client display set up */
return TRUE;

View File

@ -386,8 +386,8 @@ static BOOL wlf_client_new(freerdp* instance, rdpContext* context)
instance->PostDisconnect = wl_post_disconnect;
instance->Authenticate = client_cli_authenticate;
instance->GatewayAuthenticate = client_cli_gw_authenticate;
instance->VerifyCertificate = client_cli_verify_certificate;
instance->VerifyChangedCertificate = client_cli_verify_changed_certificate;
instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
instance->LogonErrorInfo = wlf_logon_error_info;
wfl->display = UwacOpenDisplay(NULL, &status);

View File

@ -1763,8 +1763,8 @@ static BOOL xfreerdp_client_new(freerdp* instance, rdpContext* context)
instance->PostDisconnect = xf_post_disconnect;
instance->Authenticate = client_cli_authenticate;
instance->GatewayAuthenticate = client_cli_gw_authenticate;
instance->VerifyCertificate = client_cli_verify_certificate;
instance->VerifyChangedCertificate = client_cli_verify_changed_certificate;
instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
instance->LogonErrorInfo = xf_logon_error_info;
PubSub_SubscribeTerminate(context->pubSub,
xf_TerminateEventHandler);

View File

@ -506,6 +506,7 @@ static DWORD client_cli_accept_certificate(rdpSettings* settings)
* when the connection requires it.
* This function will actually be called by tls_verify_certificate().
* @see rdp_client_connect() and tls_connect()
* @deprecated Use client_cli_verify_certificate_ex
* @param instance - pointer to the rdp_freerdp structure that contains the connection settings
* @param common_name
* @param subject
@ -518,6 +519,7 @@ DWORD client_cli_verify_certificate(freerdp* instance, const char* common_name,
const char* subject, const char* issuer,
const char* fingerprint, BOOL host_mismatch)
{
printf("WARNING: This callback is deprecated, migrate to client_cli_verify_certificate_ex\n");
printf("Certificate details:\n");
printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer);
@ -528,10 +530,50 @@ DWORD client_cli_verify_certificate(freerdp* instance, const char* common_name,
return client_cli_accept_certificate(instance->settings);
}
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
* when the connection requires it.
* This function will actually be called by tls_verify_certificate().
* @see rdp_client_connect() and tls_connect()
* @param instance pointer to the rdp_freerdp structure that contains the connection settings
* @param host The host currently connecting to
* @param port The port currently connecting to
* @param common_name The common name of the certificate, should match host or an alias of it
* @param subject The subject of the certificate
* @param issuer The certificate issuer name
* @param fingerprint The fingerprint of the certificate
* @param flags See VERIFY_CERT_FLAG_* for possible values.
*
* @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
*/
DWORD client_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
const char* common_name,
const char* subject, const char* issuer,
const char* fingerprint, DWORD flags)
{
const char* type = "RDP-Server";
if (flags & VERIFY_CERT_FLAG_GATEWAY)
type = "RDP-Gateway";
if (flags & VERIFY_CERT_FLAG_REDIRECT)
type = "RDP-Redirect";
printf("Certificate details for %s:%"PRIu16" (%s):\n", host, port, type);
printf("\tCommon Name: %s\n", common_name);
printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer);
printf("\tThumbprint: %s\n", fingerprint);
printf("The above X.509 certificate could not be verified, possibly because you do not have\n"
"the CA certificate in your certificate store, or the certificate has expired.\n"
"Please look at the OpenSSL documentation on how to add a private CA to the store.\n");
return client_cli_accept_certificate(instance->settings);
}
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
* when a stored certificate does not match the remote counterpart.
* This function will actually be called by tls_verify_certificate().
* @see rdp_client_connect() and tls_connect()
* @deprecated Use client_cli_verify_changed_certificate_ex
* @param instance - pointer to the rdp_freerdp structure that contains the connection settings
* @param common_name
* @param subject
@ -549,6 +591,7 @@ DWORD client_cli_verify_changed_certificate(freerdp* instance,
const char* old_subject, const char* old_issuer,
const char* old_fingerprint)
{
printf("WARNING: This callback is deprecated, migrate to client_cli_verify_changed_certificate_ex\n");
printf("!!! Certificate has changed !!!\n");
printf("\n");
printf("New Certificate details:\n");
@ -567,6 +610,59 @@ DWORD client_cli_verify_changed_certificate(freerdp* instance,
return client_cli_accept_certificate(instance->settings);
}
/** Callback set in the rdp_freerdp structure, and used to make a certificate validation
* when a stored certificate does not match the remote counterpart.
* This function will actually be called by tls_verify_certificate().
* @see rdp_client_connect() and tls_connect()
* @param instance pointer to the rdp_freerdp structure that contains the connection settings
* @param host The host currently connecting to
* @param port The port currently connecting to
* @param common_name The common name of the certificate, should match host or an alias of it
* @param subject The subject of the certificate
* @param issuer The certificate issuer name
* @param fingerprint The fingerprint of the certificate
* @param old_subject The subject of the previous certificate
* @param old_issuer The previous certificate issuer name
* @param old_fingerprint The fingerprint of the previous certificate
* @param flags See VERIFY_CERT_FLAG_* for possible values.
*
* @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise.
*/
DWORD client_cli_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
const char* common_name,
const char* subject, const char* issuer,
const char* fingerprint,
const char* old_subject, const char* old_issuer,
const char* old_fingerprint, DWORD flags)
{
const char* type = "RDP-Server";
if (flags & VERIFY_CERT_FLAG_GATEWAY)
type = "RDP-Gateway";
if (flags & VERIFY_CERT_FLAG_REDIRECT)
type = "RDP-Redirect";
printf("!!!Certificate for %s:%"PRIu16" (%s) has changed!!!\n", host, port, type);
printf("\n");
printf("New Certificate details:\n");
printf("\tCommon Name: %s\n", common_name);
printf("\tSubject: %s\n", subject);
printf("\tIssuer: %s\n", issuer);
printf("\tThumbprint: %s\n", fingerprint);
printf("\n");
printf("Old Certificate details:\n");
printf("\tSubject: %s\n", old_subject);
printf("\tIssuer: %s\n", old_issuer);
printf("\tThumbprint: %s\n", old_fingerprint);
printf("\n");
printf("The above X.509 certificate does not match the certificate used for previous connections.\n"
"This may indicate that the certificate has been tampered with.\n"
"Please contact the administrator of the RDP server and clarify.\n");
return client_cli_accept_certificate(instance->settings);
}
BOOL client_auto_reconnect(freerdp* instance)
{
return client_auto_reconnect_ex(instance, NULL);

View File

@ -107,11 +107,25 @@ FREERDP_API DWORD client_cli_verify_certificate(freerdp* instance, const char* c
const char* subject, const char* issuer,
const char* fingerprint, BOOL host_mismatch);
FREERDP_API DWORD client_cli_verify_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
const char* common_name,
const char* subject, const char* issuer,
const char* fingerprint, DWORD flags);
FREERDP_API DWORD client_cli_verify_changed_certificate(freerdp* instance, const char* common_name,
const char* subject, const char* issuer,
const char* fingerprint,
const char* old_subject, const char* old_issuer,
const char* old_fingerprint);
FREERDP_API DWORD client_cli_verify_changed_certificate_ex(freerdp* instance,
const char* host, UINT16 port,
const char* common_name,
const char* subject, const char* issuer,
const char* fingerprint,
const char* old_subject, const char* old_issuer,
const char* old_fingerprint, DWORD flags);
FREERDP_API BOOL client_auto_reconnect(freerdp* instance);
FREERDP_API BOOL client_auto_reconnect_ex(freerdp* instance,
BOOL(*window_events)(freerdp* instance));

View File

@ -51,35 +51,35 @@ struct rdp_certificate_store
};
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
FREERDP_API rdpCertificateData* certificate_data_new(
char* hostname, UINT16 port, char* subject,
char* issuer, char* fingerprint);
const char* hostname, UINT16 port, const char* subject,
const char* issuer, const char* fingerprint);
FREERDP_API void certificate_data_free(
rdpCertificateData* certificate_data);
rdpCertificateData* certificate_data);
FREERDP_API rdpCertificateStore* certificate_store_new(
rdpSettings* settings);
rdpSettings* settings);
FREERDP_API BOOL certificate_data_replace(
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
FREERDP_API void certificate_store_free(
rdpCertificateStore* certificate_store);
rdpCertificateStore* certificate_store);
FREERDP_API int certificate_data_match(
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
FREERDP_API BOOL certificate_data_print(
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
FREERDP_API BOOL certificate_get_stored_data(
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data,
char** subject, char** issuer,
char** fingerprint);
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data,
char** subject, char** issuer,
char** fingerprint);
#ifdef __cplusplus
}
}
#endif
#endif /* FREERDP_CRYPTO_CERTIFICATE_H */

View File

@ -72,10 +72,10 @@ Note: email and upn amongst others are also alt_names,
but the old crypto_cert_get_alt_names returned only the dns_names
*/
FREERDP_API char** crypto_cert_subject_alt_name(X509* xcert, int* count, int** lengths);
FREERDP_API void crypto_cert_subject_alt_name_free(int count, int *lengths, char** alt_names);
FREERDP_API void crypto_cert_subject_alt_name_free(int count, int* lengths, char** alt_names);
FREERDP_API BOOL x509_verify_certificate(CryptoCert cert, char* certificate_store_path);
FREERDP_API rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname,
FREERDP_API BOOL x509_verify_certificate(CryptoCert cert, const char* certificate_store_path);
FREERDP_API rdpCertificateData* crypto_get_certificate_data(X509* xcert, const char* hostname,
UINT16 port);
FREERDP_API BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey,
DWORD* PublicKeyLength);

View File

@ -96,16 +96,6 @@ FREERDP_API int tls_write_all(rdpTls* tls, const BYTE* data, int length);
FREERDP_API int tls_set_alert_code(rdpTls* tls, int level, int description);
FREERDP_API BOOL tls_match_hostname(char* pattern, int pattern_length, char* hostname);
FREERDP_API int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port);
FREERDP_API void tls_print_certificate_error(char* hostname, UINT16 port,
char* fingerprint, char* hosts_file);
FREERDP_API void tls_print_certificate_name_mismatch_error(
char* hostname, UINT16 port, char* common_name, char** alt_names,
int alt_names_count);
FREERDP_API BOOL tls_print_error(char* func, SSL* connection, int value);
FREERDP_API rdpTls* tls_new(rdpSettings* settings);
FREERDP_API void tls_free(rdpTls* tls);

View File

@ -59,6 +59,14 @@ typedef RDP_CLIENT_ENTRY_POINTS_V1 RDP_CLIENT_ENTRY_POINTS;
extern "C" {
#endif
/* Flags used by certificate callbacks */
#define VERIFY_CERT_FLAG_NONE 0x00
#define VERIFY_CERT_FLAG_LEGACY 0x02
#define VERIFY_CERT_FLAG_REDIRECT 0x10
#define VERIFY_CERT_FLAG_GATEWAY 0x20
#define VERIFY_CERT_FLAG_CHANGED 0x40
#define VERIFY_CERT_FLAG_MISMATCH 0x80
typedef BOOL (*pContextNew)(freerdp* instance, rdpContext* context);
typedef void (*pContextFree)(freerdp* instance, rdpContext* context);
@ -71,6 +79,7 @@ typedef BOOL (*pAuthenticate)(freerdp* instance, char** username,
/** @brief Callback used if user interaction is required to accept
* an unknown certificate.
*
* @deprecated Use pVerifyCertificateEx
* @param common_name The certificate registered hostname.
* @param subject The common name of the certificate.
* @param issuer The issuer of the certificate.
@ -88,9 +97,33 @@ typedef DWORD (*pVerifyCertificate)(freerdp* instance,
const char* fingerprint,
BOOL host_mismatch);
/** @brief Callback used if user interaction is required to accept
* an unknown certificate.
*
* @param host The hostname connecting to.
* @param port The port connecting to.
* @param common_name The certificate registered hostname.
* @param subject The common name of the certificate.
* @param issuer The issuer of the certificate.
* @param fingerprint The fingerprint of the certificate.
* @param flags Flags of type VERIFY_CERT_FLAG*
*
* @return 1 to accept and store a certificate, 2 to accept
* a certificate only for this session, 0 otherwise.
*/
typedef DWORD (*pVerifyCertificateEx)(freerdp* instance,
const char* host,
UINT16 port,
const char* common_name,
const char* subject,
const char* issuer,
const char* fingerprint,
DWORD flags);
/** @brief Callback used if user interaction is required to accept
* a changed certificate.
*
* @deprecated Use pVerifyChangedCertificateEx
* @param common_name The certificate registered hostname.
* @param subject The common name of the new certificate.
* @param issuer The issuer of the new certificate.
@ -111,9 +144,53 @@ typedef DWORD (*pVerifyChangedCertificate)(freerdp* instance,
const char* old_subject,
const char* old_issuer,
const char* old_fingerprint);
typedef int (*pVerifyX509Certificate)(freerdp* instance, BYTE* data,
int length, const char* hostname,
int port, DWORD flags);
/** @brief Callback used if user interaction is required to accept
* a changed certificate.
*
* @param host The hostname connecting to.
* @param port The port connecting to.
* @param common_name The certificate registered hostname.
* @param subject The common name of the new certificate.
* @param issuer The issuer of the new certificate.
* @param fingerprint The fingerprint of the new certificate.
* @param old_subject The common name of the old certificate.
* @param old_issuer The issuer of the new certificate.
* @param old_fingerprint The fingerprint of the old certificate.
* @param flags Flags of type VERIFY_CERT_FLAG*
*
* @return 1 to accept and store a certificate, 2 to accept
* a certificate only for this session, 0 otherwise.
*/
typedef DWORD (*pVerifyChangedCertificateEx)(freerdp* instance,
const char* host,
UINT16 port,
const char* common_name,
const char* subject,
const char* issuer,
const char* new_fingerprint,
const char* old_subject,
const char* old_issuer,
const char* old_fingerprint,
DWORD flags);
/** @brief Callback used if user interaction is required to accept
* a certificate.
*
* @param instance Pointer to the freerdp instance.
* @param data Pointer to certificate data in PEM format.
* @param length The length of the certificate data.
* @param hostname The hostname connecting to.
* @param port The port connecting to.
* @param flags Flags of type VERIFY_CERT_FLAG*
*
* @return 1 to accept and store a certificate, 2 to accept
* a certificate only for this session, 0 otherwise.
*/
typedef int (*pVerifyX509Certificate)(freerdp* instance, const BYTE* data,
size_t length, const char* hostname,
UINT16 port, DWORD flags);
typedef int (*pLogonErrorInfo)(freerdp* instance, UINT32 data, UINT32 type);
@ -274,11 +351,12 @@ struct rdp_freerdp
It is used to get the username/password when it was not provided at connection time. */
ALIGN64 pVerifyCertificate VerifyCertificate; /**< (offset 51)
Callback for certificate validation.
Used to verify that an unknown certificate is trusted. */
Used to verify that an unknown certificate is trusted.
DEPRECATED: Use VerifyChangedCertificateEx*/
ALIGN64 pVerifyChangedCertificate VerifyChangedCertificate; /**< (offset 52)
Callback for changed certificate validation.
Used when a certificate differs from stored fingerprint.
If returns TRUE, the new fingerprint will be trusted and old thrown out. */
DEPRECATED: Use VerifyChangedCertificateEx */
ALIGN64 pVerifyX509Certificate
VerifyX509Certificate; /**< (offset 53) Callback for X509 certificate verification (PEM format) */
@ -305,7 +383,13 @@ struct rdp_freerdp
This is called by freerdp_channel_process() (if not NULL).
Clients will typically use a function that calls freerdp_channels_data() to perform the needed tasks. */
UINT64 paddingE[80 - 66]; /* 66 */
ALIGN64 pVerifyCertificateEx VerifyCertificateEx; /**< (offset 66)
Callback for certificate validation.
Used to verify that an unknown certificate is trusted. */
ALIGN64 pVerifyChangedCertificateEx VerifyChangedCertificateEx; /**< (offset 67)
Callback for changed certificate validation.
Used when a certificate differs from stored fingerprint. */
UINT64 paddingE[80 - 68]; /* 68 */
};
struct rdp_channel_handles

View File

@ -46,23 +46,25 @@ static const char certificate_legacy_hosts_file[] = "known_hosts";
#define TAG FREERDP_TAG("crypto")
static BOOL certificate_split_line(char* line, char** host, UINT16* port,
char**subject, char**issuer,
char** fingerprint);
char** subject, char** issuer,
char** fingerprint);
static BOOL certificate_line_is_comment(const char* line, size_t length)
{
while(length > 0)
while (length > 0)
{
switch(*line)
switch (*line)
{
case ' ':
case '\t':
line++;
length--;
break;
case '#':
return TRUE;
default:
return FALSE;
case ' ':
case '\t':
line++;
length--;
break;
case '#':
return TRUE;
default:
return FALSE;
}
}
@ -76,7 +78,6 @@ BOOL certificate_store_init(rdpCertificateStore* certificate_store)
{
char* server_path = NULL;
rdpSettings* settings;
settings = certificate_store->settings;
if (!PathFileExistsA(settings->ConfigPath))
@ -86,10 +87,12 @@ BOOL certificate_store_init(rdpCertificateStore* certificate_store)
WLog_ERR(TAG, "error creating directory '%s'", settings->ConfigPath);
goto fail;
}
WLog_INFO(TAG, "creating directory %s", settings->ConfigPath);
}
if (!(certificate_store->path = GetCombinedPath(settings->ConfigPath, (char*) certificate_store_dir)))
if (!(certificate_store->path = GetCombinedPath(settings->ConfigPath,
(char*) certificate_store_dir)))
goto fail;
if (!PathFileExistsA(certificate_store->path))
@ -99,6 +102,7 @@ BOOL certificate_store_init(rdpCertificateStore* certificate_store)
WLog_ERR(TAG, "error creating directory [%s]", certificate_store->path);
goto fail;
}
WLog_INFO(TAG, "creating directory [%s]", certificate_store->path);
}
@ -112,20 +116,20 @@ BOOL certificate_store_init(rdpCertificateStore* certificate_store)
WLog_ERR(TAG, "error creating directory [%s]", server_path);
goto fail;
}
WLog_INFO(TAG, "created directory [%s]", server_path);
}
if (!(certificate_store->file = GetCombinedPath(settings->ConfigPath, (char*) certificate_known_hosts_file)))
if (!(certificate_store->file = GetCombinedPath(settings->ConfigPath,
(char*) certificate_known_hosts_file)))
goto fail;
if (!(certificate_store->legacy_file = GetCombinedPath(settings->ConfigPath,
(char*) certificate_legacy_hosts_file)))
(char*) certificate_legacy_hosts_file)))
goto fail;
free(server_path);
return TRUE;
fail:
WLog_ERR(TAG, "certificate store initialization failed");
free(server_path);
@ -137,7 +141,7 @@ fail:
}
static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
rdpCertificateData* certificate_data)
{
HANDLE fp;
int match = 1;
@ -149,12 +153,11 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
UINT64 size;
size_t length;
DWORD read;
/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
PathCchConvertStyleA(certificate_store->legacy_file, strlen(certificate_store->legacy_file), PATH_STYLE_UNIX);
PathCchConvertStyleA(certificate_store->legacy_file, strlen(certificate_store->legacy_file),
PATH_STYLE_UNIX);
fp = CreateFileA(certificate_store->legacy_file, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fp == INVALID_HANDLE_VALUE)
return match;
@ -162,10 +165,11 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
if ((lowSize = GetFileSize(fp, &highSize)) == INVALID_FILE_SIZE)
{
WLog_ERR(TAG, "GetFileSize(%s) returned %s [0x%08"PRIX32"]",
certificate_store->legacy_file, strerror(errno), GetLastError());
certificate_store->legacy_file, strerror(errno), GetLastError());
CloseHandle(fp);
return match;
}
size = (UINT64)lowSize | ((UINT64)highSize << 32);
if (size < 1)
@ -175,6 +179,7 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
}
mdata = (char*) malloc(size + 2);
if (!mdata)
{
CloseHandle(fp);
@ -182,6 +187,7 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
}
data = mdata;
if (!ReadFile(fp, data, size, &read, NULL) || (read != size))
{
free(data);
@ -190,7 +196,6 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
}
CloseHandle(fp);
data[size] = '\n';
data[size + 1] = '\0';
pline = StrSep(&data, "\r\n");
@ -202,13 +207,13 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
if (length > 0)
{
hostname = StrSep(&pline, " \t");
if (!hostname || !pline)
WLog_WARN(TAG, "Invalid %s entry %s %s!", certificate_legacy_hosts_file,
hostname, pline);
hostname, pline);
else if (strcmp(hostname, certificate_data->hostname) == 0)
{
const int diff = strcmp(pline, certificate_data->fingerprint);
match = (diff == 0) ? 0 : -1;
break;
}
@ -222,44 +227,49 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
if (0 == match)
{
rdpCertificateData* data = certificate_data_new(
hostname,
certificate_data->port,
NULL, NULL,
certificate_data->fingerprint);
hostname,
certificate_data->port,
NULL, NULL,
certificate_data->fingerprint);
if (data)
{
free (data->subject);
free (data->issuer);
free(data->subject);
free(data->issuer);
data->subject = NULL;
data->issuer = NULL;
if (certificate_data->subject)
{
data->subject = _strdup(certificate_data->subject);
if (!data->subject)
goto out;
}
if (certificate_data->issuer)
{
data->issuer = _strdup(certificate_data->issuer);
if (!data->issuer)
goto out;
}
match = certificate_data_print(certificate_store, data) ? 0 : 1;
}
out:
out:
certificate_data_free(data);
}
free(mdata);
return match;
}
static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data,
char** psubject, char** pissuer,
char** fprint)
rdpCertificateData* certificate_data,
char** psubject, char** pissuer,
char** fprint)
{
BOOL found = FALSE;
HANDLE fp;
@ -276,11 +286,10 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
char* fingerprint = NULL;
unsigned short port = 0;
DWORD read;
/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
PathCchConvertStyleA(certificate_store->file, strlen(certificate_store->file), PATH_STYLE_UNIX);
fp = CreateFileA(certificate_store->file, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL, NULL);
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NORMAL, NULL);
if (fp == INVALID_HANDLE_VALUE)
return match;
@ -288,10 +297,11 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
if ((lowSize = GetFileSize(fp, &highSize)) == INVALID_FILE_SIZE)
{
WLog_ERR(TAG, "GetFileSize(%s) returned %s [0x%08"PRIX32"]",
certificate_store->legacy_file, strerror(errno), GetLastError());
certificate_store->legacy_file, strerror(errno), GetLastError());
CloseHandle(fp);
return match;
}
size = (UINT64)lowSize | ((UINT64)highSize << 32);
if (size < 1)
@ -301,6 +311,7 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
}
mdata = (char*) malloc(size + 2);
if (!mdata)
{
CloseHandle(fp);
@ -308,14 +319,15 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
}
data = mdata;
if (!ReadFile(fp, data, size, &read, NULL) || (read != size))
{
free(data);
CloseHandle(fp);
return match;
}
CloseHandle(fp);
CloseHandle(fp);
data[size] = '\n';
data[size + 1] = '\0';
pline = StrSep(&data, "\r\n");
@ -330,9 +342,9 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
{
}
else if (!certificate_split_line(pline, &hostname, &port,
&subject, &issuer, &fingerprint))
&subject, &issuer, &fingerprint))
WLog_WARN(TAG, "Invalid %s entry %s!",
certificate_known_hosts_file, pline);
certificate_known_hosts_file, pline);
else if (strcmp(pline, certificate_data->hostname) == 0)
{
int outLen;
@ -340,16 +352,21 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
if (port == certificate_data->port)
{
found = TRUE;
if (fingerprint)
{
match = (strcmp(certificate_data->fingerprint, fingerprint) == 0) ? 0 : -1;
if (fprint)
*fprint = _strdup(fingerprint);
}
if (subject && psubject)
crypto_base64_decode(subject, strlen(subject), (BYTE**)psubject, &outLen);
if (issuer && pissuer)
crypto_base64_decode(issuer, strlen(issuer), (BYTE**)pissuer, &outLen);
break;
}
}
@ -357,6 +374,7 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
pline = StrSep(&data, "\r\n");
}
free(mdata);
if ((match != 0) && !found)
@ -366,27 +384,28 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
}
BOOL certificate_get_stored_data(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data,
char** subject, char** issuer,
char** fingerprint)
rdpCertificateData* certificate_data,
char** subject, char** issuer,
char** fingerprint)
{
int rc = certificate_data_match_raw(certificate_store, certificate_data,
subject, issuer, fingerprint);
subject, issuer, fingerprint);
if ((rc == 0) || (rc == -1))
return TRUE;
return FALSE;
}
int certificate_data_match(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
rdpCertificateData* certificate_data)
{
return certificate_data_match_raw(certificate_store, certificate_data,
NULL, NULL, NULL);
NULL, NULL, NULL);
}
BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
rdpCertificateData* certificate_data)
{
HANDLE fp;
BOOL rc = FALSE;
@ -397,11 +416,10 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
UINT64 size;
DWORD read, written;
DWORD lowSize, highSize;
/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
PathCchConvertStyleA(certificate_store->file, strlen(certificate_store->file), PATH_STYLE_UNIX);
fp = CreateFileA(certificate_store->file, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fp == INVALID_HANDLE_VALUE)
return FALSE;
@ -409,10 +427,11 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
if ((lowSize = GetFileSize(fp, &highSize)) == INVALID_FILE_SIZE)
{
WLog_ERR(TAG, "GetFileSize(%s) returned %s [0x%08"PRIX32"]",
certificate_store->legacy_file, strerror(errno), GetLastError());
certificate_store->legacy_file, strerror(errno), GetLastError());
CloseHandle(fp);
return FALSE;
}
size = (UINT64)lowSize | ((UINT64)highSize << 32);
if (size < 1)
@ -422,6 +441,7 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
}
data = (char*) malloc(size + 2);
if (!data)
{
fclose(fp);
@ -438,7 +458,7 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
if (SetFilePointer(fp, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
WLog_ERR(TAG, "SetFilePointer(%s) returned %s [0x%08"PRIX32"]",
certificate_store->file, strerror(errno), GetLastError());
certificate_store->file, strerror(errno), GetLastError());
free(data);
CloseHandle(fp);
return FALSE;
@ -447,7 +467,7 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
if (!SetEndOfFile(fp))
{
WLog_ERR(TAG, "SetEndOfFile(%s) returned %s [0x%08"PRIX32"]",
certificate_store->file, strerror(errno), GetLastError());
certificate_store->file, strerror(errno), GetLastError());
free(data);
CloseHandle(fp);
return FALSE;
@ -477,12 +497,12 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
}
else if (!certificate_split_line(pline, &hostname, &port, &subject, &issuer, &fingerprint))
WLog_WARN(TAG, "Skipping invalid %s entry %s!",
certificate_known_hosts_file, pline);
certificate_known_hosts_file, pline);
else
{
/* If this is the replaced hostname, use the updated fingerprint. */
if ((strcmp(hostname, certificate_data->hostname) == 0) &&
(port == certificate_data->port))
(port == certificate_data->port))
{
fingerprint = certificate_data->fingerprint;
rc = TRUE;
@ -490,33 +510,37 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
size = _snprintf(NULL, 0, "%s %"PRIu16" %s %s %s\n", hostname, port, fingerprint, subject, issuer);
tdata = malloc(size + 1);
if (!tdata)
{
WLog_ERR(TAG, "malloc(%s) returned %s [0x%08X]",
certificate_store->file, strerror(errno), errno);
certificate_store->file, strerror(errno), errno);
free(data);
CloseHandle(fp);
return FALSE;
}
if (_snprintf(tdata, size + 1, "%s %"PRIu16" %s %s %s\n", hostname, port, fingerprint, subject, issuer) != size)
if (_snprintf(tdata, size + 1, "%s %"PRIu16" %s %s %s\n", hostname, port, fingerprint, subject,
issuer) != size)
{
WLog_ERR(TAG, "_snprintf(%s) returned %s [0x%08X]",
certificate_store->file, strerror(errno), errno);
certificate_store->file, strerror(errno), errno);
free(tdata);
free(data);
CloseHandle(fp);
return FALSE;
}
if (!WriteFile(fp, tdata, size, &written, NULL) || (written != size))
{
WLog_ERR(TAG, "WriteFile(%s) returned %s [0x%08X]",
certificate_store->file, strerror(errno), errno);
certificate_store->file, strerror(errno), errno);
free(tdata);
free(data);
CloseHandle(fp);
return FALSE;
}
free(tdata);
}
}
@ -526,64 +550,65 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
CloseHandle(fp);
free(data);
return rc;
}
BOOL certificate_split_line(char* line, char** host, UINT16* port, char** subject,
char** issuer, char** fingerprint)
char** issuer, char** fingerprint)
{
char* cur;
size_t length = strlen(line);
char* cur;
size_t length = strlen(line);
if (length <= 0)
return FALSE;
if (length <= 0)
return FALSE;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*host = cur;
cur = StrSep(&line, " \t");
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
if (!cur)
return FALSE;
if(sscanf(cur, "%hu", port) != 1)
return FALSE;
*host = cur;
cur = StrSep(&line, " \t");
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
if (!cur)
return FALSE;
*fingerprint = cur;
if (sscanf(cur, "%hu", port) != 1)
return FALSE;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
cur = StrSep(&line, " \t");
*subject = cur;
if (!cur)
return FALSE;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*fingerprint = cur;
cur = StrSep(&line, " \t");
*issuer = cur;
if (!cur)
return FALSE;
return TRUE;
*subject = cur;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*issuer = cur;
return TRUE;
}
BOOL certificate_data_print(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
BOOL certificate_data_print(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
{
HANDLE fp;
char* tdata;
UINT64 size;
DWORD written;
/* reopen in append mode */
/* Assure POSIX style paths, CreateFile expects either '/' or '\\' */
PathCchConvertStyleA(certificate_store->file, strlen(certificate_store->file), PATH_STYLE_UNIX);
fp = CreateFileA(certificate_store->file, GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fp == INVALID_HANDLE_VALUE)
return FALSE;
@ -591,48 +616,53 @@ BOOL certificate_data_print(rdpCertificateStore* certificate_store, rdpCertifica
if (SetFilePointer(fp, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
{
WLog_ERR(TAG, "SetFilePointer(%s) returned %s [0x%08"PRIX32"]",
certificate_store->file, strerror(errno), GetLastError());
certificate_store->file, strerror(errno), GetLastError());
CloseHandle(fp);
return FALSE;
}
size = _snprintf(NULL, 0, "%s %"PRIu16" %s %s %s\n", certificate_data->hostname, certificate_data->port,
certificate_data->fingerprint, certificate_data->subject,
certificate_data->issuer);
size = _snprintf(NULL, 0, "%s %"PRIu16" %s %s %s\n", certificate_data->hostname,
certificate_data->port,
certificate_data->fingerprint, certificate_data->subject,
certificate_data->issuer);
tdata = malloc(size + 1);
if (!tdata)
{
WLog_ERR(TAG, "malloc(%s) returned %s [0x%08X]",
certificate_store->file, strerror(errno), errno);
certificate_store->file, strerror(errno), errno);
CloseHandle(fp);
return FALSE;
}
if (_snprintf(tdata, size + 1, "%s %"PRIu16" %s %s %s\n", certificate_data->hostname, certificate_data->port,
certificate_data->fingerprint, certificate_data->subject,
certificate_data->issuer) != size)
if (_snprintf(tdata, size + 1, "%s %"PRIu16" %s %s %s\n", certificate_data->hostname,
certificate_data->port,
certificate_data->fingerprint, certificate_data->subject,
certificate_data->issuer) != size)
{
WLog_ERR(TAG, "_snprintf(%s) returned %s [0x%08X]",
certificate_store->file, strerror(errno), errno);
certificate_store->file, strerror(errno), errno);
free(tdata);
CloseHandle(fp);
return FALSE;
}
if (!WriteFile(fp, tdata, size, &written, NULL) || (written != size))
{
WLog_ERR(TAG, "WriteFile(%s) returned %s [0x%08X]",
certificate_store->file, strerror(errno), errno);
certificate_store->file, strerror(errno), errno);
free(tdata);
CloseHandle(fp);
return FALSE;
}
free(tdata);
CloseHandle(fp);
return TRUE;
}
rdpCertificateData* certificate_data_new(char* hostname, UINT16 port, char* subject, char* issuer, char* fingerprint)
rdpCertificateData* certificate_data_new(const char* hostname, UINT16 port, const char* subject,
const char* issuer, const char* fingerprint)
{
size_t i;
rdpCertificateData* certdata;
@ -643,31 +673,34 @@ rdpCertificateData* certificate_data_new(char* hostname, UINT16 port, char* subj
if (!fingerprint)
return NULL;
certdata = (rdpCertificateData *)calloc(1, sizeof(rdpCertificateData));
certdata = (rdpCertificateData*)calloc(1, sizeof(rdpCertificateData));
if (!certdata)
return NULL;
certdata->port = port;
certdata->hostname = _strdup(hostname);
if (subject)
certdata->subject = crypto_base64_encode((BYTE*)subject, strlen(subject));
else
certdata->subject = crypto_base64_encode((BYTE*)"", 0);
if (issuer)
certdata->issuer = crypto_base64_encode((BYTE*)issuer, strlen(issuer));
else
certdata->issuer = crypto_base64_encode((BYTE*)"", 0);
certdata->fingerprint = _strdup(fingerprint);
if (!certdata->hostname || !certdata->subject ||
!certdata->issuer || !certdata->fingerprint)
!certdata->issuer || !certdata->fingerprint)
goto fail;
for (i=0; i<strlen(hostname); i++)
for (i = 0; i < strlen(hostname); i++)
certdata->hostname[i] = tolower(certdata->hostname[i]);
return certdata;
fail:
free(certdata->hostname);
free(certdata->subject);
@ -692,7 +725,6 @@ void certificate_data_free(rdpCertificateData* certificate_data)
rdpCertificateStore* certificate_store_new(rdpSettings* settings)
{
rdpCertificateStore* certificate_store;
certificate_store = (rdpCertificateStore*) calloc(1, sizeof(rdpCertificateStore));
if (!certificate_store)

View File

@ -754,9 +754,8 @@ char* crypto_cert_issuer(X509* xcert)
return crypto_print_name(X509_get_issuer_name(xcert));
}
BOOL x509_verify_certificate(CryptoCert cert, char* certificate_store_path)
BOOL x509_verify_certificate(CryptoCert cert, const char* certificate_store_path)
{
X509_VERIFY_PARAM* verify_param;
X509_STORE_CTX* csc;
BOOL status = FALSE;
X509_STORE* cert_ctx = NULL;
@ -812,7 +811,7 @@ end:
return status;
}
rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname, UINT16 port)
rdpCertificateData* crypto_get_certificate_data(X509* xcert, const char* hostname, UINT16 port)
{
char* issuer;
char* subject;

View File

@ -79,6 +79,13 @@ struct _BIO_RDP_TLS
};
typedef struct _BIO_RDP_TLS BIO_RDP_TLS;
static int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, UINT16 port);
static void tls_print_certificate_name_mismatch_error(const char* hostname, UINT16 port,
const char* common_name, char** alt_names,
int alt_names_count);
static void tls_print_certificate_error(const char* hostname, UINT16 port, const char* fingerprint,
const char* hosts_file);
static int bio_rdp_tls_write(BIO* bio, const char* buf, int size)
{
int error;
@ -654,7 +661,6 @@ static BOOL tls_prepare(rdpTls* tls, BIO* underlying, SSL_METHOD* method,
SSL_CTX_set_min_proto_version(tls->ctx, TLS1_VERSION); /* min version */
SSL_CTX_set_max_proto_version(tls->ctx, 0); /* highest supported version by library */
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
SSL_CTX_set_security_level(tls->ctx, settings->TlsSecLevel);
#endif
@ -844,7 +850,6 @@ int tls_connect(rdpTls* tls, BIO* underlying)
* support empty fragments. This needs to be disabled.
*/
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
/**
* disable SSLv2 and SSLv3
@ -1098,7 +1103,8 @@ int tls_set_alert_code(rdpTls* tls, int level, int description)
return 0;
}
BOOL tls_match_hostname(char* pattern, int pattern_length, char* hostname)
static BOOL tls_match_hostname(const char* pattern, const size_t pattern_length,
const char* hostname)
{
if (strlen(hostname) == pattern_length)
{
@ -1107,9 +1113,9 @@ BOOL tls_match_hostname(char* pattern, int pattern_length, char* hostname)
}
if ((pattern_length > 2) && (pattern[0] == '*') && (pattern[1] == '.')
&& (((int) strlen(hostname)) >= pattern_length))
&& ((strlen(hostname)) >= pattern_length))
{
char* check_hostname = &hostname[strlen(hostname) - pattern_length + 1];
const char* check_hostname = &hostname[strlen(hostname) - pattern_length + 1];
if (_strnicmp(check_hostname, &pattern[1], pattern_length - 1) == 0)
{
@ -1183,23 +1189,23 @@ static BOOL is_accepted(rdpTls* tls, const BYTE* pem, size_t length)
return FALSE;
}
static BOOL accept_cert(rdpTls* tls, BYTE* pem, UINT32 length)
static BOOL accept_cert(rdpTls* tls, const BYTE* pem, UINT32 length)
{
rdpSettings* settings = tls->settings;
if (tls->isGatewayTransport)
{
settings->GatewayAcceptedCert = (char*)pem;
settings->GatewayAcceptedCert = _strdup(pem);
settings->GatewayAcceptedCertLength = length;
}
else if (is_redirected(tls))
{
settings->RedirectionAcceptedCert = (char*)pem;
settings->RedirectionAcceptedCert = _strdup(pem);
settings->RedirectionAcceptedCertLength = length;
}
else
{
settings->AcceptedCert = (char*)pem;
settings->AcceptedCert = _strdup(pem);
settings->AcceptedCertLength = length;
}
@ -1299,8 +1305,8 @@ fail:
return rc;
}
int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
int port)
int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname,
UINT16 port)
{
int match;
int index;
@ -1312,183 +1318,209 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
BOOL certificate_status;
BOOL hostname_match = FALSE;
BOOL verification_status = FALSE;
rdpCertificateData* certificate_data;
rdpCertificateData* certificate_data = NULL;
freerdp* instance = (freerdp*) tls->settings->instance;
DWORD length;
BYTE* pemCert;
BYTE* pemCert = NULL;
DWORD flags = VERIFY_CERT_FLAG_NONE;
if (!tls_extract_pem(cert, &pemCert, &length))
return -1;
goto end;
/* Check, if we already accepted this key. */
if (is_accepted(tls, pemCert, length))
{
free(pemCert);
return 1;
}
goto end;
if (tls->isGatewayTransport || is_redirected(tls))
flags |= VERIFY_CERT_FLAG_LEGACY;
if (tls->isGatewayTransport)
flags |= VERIFY_CERT_FLAG_GATEWAY;
if (is_redirected(tls))
flags |= VERIFY_CERT_FLAG_REDIRECT;
/* Certificate management is done by the application */
if (tls->settings->ExternalCertificateManagement)
{
int status = -1;
if (instance->VerifyX509Certificate)
status = instance->VerifyX509Certificate(instance, pemCert, length, hostname,
port, tls->isGatewayTransport | is_redirected(tls) ? 2 : 0);
port, flags);
else
WLog_ERR(TAG, "No VerifyX509Certificate callback registered!");
if (status > 0)
{
accept_cert(tls, pemCert, length);
}
else if (status < 0)
{
WLog_ERR(TAG, "VerifyX509Certificate failed: (length = %d) status: [%d] %s",
length, status, pemCert);
free(pemCert);
return -1;
goto end;
}
else
free(pemCert);
return (status == 0) ? 0 : 1;
verification_status = (status == 0) ? FALSE : TRUE;
}
/* ignore certificate verification if user explicitly required it (discouraged) */
if (tls->settings->IgnoreCertificate)
{
free(pemCert);
return 1; /* success! */
}
if (!tls->isGatewayTransport && tls->settings->AuthenticationLevel == 0)
{
free(pemCert);
return 1; /* success! */
}
/* if user explicitly specified a certificate name, use it instead of the hostname */
if (!tls->isGatewayTransport && tls->settings->CertificateName)
hostname = tls->settings->CertificateName;
/* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */
certificate_status = x509_verify_certificate(cert,
tls->certificate_store->path);
/* verify certificate name match */
certificate_data = crypto_get_certificate_data(cert->px509, hostname, port);
/* extra common name and alternative names */
common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length);
dns_names = crypto_cert_get_dns_names(cert->px509, &dns_names_count,
&dns_names_lengths);
/* compare against common name */
if (common_name)
{
if (tls_match_hostname(common_name, common_name_length, hostname))
hostname_match = TRUE;
}
/* compare against alternative names */
if (dns_names)
{
for (index = 0; index < dns_names_count; index++)
{
if (tls_match_hostname(dns_names[index], dns_names_lengths[index], hostname))
{
hostname_match = TRUE;
break;
}
}
}
/* if the certificate is valid and the certificate name matches, verification succeeds */
if (certificate_status && hostname_match)
else if (tls->settings->IgnoreCertificate)
verification_status = TRUE; /* success! */
/* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */
if (!certificate_status || !hostname_match)
else if (!tls->isGatewayTransport && (tls->settings->AuthenticationLevel == 0))
verification_status = TRUE; /* success! */
else
{
char* issuer;
char* subject;
char* fingerprint;
DWORD accept_certificate = 0;
issuer = crypto_cert_issuer(cert->px509);
subject = crypto_cert_subject(cert->px509);
fingerprint = crypto_cert_fingerprint(cert->px509);
/* search for matching entry in known_hosts file */
match = certificate_data_match(tls->certificate_store, certificate_data);
/* if user explicitly specified a certificate name, use it instead of the hostname */
if (!tls->isGatewayTransport && tls->settings->CertificateName)
hostname = tls->settings->CertificateName;
if (match == 1)
/* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */
certificate_status = x509_verify_certificate(cert,
tls->certificate_store->path);
/* verify certificate name match */
certificate_data = crypto_get_certificate_data(cert->px509, hostname, port);
/* extra common name and alternative names */
common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length);
dns_names = crypto_cert_get_dns_names(cert->px509, &dns_names_count,
&dns_names_lengths);
/* compare against common name */
if (common_name)
{
/* no entry was found in known_hosts file, prompt user for manual verification */
if (!hostname_match)
tls_print_certificate_name_mismatch_error(
hostname, port,
common_name, dns_names,
dns_names_count);
if (tls_match_hostname(common_name, common_name_length, hostname))
hostname_match = TRUE;
}
/* Automatically accept certificate on first use */
if (tls->settings->AutoAcceptCertificate)
/* compare against alternative names */
if (dns_names)
{
for (index = 0; index < dns_names_count; index++)
{
WLog_INFO(TAG, "No certificate stored, automatically accepting.");
accept_certificate = 1;
}
else if (instance->VerifyCertificate)
{
accept_certificate = instance->VerifyCertificate(
instance, common_name,
subject, issuer,
fingerprint, !hostname_match);
}
switch (accept_certificate)
{
case 1:
/* user accepted certificate, add entry in known_hosts file */
verification_status = certificate_data_print(tls->certificate_store,
certificate_data);
break;
case 2:
/* user did accept temporaty, do not add to known hosts file */
verification_status = TRUE;
break;
default:
/* user did not accept, abort and do not add entry in known_hosts file */
verification_status = FALSE; /* failure! */
if (tls_match_hostname(dns_names[index], dns_names_lengths[index], hostname))
{
hostname_match = TRUE;
break;
}
}
}
else if (match == -1)
/* if the certificate is valid and the certificate name matches, verification succeeds */
if (certificate_status && hostname_match)
verification_status = TRUE; /* success! */
if (!hostname_match)
flags |= VERIFY_CERT_FLAG_MISMATCH;
/* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */
if (!certificate_status || !hostname_match)
{
char* old_subject = NULL;
char* old_issuer = NULL;
char* old_fingerprint = NULL;
/* entry was found in known_hosts file, but fingerprint does not match. ask user to use it */
tls_print_certificate_error(hostname, port, fingerprint,
tls->certificate_store->file);
char* issuer;
char* subject;
char* fingerprint;
DWORD accept_certificate = 0;
issuer = crypto_cert_issuer(cert->px509);
subject = crypto_cert_subject(cert->px509);
fingerprint = crypto_cert_fingerprint(cert->px509);
/* search for matching entry in known_hosts file */
match = certificate_data_match(tls->certificate_store, certificate_data);
if (!certificate_get_stored_data(tls->certificate_store,
certificate_data, &old_subject,
&old_issuer, &old_fingerprint))
WLog_WARN(TAG, "Failed to get certificate entry for %s:%d",
hostname, port);
if (instance->VerifyChangedCertificate)
if (match == 1)
{
accept_certificate = instance->VerifyChangedCertificate(
instance, common_name, subject, issuer,
fingerprint, old_subject, old_issuer,
old_fingerprint);
/* no entry was found in known_hosts file, prompt user for manual verification */
if (!hostname_match)
tls_print_certificate_name_mismatch_error(
hostname, port,
common_name, dns_names,
dns_names_count);
/* Automatically accept certificate on first use */
if (tls->settings->AutoAcceptCertificate)
{
WLog_INFO(TAG, "No certificate stored, automatically accepting.");
accept_certificate = 1;
}
else if (instance->VerifyX509Certificate)
{
int rc = instance->VerifyX509Certificate(instance, pemCert, length, hostname,
port, flags);
if (rc == 1)
accept_certificate = 1;
else if (rc > 1)
accept_certificate = 2;
else
accept_certificate = 0;
}
else if (instance->VerifyCertificateEx)
{
accept_certificate = instance->VerifyCertificateEx(
instance, hostname, port, common_name,
subject, issuer,
fingerprint, flags);
}
else if (instance->VerifyCertificate)
{
WLog_WARN(TAG,
"The VerifyCertificate callback is deprecated, migrate your application to VerifyCertificateEx");
accept_certificate = instance->VerifyCertificate(
instance, common_name,
subject, issuer,
fingerprint, !hostname_match);
}
}
else if (match == -1)
{
char* old_subject = NULL;
char* old_issuer = NULL;
char* old_fingerprint = NULL;
/* entry was found in known_hosts file, but fingerprint does not match. ask user to use it */
tls_print_certificate_error(hostname, port, fingerprint,
tls->certificate_store->file);
free(old_subject);
free(old_issuer);
free(old_fingerprint);
if (!certificate_get_stored_data(tls->certificate_store,
certificate_data, &old_subject,
&old_issuer, &old_fingerprint))
WLog_WARN(TAG, "Failed to get certificate entry for %s:%d",
hostname, port);
if (instance->VerifyX509Certificate)
{
const int rc = instance->VerifyX509Certificate(instance, pemCert, length, hostname,
port, flags | VERIFY_CERT_FLAG_CHANGED);
if (rc == 1)
accept_certificate = 1;
else if (rc > 1)
accept_certificate = 2;
else
accept_certificate = 0;
}
else if (instance->VerifyChangedCertificateEx)
{
accept_certificate = instance->VerifyChangedCertificateEx(
instance, hostname, port, common_name, subject, issuer,
fingerprint, old_subject, old_issuer,
old_fingerprint, flags | VERIFY_CERT_FLAG_CHANGED);
}
else if (instance->VerifyChangedCertificate)
{
WLog_WARN(TAG,
"The VerifyChangedCertificate callback is deprecated, migrate your application to VerifyChangedCertificateEx");
accept_certificate = instance->VerifyChangedCertificate(
instance, common_name, subject, issuer,
fingerprint, old_subject, old_issuer,
old_fingerprint);
}
free(old_subject);
free(old_issuer);
free(old_fingerprint);
}
else if (match == 0)
accept_certificate = 2; /* success! */
/* Save certificate or do a simple accept / reject */
switch (accept_certificate)
{
case 1:
@ -1507,15 +1539,17 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
verification_status = FALSE; /* failure! */
break;
}
}
else if (match == 0)
verification_status = TRUE; /* success! */
free(issuer);
free(subject);
free(fingerprint);
free(issuer);
free(subject);
free(fingerprint);
}
if (verification_status)
accept_cert(tls, pemCert, length);
}
end:
certificate_data_free(certificate_data);
free(common_name);
@ -1523,20 +1557,12 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
crypto_cert_dns_names_free(dns_names_count, dns_names_lengths,
dns_names);
if (verification_status > 0)
{
accept_cert(tls, pemCert, length);
}
else
{
free(pemCert);
}
free(pemCert);
return (verification_status == 0) ? 0 : 1;
}
void tls_print_certificate_error(char* hostname, UINT16 port, char* fingerprint,
char* hosts_file)
void tls_print_certificate_error(const char* hostname, UINT16 port, const char* fingerprint,
const char* hosts_file)
{
WLog_ERR(TAG, "The host key for %s:%"PRIu16" has changed", hostname, port);
WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
@ -1557,8 +1583,8 @@ void tls_print_certificate_error(char* hostname, UINT16 port, char* fingerprint,
WLog_ERR(TAG, "Host key verification failed.");
}
void tls_print_certificate_name_mismatch_error(char* hostname, UINT16 port,
char* common_name, char** alt_names,
void tls_print_certificate_name_mismatch_error(const char* hostname, UINT16 port,
const char* common_name, char** alt_names,
int alt_names_count)
{
int index;