Merge pull request #2687 from akallabeth/known_hosts_v2

Store SSL fingerprints with host and port
This commit is contained in:
Vic Lee 2015-06-12 12:52:53 +00:00
commit 145fc10412
14 changed files with 759 additions and 120 deletions

View File

@ -35,14 +35,17 @@ typedef struct rdp_certificate_store rdpCertificateStore;
struct rdp_certificate_data
{
char* hostname;
UINT16 port;
char* subject;
char* issuer;
char* fingerprint;
};
struct rdp_certificate_store
{
FILE* fp;
char* path;
char* file;
char* legacy_file;
rdpSettings* settings;
rdpCertificateData* certificate_data;
};
@ -51,13 +54,29 @@ struct rdp_certificate_store
extern "C" {
#endif
FREERDP_API rdpCertificateData* certificate_data_new(char* hostname, char* fingerprint);
FREERDP_API void certificate_data_free(rdpCertificateData* certificate_data);
FREERDP_API rdpCertificateStore* certificate_store_new(rdpSettings* settings);
FREERDP_API void certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data);
FREERDP_API void certificate_store_free(rdpCertificateStore* certificate_store);
FREERDP_API int certificate_data_match(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data);
FREERDP_API void certificate_data_print(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data);
FREERDP_API rdpCertificateData* certificate_data_new(
char* hostname, UINT16 port, char*subject,
char*issuer, char* fingerprint);
FREERDP_API void certificate_data_free(
rdpCertificateData* certificate_data);
FREERDP_API rdpCertificateStore* certificate_store_new(
rdpSettings* settings);
FREERDP_API BOOL certificate_data_replace(
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
FREERDP_API void certificate_store_free(
rdpCertificateStore* certificate_store);
FREERDP_API int certificate_data_match(
rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data);
FREERDP_API BOOL certificate_data_print(
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);
#ifdef __cplusplus
}

View File

@ -133,7 +133,7 @@ FREERDP_API void crypto_cert_print_info(X509* xcert);
FREERDP_API void crypto_cert_free(CryptoCert cert);
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 rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname, UINT16 port);
FREERDP_API BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey, DWORD* PublicKeyLength);
#define TSSK_KEY_LENGTH 64

View File

@ -98,8 +98,11 @@ 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, char* fingerprint, char* hosts_file);
FREERDP_API void tls_print_certificate_name_mismatch_error(char* hostname, char* common_name, char** alt_names, int alt_names_count);
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);

View File

@ -67,7 +67,10 @@ typedef BOOL (*pPostConnect)(freerdp* instance);
typedef void (*pPostDisconnect)(freerdp* instance);
typedef BOOL (*pAuthenticate)(freerdp* instance, char** username, char** password, char** domain);
typedef BOOL (*pVerifyCertificate)(freerdp* instance, char* subject, char* issuer, char* fingerprint);
typedef BOOL (*pVerifyChangedCertificate)(freerdp* instance, char* subject, char* issuer, char* new_fingerprint, char* old_fingerprint);
typedef BOOL (*pVerifyChangedCertificate)(freerdp* instance, char* subject,
char* issuer, char* new_fingerprint,
char* old_subject, char* old_issuer,
char* old_fingerprint);
typedef int (*pVerifyX509Certificate)(freerdp* instance, BYTE* data, int length, const char* hostname, int port, DWORD flags);
typedef int (*pLogonErrorInfo)(freerdp* instance, UINT32 data, UINT32 type);
@ -207,7 +210,7 @@ struct rdp_freerdp
Callback for certificate validation.
Used to verify that an unknown certificate is trusted. */
ALIGN64 pVerifyChangedCertificate VerifyChangedCertificate; /**< (offset 52)
Callback for changed certificate validation.
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. */

View File

@ -28,6 +28,8 @@
#include <unistd.h>
#endif
#include <ctype.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/path.h>

View File

