Added subject and issuer to saved data.

When a certificate has changed, display not only the
fingerprint but also subject and issuer of old certificate.
This commit is contained in:
Armin Novak 2015-06-11 11:16:45 +02:00
parent 7786cf1376
commit 7fc1c65165
6 changed files with 134 additions and 50 deletions

View File

@ -36,6 +36,8 @@ struct rdp_certificate_data
{
char* hostname;
UINT16 port;
char* subject;
char* issuer;
char* fingerprint;
};
@ -52,15 +54,29 @@ struct rdp_certificate_store
extern "C" {
#endif
FREERDP_API rdpCertificateData* certificate_data_new(char* hostname, UINT16 port, 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_fingerprint(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data, char** fingerprint);
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

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

@ -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>
@ -43,8 +44,9 @@ 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** fingerprint);
static BOOL certificate_split_line(char* line, char** host, UINT16* port,
char**subject, char**issuer,
char** fingerprint);
BOOL certificate_store_init(rdpCertificateStore* certificate_store)
{
@ -184,7 +186,8 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
{
rdpCertificateData* data = certificate_data_new(hostname,
certificate_data->port,
pline);
NULL, NULL,
pline);
if (data)
match = certificate_data_print(certificate_store, data) ? 0 : 1;
certificate_data_free(data);
@ -195,7 +198,9 @@ static int certificate_data_match_legacy(rdpCertificateStore* certificate_store,
}
static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data, char** fprint)
rdpCertificateData* certificate_data,
char** psubject, char** pissuer,
char** fprint)
{
BOOL found = FALSE;
FILE* fp;
@ -206,6 +211,8 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
int match = 1;
long int size;
char* hostname = NULL;
char* subject = NULL;
char* issuer = NULL;
char* fingerprint = NULL;
unsigned short port = 0;
@ -250,16 +257,24 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
if (length > 0)
{
if (!certificate_split_line(pline, &hostname, &port, &fingerprint))
WLog_WARN(TAG, "Invalid %s entry %s!", certificate_known_hosts_file, pline);
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)
{
int outLen;
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), psubject, &outLen);
if (issuer && pissuer)
crypto_base64_decode(issuer, strlen(issuer), pissuer, &outLen);
break;
}
}
@ -275,22 +290,28 @@ static int certificate_data_match_raw(rdpCertificateStore* certificate_store,
return match;
}
BOOL certificate_get_fingerprint(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data, char** fingerprint)
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, 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)
int certificate_data_match(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
{
return certificate_data_match_raw(certificate_store, certificate_data, NULL);
return certificate_data_match_raw(certificate_store, certificate_data,
NULL, NULL, NULL);
}
BOOL certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
BOOL certificate_data_replace(rdpCertificateStore* certificate_store,
rdpCertificateData* certificate_data)
{
FILE* fp;
BOOL rc = FALSE;
@ -353,9 +374,12 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertifi
if (length > 0)
{
UINT16 port = 0;
char* hostname = NULL, *fingerprint;
char* hostname = NULL;
char* fingerprint = NULL;
char* subject = NULL;
char* issuer = NULL;
if (!certificate_split_line(pline, &hostname, &port, &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
@ -367,7 +391,7 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertifi
fingerprint = certificate_data->fingerprint;
rc = TRUE;
}
fprintf(fp, "%s %hu %s\n", hostname, port, fingerprint);
fprintf(fp, "%s %hu %s %s %s\n", hostname, port, fingerprint, subject, issuer);
}
}
@ -380,7 +404,8 @@ BOOL certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertifi
return rc;
}
BOOL certificate_split_line(char* line, char** host, UINT16* port, char** fingerprint)
BOOL certificate_split_line(char* line, char** host, UINT16* port, char** subject,
char** issuer, char** fingerprint)
{
char* cur;
size_t length = strlen(line);
@ -406,6 +431,18 @@ BOOL certificate_split_line(char* line, char** host, UINT16* port, char** finger
*fingerprint = cur;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*subject = cur;
cur = StrSep(&line, " \t");
if (!cur)
return FALSE;
*issuer = cur;
return TRUE;
}
@ -419,15 +456,16 @@ BOOL certificate_data_print(rdpCertificateStore* certificate_store, rdpCertifica
if (!fp)
return FALSE;
fprintf(fp, "%s %hu %s\n", certificate_data->hostname, certificate_data->port,
certificate_data->fingerprint);
fprintf(fp, "%s %hu %s %s %s\n", certificate_data->hostname, certificate_data->port,
certificate_data->fingerprint, certificate_data->subject,
certificate_data->issuer);
fclose(fp);
return TRUE;
}
rdpCertificateData* certificate_data_new(char* hostname, UINT16 port, char* fingerprint)
rdpCertificateData* certificate_data_new(char* hostname, UINT16 port, char* subject, char* issuer, char* fingerprint)
{
rdpCertificateData* certdata;
@ -437,16 +475,21 @@ rdpCertificateData* certificate_data_new(char* hostname, UINT16 port, char* fing
certdata->port = port;
certdata->hostname = _strdup(hostname);
if (!certdata->hostname)
goto out_free;
certdata->subject = crypto_base64_encode(subject, strlen(subject));
certdata->issuer = crypto_base64_encode(issuer, strlen(subject));
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;
}
@ -456,6 +499,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);
}

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;
@ -557,6 +557,8 @@ end:
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, UIN
if (!fp)
return NULL;
certdata = certificate_data_new(hostname, port, 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

@ -83,6 +83,8 @@ int TestKnownHosts(int argc, char* argv[])
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");
@ -152,13 +154,17 @@ int TestKnownHosts(int argc, char* argv[])
}
/* Test if we can read out the old fingerprint. */
if (!certificate_get_fingerprint(store, data, &fp))
if (!certificate_get_stored_data(store, data, &subject, &issuer, &fp))
{
fprintf(stderr, "Could not read old fingerprint!\n");
goto finish;
}
printf("Got '%s'\n", fp);
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);
@ -177,7 +183,7 @@ int TestKnownHosts(int argc, char* argv[])
}
/* Test if we read out the old fingerprint fails. */
if (certificate_get_fingerprint(store, data, &fp))
if (certificate_get_stored_data(store, data, &subject, &issuer, &fp))
{
fprintf(stderr, "Read out not existing old fingerprint succeeded?!\n");
goto finish;
@ -308,6 +314,8 @@ finish:
free (currentFileV2);
free (legacyFileV2);
free (legacyFile);
free(subject);
free(issuer);
free(fp);
return rc;

View File

@ -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 */
@ -1172,14 +1172,17 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
}
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);
if (!certificate_get_fingerprint(tls->certificate_store,
certificate_data, &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:hu",
hostname, port);
@ -1187,7 +1190,8 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int por
{
accept_certificate = instance->VerifyChangedCertificate(
instance, subject, issuer,
fingerprint, old_fingerprint);
fingerprint, old_subject, old_issuer,
old_fingerprint);
}
free(old_fingerprint);
@ -1226,7 +1230,7 @@ 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;
}