@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
#include <winpr/crypto.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/path.h>
@ -35,13 +36,18 @@
static const char certificate_store_dir[] = "certs";
static const char certificate_server_dir[] = "server";
static const char certificate_known_hosts_file[] = "known_hosts";
static const char certificate_known_hosts_file[] = "known_hosts2";
static const char certificate_legacy_hosts_file[] = "known_hosts";
#include <freerdp/log.h>
#include <freerdp/crypto/certificate.h>
#define TAG FREERDP_TAG("crypto")
static BOOL certificate_split_line(char* line, char** host, UINT16* port,
char**subject, char**issuer,
char** fingerprint);
BOOL certificate_store_init(rdpCertificateStore* certificate_store)
{
char* server_path = NULL;
@ -53,10 +59,10 @@ BOOL certificate_store_init(rdpCertificateStore* certificate_store)
{
if (!CreateDirectoryA(settings->ConfigPath, 0))
{
WLog_ERR(TAG, "error creating directory '%s'", settings->ConfigPath);
WLog_ERR(TAG, "error creating directory '%s'", settings->ConfigPath);
goto fail;
}
WLog_INFO(TAG, "creating directory %s", settings->ConfigPath);
WLog_INFO(TAG, "creating directory %s", settings->ConfigPath);
}
if (!(certificate_store->path = GetCombinedPath(settings->ConfigPath, (char*) certificate_store_dir)))
@ -66,10 +72,10 @@ BOOL certificate_store_init(rdpCertificateStore* certificate_store)
{
if (!CreateDirectoryA(certificate_store->path, 0))
{
WLog_ERR(TAG, "error creating directory [%s]", certificate_store->path);
WLog_ERR(TAG, "error creating directory [%s]", certificate_store->path);
goto fail;
}
WLog_INFO(TAG, "creating directory [%s]", certificate_store->path);
WLog_INFO(TAG, "creating directory [%s]", certificate_store->path);
}
if (!(server_path = GetCombinedPath(settings->ConfigPath, (char*) certificate_server_dir)))
@ -79,32 +85,25 @@ BOOL certificate_store_init(rdpCertificateStore* certificate_store)
{
if (!CreateDirectoryA(server_path, 0))
{
WLog_ERR(TAG, "error creating directory [%s]", server_path);
WLog_ERR(TAG, "error creating directory [%s]", server_path);
goto fail;
}
WLog_INFO(TAG, "created directory [%s]", server_path);
WLog_INFO(TAG, "created directory [%s]", server_path);
}
if (!(certificate_store->file = GetCombinedPath(settings->ConfigPath, (char*) certificate_known_hosts_file)))
goto fail;
if (!PathFileExistsA(certificate_store->file))
certificate_store->fp = fopen((char*) certificate_store->file, "w+");
else
certificate_store->fp = fopen((char*) certificate_store->file, "r+");
if (!certificate_store->fp)
{
WLog_ERR(TAG, "error opening [%s]", certificate_store->file);
if (!(certificate_store->legacy_file = GetCombinedPath(settings->ConfigPath,
(char*) certificate_legacy_hosts_file)))
goto fail;
}
free(server_path);
return TRUE;
fail:
WLog_ERR(TAG, "certificate store initialization failed");
WLog_ERR(TAG, "certificate store initialization failed");
free(server_path);
free(certificate_store->path);
free(certificate_store->file);
@ -113,16 +112,111 @@ fail:
return FALSE;
}
int certificate_data_match(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
{
FILE* fp;
int match = 1;
char* data;
char* mdata;
char* pline;
char* hostname;
long size;
size_t length;
fp = fopen(certificate_store->legacy_file, "rb");
if (!fp)
return match;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (size < 1)
{
fclose(fp);
return match;
}
mdata = (char*) malloc(size + 2);
if (!mdata)
{
fclose(fp);
return match;
}
data = mdata;
if (fread(data, size, 1, fp) != 1)
{
free(data);
fclose(fp);
return match;
}
fclose(fp);
data[size] = '\n';
data[size + 1] = '\0';
pline = StrSep(&data, "\r\n");
while (pline != NULL)
{
length = strlen(pline);
if (length > 0)
{
hostname = StrSep(&pline, " \t");
if (!hostname || !pline)
WLog_WARN(TAG, "Invalid %s entry %s %s!", certificate_legacy_hosts_file,
hostname, pline);
else if (strcmp(hostname, certificate_data->hostname) == 0)
{
match = strcmp(pline, certificate_data->fingerprint);
break;
}
}
pline = StrSep(&data, "\r\n");
}
free(mdata);
/* Found a valid fingerprint in legacy file,
* copy to new file in new format. */
if (0 == match)
{
rdpCertificateData* data = certificate_data_new(hostname,
certificate_data->port,
NULL, NULL,
pline);
if (data)
match = certificate_data_print(certificate_store, data) ? 0 : 1;
certificate_data_free(data);
}
return match;
}
static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data,
char** psubject, char** pissuer,
char** fprint)
{
BOOL found = FALSE;
FILE* fp;
int length;
char* data;
char* mdata;
char* pline;
int match = 1;
long int size;
char* hostname = NULL;
char* subject = NULL;
char* issuer = NULL;
char* fingerprint = NULL;
unsigned short port = 0;
fp = certificate_store->fp;
fp = fopen(certificate_store->file, "rb");
if (!fp)
return match;
@ -132,19 +226,30 @@ int certificate_data_match(rdpCertificateStore* certificate_store, rdpCertificat
fseek(fp, 0, SEEK_SET);
if (size < 1)
return match;
data = (char*) malloc(size + 2);
if (fread(data, size, 1, fp) != 1)
{
free(data);
fclose(fp);
return match;
}
mdata = (char*) malloc(size + 2);
if (!mdata)
{
fclose(fp);
return match;
}
data = mdata;
if (fread(data, size, 1, fp) != 1)
{
fclose(fp);
free(data);
return match;
}
fclose(fp);
data[size] = '\n';
data[size + 1] = '\0';
pline = strtok(data, "\n");
pline = StrSep(&data, "\r\n");
while (pline != NULL)
{
@ -152,62 +257,115 @@ int certificate_data_match(rdpCertificateStore* certificate_store, rdpCertificat
if (length > 0)
{
length = strcspn(pline, " \t");
pline[length] = '\0';
if (strcmp(pline, certificate_data->hostname) == 0)
if (!certificate_split_line(pline, &hostname, &port,
&subject, &issuer, &fingerprint))
WLog_WARN(TAG, "Invalid %s entry %s!",
certificate_known_hosts_file, pline);
else if (strcmp(pline, certificate_data->hostname) == 0)
{
pline = &pline[length + 1];
int outLen;
if (strcmp(pline, certificate_data->fingerprint) == 0)
match = 0;
else
match = -1;
break;
if (port == certificate_data->port)
{
found = TRUE;
match = strcmp(certificate_data->fingerprint, fingerprint);
if (fingerprint && 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;
}
}
}
pline = strtok(NULL, "\n");
pline = StrSep(&data, "\r\n");
}
free(data);
free(mdata);
if ((match != 0) && !found)
match = certificate_data_match_legacy(certificate_store, certificate_data);
return match;
}
void certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
BOOL certificate_get_stored_data(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data,
char** subject, char** issuer,
char** fingerprint)
{
int rc = certificate_data_match_raw(certificate_store, certificate_data,
subject, issuer, fingerprint);
if ((rc == 0) || (rc == -1))
return TRUE;
return FALSE;
}
int certificate_data_match(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
{
return certificate_data_match_raw(certificate_store, certificate_data,
NULL, NULL, NULL);
}
BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
{
FILE* fp;
BOOL rc = FALSE;
int length;
char* data;
char* sdata;
char* pline;
long int size;
fp = certificate_store->fp;
fp = fopen(certificate_store->file, "rb");
if (!fp)
return;
return FALSE;
/* Read the current contents of the file. */
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (size < 1)
return;
{
fclose(fp);
return FALSE;
}
data = (char*) malloc(size + 2);
if (!data)
{
fclose(fp);
return FALSE;
}
if (fread(data, size, 1, fp) != 1)
{
fclose(fp);
free(data);
return;
return FALSE;
}
fclose(fp);
fp = fopen(certificate_store->file, "wb");
if (!fp)
{
free(data);
return FALSE;
}
/* Write the file back out, with appropriate fingerprint substitutions */
fp = fopen(certificate_store->file, "w+");
data[size] = '\n';
data[size + 1] = '\0';
pline = strtok(data, "\n"); // xxx: use strsep
sdata = data;
pline = StrSep(&sdata, "\r\n");
while (pline != NULL)
{
@ -215,60 +373,135 @@ void certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertifi
if (length > 0)
{
char* hostname = pline, *fingerprint;
length = strcspn(pline, " \t");
hostname[length] = '\0';
UINT16 port = 0;
char* hostname = NULL;
char* fingerprint = NULL;
char* subject = NULL;
char* issuer = NULL;
/* If this is the replaced hostname, use the updated fingerprint. */
if (strcmp(hostname, certificate_data->hostname) == 0)
fingerprint = certificate_data->fingerprint;
if (!certificate_split_line(pline, &hostname, &port, &subject, &issuer, &fingerprint))
WLog_WARN(TAG, "Skipping invalid %s entry %s!",
certificate_known_hosts_file, pline);
else
fingerprint = &hostname[length + 1];
fprintf(fp, "%s %s\n", hostname, fingerprint);
{
/* If this is the replaced hostname, use the updated fingerprint. */
if ((strcmp(hostname, certificate_data->hostname) == 0) &&
(port == certificate_data->port))
{
fingerprint = certificate_data->fingerprint;
rc = TRUE;
}
fprintf(fp, "%s %hu %s %s %s\n", hostname, port, fingerprint, subject, issuer);
}
}
pline = strtok(NULL, "\n");
pline = StrSep(&sdata, "\r\n");
}
fclose(fp);
free(data);
free(data);
return rc;
}
void certificate_data_print(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
BOOL certificate_split_line(char* line, char** host, UINT16* port, char** subject,
char** issuer, char** fingerprint)
{
char* cur;
size_t length = strlen(line);
if (length <= 0)
return FALSE;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*host = cur;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
if(sscanf(cur, "%hu", port) != 1)
return FALSE;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*fingerprint = cur;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*subject = cur;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*issuer = cur;
return TRUE;
}
BOOL certificate_data_print(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
{
FILE* fp;
/* reopen in append mode */
fp = fopen(certificate_store->file, "a");
fp = fopen(certificate_store->file, "ab");
if (!fp)
return;
return FALSE;
fprintf(fp, "%s %hu %s %s %s\n", certificate_data->hostname, certificate_data->port,
certificate_data->fingerprint, certificate_data->subject,
certificate_data->issuer);
fprintf(fp, "%s %s\n", certificate_data->hostname, certificate_data->fingerprint);
fclose(fp);
return TRUE;
}
rdpCertificateData* certificate_data_new(char* hostname, char* fingerprint)
rdpCertificateData* certificate_data_new(char* hostname, UINT16 port, char* subject, char* issuer, char* fingerprint)
{
rdpCertificateData* certdata;
if (!hostname)
return NULL;
if (!fingerprint)
return NULL;
certdata = (rdpCertificateData *)calloc(1, sizeof(rdpCertificateData));
if (!certdata)
return NULL;
certdata->port = port;
certdata->hostname = _strdup(hostname);
if (!certdata->hostname)
goto out_free;
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(subject));
else
certdata->issuer = crypto_base64_encode((BYTE*)"", 0);
certdata->fingerprint = _strdup(fingerprint);
if (!certdata->fingerprint)
goto out_free_hostname;
if (!certdata->hostname || !certdata->subject ||
!certdata->issuer || !certdata->fingerprint)
goto fail;
return certdata;
out_free_hostname:
fail:
free(certdata->hostname);
out_free:
free(certdata->subject);
free(certdata->issuer);
free(certdata->fingerprint);
free(certdata);
return NULL;
}
@ -278,6 +511,8 @@ void certificate_data_free(rdpCertificateData* certificate_data)
if (certificate_data != NULL)
{
free(certificate_data->hostname);
free(certificate_data->subject);
free(certificate_data->issuer);
free(certificate_data->fingerprint);
free(certificate_data);
}
@ -307,9 +542,6 @@ void certificate_store_free(rdpCertificateStore* certstore)
{
if (certstore != NULL)
{
if (certstore->fp != NULL)
fclose(certstore->fp);
free(certstore->path);
free(certstore->file);
free(certstore);

View File

@ -288,7 +288,7 @@ static int crypto_rsa_common(const BYTE* input, int length, UINT32 key_length, c
BN_free(&mod);
BN_CTX_free(ctx);
out_free_input_reverse:
out_free_input_reverse:
free(input_reverse);
return output_length;
@ -376,7 +376,7 @@ char* crypto_print_name(X509_NAME* name)
{
char* buffer = NULL;
BIO* outBIO = BIO_new(BIO_s_mem());
if (X509_NAME_print_ex(outBIO, name, 0, XN_FLAG_ONELINE) > 0)
{
unsigned long size = BIO_number_written(outBIO);
@ -433,7 +433,7 @@ char* crypto_cert_subject_common_name(X509* xcert, int* length)
}
FREERDP_API void crypto_cert_subject_alt_name_free(int count, int *lengths,
char** alt_name)
char** alt_name)
{
int i;
@ -555,8 +555,10 @@ end:
return status;
}
rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname)
rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname, UINT16 port)
{
char* issuer;
char* subject;
char* fp;
rdpCertificateData* certdata;
@ -564,7 +566,13 @@ rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname)
if (!fp)
return NULL;
certdata = certificate_data_new(hostname, fp);
issuer = crypto_cert_issuer(xcert);
subject = crypto_cert_subject(xcert);
certdata = certificate_data_new(hostname, port, issuer, subject, fp);
free(subject);
free(issuer);
free(fp);
return certdata;
@ -590,8 +598,8 @@ void crypto_cert_print_info(X509* xcert)
WLog_INFO(TAG, "\tIssuer: %s", issuer);
WLog_INFO(TAG, "\tThumbprint: %s", fp);
WLog_INFO(TAG, "The above X.509 certificate could not be verified, possibly because you do not have "
"the CA certificate in your certificate store, or the certificate has expired. "
"Please look at the documentation on how to create local certificate store for a private CA.");
"the CA certificate in your certificate store, or the certificate has expired. "
"Please look at the documentation on how to create local certificate store for a private CA.");
free(fp);
out_free_issuer:
free(issuer);

View File

@ -5,6 +5,7 @@ set(MODULE_PREFIX "TEST_FREERDP_CRYPTO")
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestKnownHosts.c
TestBase64.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS

View File

@ -0,0 +1,322 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
*
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 Armin Novak <armin.novak@thincast.com>
*
* 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.
*/
#include <winpr/path.h>
#include <winpr/file.h>
#include <freerdp/crypto/certificate.h>
static int prepare(const char* currentFileV2, const char* legacyFileV2, const char* legacyFile)
{
char* legacy[] = {
"someurl ff:11:22:dd\r\n",
"otherurl aa:bb:cc:dd\r",
"legacyurl aa:bb:cc:dd\n"
};
char* hosts[] = {
"someurl 3389 ff:11:22:dd subject issuer\r\n",
"otherurl\t3389\taa:bb:cc:dd\tsubject2\tissuer2\r",
};
FILE* fl = NULL;
FILE* fc = NULL;
size_t i;
fc = fopen(currentFileV2, "w+");
if (!fc)
goto finish;
fl = fopen(legacyFileV2, "w+");
if (!fl)
goto finish;
for (i=0; i<sizeof(hosts)/sizeof(hosts[0]); i++)
{
fwrite(hosts[i], strlen(hosts[i]), sizeof(char), fl);
fwrite(hosts[i], strlen(hosts[i]), sizeof(char), fc);
}
fclose(fc);
fc = NULL;
fclose(fl);
fl = NULL;
fl = fopen(legacyFile, "w+");
if (!fl)
goto finish;
for (i=0; i<sizeof(legacy)/sizeof(legacy[0]); i++)
fwrite(legacy[i], strlen(legacy[i]), sizeof(char), fl);
fclose(fl);
return 0;
finish:
fclose(fl);
fclose(fc);
return -1;
}
int TestKnownHosts(int argc, char* argv[])
{
int rc = -1;
rdpSettings current;
rdpSettings legacy;
rdpCertificateData* data = NULL;
rdpCertificateStore* store = NULL;
char* currentFileV2 = NULL;
char* legacyFileV2 = NULL;
char* legacyFile = NULL;
char* subject = NULL;
char* issuer = NULL;
char* fp = NULL;
current.ConfigPath = GetKnownSubPath(KNOWN_PATH_TEMP, "TestKnownHostsCurrent");
legacy.ConfigPath = GetKnownSubPath(KNOWN_PATH_TEMP, "TestKnownHostsLegacy");
if (!PathFileExistsA(current.ConfigPath))
{
if (!CreateDirectoryA(current.ConfigPath, NULL))
{
fprintf(stderr, "Could not create %s!\n", current.ConfigPath);
goto finish;
}
}
if (!PathFileExistsA(legacy.ConfigPath))
{
if (!CreateDirectoryA(legacy.ConfigPath, NULL))
{
fprintf(stderr, "Could not create %s!\n", legacy.ConfigPath);
goto finish;
}
}
currentFileV2 = GetCombinedPath(current.ConfigPath, "known_hosts2");
if (!currentFileV2)
{
fprintf(stderr, "Could not get file path!\n");
goto finish;
}
legacyFileV2 = GetCombinedPath(legacy.ConfigPath, "known_hosts2");
if (!legacyFileV2)
{
fprintf(stderr, "Could not get file path!\n");
goto finish;
}
legacyFile = GetCombinedPath(legacy.ConfigPath, "known_hosts");
if (!legacyFile)
{
fprintf(stderr, "Could not get file path!\n");
goto finish;
}
store = certificate_store_new(&current);
if (!store)
{
fprintf(stderr, "Could not create certificate store!\n");
goto finish;
}
if (prepare(currentFileV2, legacyFileV2, legacyFile))
goto finish;
/* Test if host is found in current file. */
data = certificate_data_new("someurl", 3389, "subject", "issuer", "ff:11:22:dd");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (0 != certificate_data_match(store, data))
{
fprintf(stderr, "Could not find data in v2 file!\n");
goto finish;
}
/* Test if we can read out the old fingerprint. */
if (!certificate_get_stored_data(store, data, &subject, &issuer, &fp))
{
fprintf(stderr, "Could not read old fingerprint!\n");
goto finish;
}
printf("Got %s, %s '%s'\n", subject, issuer, fp);
free(subject);
free(issuer);
free(fp);
subject = NULL;
issuer = NULL;
fp = NULL;
certificate_data_free(data);
/* Test if host not found in current file. */
data = certificate_data_new("somehost", 1234, "", "", "ff:aa:bb:cc");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (0 == certificate_data_match(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
/* Test if we read out the old fingerprint fails. */
if (certificate_get_stored_data(store, data, &subject, &issuer, &fp))
{
fprintf(stderr, "Read out not existing old fingerprint succeeded?!\n");
goto finish;
}
certificate_data_free(data);
/* Test host add current file. */
data = certificate_data_new("somehost", 1234, "", "", "ff:aa:bb:cc");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_print(store, data))
{
fprintf(stderr, "Could not add host to file!\n");
goto finish;
}
if (0 != certificate_data_match(store, data))
{
fprintf(stderr, "Could not find host written in v2 file!\n");
goto finish;
}
certificate_data_free(data);
/* Test host replace current file. */
data = certificate_data_new("somehost", 1234, "", "", "ff:aa:bb:dd:ee");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (!certificate_data_replace(store, data))
{
fprintf(stderr, "Could not replace data!\n");
goto finish;
}
if (0 != certificate_data_match(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
certificate_data_free(data);
/* Test host replace invalid entry in current file. */
data = certificate_data_new("somehostXXXX", 1234, "", "", "ff:aa:bb:dd:ee");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (certificate_data_replace(store, data))
{
fprintf(stderr, "Invalid return for replace invalid entry!\n");
goto finish;
}
if (0 == certificate_data_match(store, data))
{
fprintf(stderr, "Invalid host found in v2 file!\n");
goto finish;
}
certificate_data_free(data);
certificate_store_free(store);
store = certificate_store_new(&legacy);
if (!store)
{
fprintf(stderr, "could not create certificate store!\n");
goto finish;
}
/* test if host found in legacy file. */
data = certificate_data_new("legacyurl", 1234, "", "", "aa:bb:cc:dd");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (0 != certificate_data_match(store, data))
{
fprintf(stderr, "Could not find host in file!\n");
goto finish;
}
certificate_data_free(data);
/* test if host not found. */
data = certificate_data_new("somehost-not-in-file", 1234, "", "", "ff:aa:bb:cc");
if (!data)
{
fprintf(stderr, "Could not create certificate data!\n");
goto finish;
}
if (0 == certificate_data_match(store, data))
{
fprintf(stderr, "Invalid host found in file!\n");
goto finish;
}
rc = 0;
finish:
if (store)
certificate_store_free(store);
if (data)
certificate_data_free(data);
DeleteFileA(currentFileV2);
//RemoveDirectoryA(current.ConfigPath);
DeleteFileA(legacyFileV2);
DeleteFileA(legacyFile);
//RemoveDirectoryA(legacy.ConfigPath);
free (currentFileV2);
free (legacyFileV2);
free (legacyFile);
free(subject);
free(issuer);
free(fp);
return rc;
}

View File

@ -0,0 +1,2 @@
someurl ff:11:22:dd
otherurl aa:bb:cc:dd

View File

@ -0,0 +1,2 @@
someurl 3389 ff:11:22:dd
otherurl 3389 aa:bb:cc:dd

View File

@ -602,7 +602,7 @@ BOOL tls_prepare(rdpTls* tls, BIO* underlying, const SSL_METHOD* method, int opt
return FALSE;
}
}
tls->bio = BIO_new_rdp_tls(tls->ctx, clientMode);
if (BIO_get_ssl(tls->bio, &tls->ssl) < 0)
@ -761,7 +761,7 @@ int tls_connect(rdpTls* tls, BIO* underlying)
#ifndef OPENSSL_NO_TLSEXT
static void tls_openssl_tlsext_debug_callback(SSL *s, int client_server,
int type, unsigned char *data, int len, void *arg)
int type, unsigned char *data, int len, void *arg)
{
/* see code comment in tls_accept() below */
@ -796,7 +796,7 @@ BOOL tls_accept(rdpTls* tls, BIO* underlying, const char* cert_file, const char*
#ifdef SSL_OP_NO_COMPRESSION
options |= SSL_OP_NO_COMPRESSION;
#endif
/**
* SSL_OP_TLS_BLOCK_PADDING_BUG:
*
@ -994,7 +994,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
*/
bio = BIO_new(BIO_s_mem());
if (!bio)
{
WLog_ERR(TAG, "BIO_new() failure");
@ -1008,19 +1008,19 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status);
return -1;
}
offset = 0;
length = 2048;
pemCert = (BYTE*) malloc(length + 1);
status = BIO_read(bio, pemCert, length);
if (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
return -1;
}
offset += status;
while (offset >= length)
@ -1048,17 +1048,17 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
WLog_ERR(TAG, "failed to read certificate");
return -1;
}
length = offset;
pemCert[length] = '\0';
status = -1;
if (instance->VerifyX509Certificate)
{
status = instance->VerifyX509Certificate(instance, pemCert, length, hostname, port, tls->isGatewayTransport);
}
WLog_ERR(TAG, "(length = %d) status: %d%s", length, status, pemCert);
free(pemCert);
@ -1082,7 +1082,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
certificate_status = x509_verify_certificate(cert, tls->certificate_store->path);
/* verify certificate name match */
certificate_data = crypto_get_certificate_data(cert->px509, hostname);
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);
@ -1124,7 +1124,9 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
/* if the certificate is valid but the certificate name does not match, warn user, do not accept */
if (certificate_status && !hostname_match)
tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count);
tls_print_certificate_name_mismatch_error(hostname, port,
common_name, alt_names,
alt_names_count);
/* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */
@ -1147,7 +1149,10 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
{
/* no entry was found in known_hosts file, prompt user for manual verification */
if (!hostname_match)
tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count);
tls_print_certificate_name_mismatch_error(
hostname, port,
common_name, alt_names,
alt_names_count);
if (instance->VerifyCertificate)
{
@ -1162,20 +1167,35 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
else
{
/* user accepted certificate, add entry in known_hosts file */
certificate_data_print(tls->certificate_store, certificate_data);
verification_status = TRUE; /* success! */
verification_status = certificate_data_print(tls->certificate_store, certificate_data);
}
}
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, fingerprint, tls->certificate_store->file);
tls_print_certificate_error(hostname, port, fingerprint,
tls->certificate_store->file);
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:hu",
hostname, port);
if (instance->VerifyChangedCertificate)
{
accept_certificate = instance->VerifyChangedCertificate(instance, subject, issuer, fingerprint, "");
accept_certificate = instance->VerifyChangedCertificate(
instance, subject, issuer,
fingerprint, old_subject, old_issuer,
old_fingerprint);
}
free(old_fingerprint);
if (!accept_certificate)
{
/* user did not accept, abort and do not change known_hosts file */
@ -1184,8 +1204,7 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
else
{
/* user accepted new certificate, add replace fingerprint for this host in known_hosts file */
certificate_data_replace(tls->certificate_store, certificate_data);
verification_status = TRUE; /* success! */
verification_status = certificate_data_replace(tls->certificate_store, certificate_data);
}
}
else if (match == 0)
@ -1211,14 +1230,15 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
if (alt_names)
crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths,
alt_names);
alt_names);
return (verification_status == 0) ? 0 : 1;
}
void tls_print_certificate_error(char* hostname, char* fingerprint, char *hosts_file)
void tls_print_certificate_error(char* hostname, UINT16 port, char* fingerprint,
char *hosts_file)
{
WLog_ERR(TAG, "The host key for %s has changed", hostname);
WLog_ERR(TAG, "The host key for %s:%hu has changed", hostname, port);
WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
WLog_ERR(TAG, "@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @");
WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
@ -1232,7 +1252,9 @@ void tls_print_certificate_error(char* hostname, char* fingerprint, char *hosts_
WLog_ERR(TAG, "Host key verification failed.");
}
void tls_print_certificate_name_mismatch_error(char* hostname, char* common_name, char** alt_names, int alt_names_count)
void tls_print_certificate_name_mismatch_error(char* hostname, UINT16 port,
char* common_name, char** alt_names,
int alt_names_count)
{
int index;
@ -1240,8 +1262,10 @@ void tls_print_certificate_name_mismatch_error(char* hostname, char* common_name
WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
WLog_ERR(TAG, "@ WARNING: CERTIFICATE NAME MISMATCH! @");
WLog_ERR(TAG, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
WLog_ERR(TAG, "The hostname used for this connection (%s) ", hostname);
WLog_ERR(TAG, "does not match %s given in the certificate:", alt_names_count < 1 ? "the name" : "any of the names");
WLog_ERR(TAG, "The hostname used for this connection (%s:%hu) ",
hostname, port);
WLog_ERR(TAG, "does not match %s given in the certificate:",
alt_names_count < 1 ? "the name" : "any of the names");
WLog_ERR(TAG, "Common Name (CN):");
WLog_ERR(TAG, "\t%s", common_name ? common_name : "no CN found in certificate");
if (alt_names_count > 0)

View File

@ -186,6 +186,8 @@ WINPR_API void ByteSwapUnicode(WCHAR* wstr, int length);
WINPR_API int ConvertLineEndingToLF(char* str, int size);
WINPR_API char* ConvertLineEndingToCRLF(const char* str, int* size);
WINPR_API char* StrSep(char** stringp, const char* delim);
#ifdef __cplusplus
}
#endif

View File

@ -463,3 +463,22 @@ char* ConvertLineEndingToCRLF(const char* str, int* size)
return newStr;
}
char* StrSep(char** stringp, const char* delim)
{
char* start = *stringp;
char* p;
p = (start != NULL) ? strpbrk(start, delim) : NULL;
if (!p)
*stringp = NULL;
else
{
*p = '\0';
*stringp = p + 1;
}
return start;
}