libfreerdp-crypto: Allow clients to accept a changed cert

This commit is contained in:
Dorian Johnson
2012-06-28 20:05:10 -05:00
parent ad9419705a
commit 3b3f05a0b1
4 changed files with 86 additions and 9 deletions

View File

@@ -50,6 +50,7 @@ struct rdp_certificate_store
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);

View File

@@ -52,6 +52,7 @@ typedef boolean (*pPreConnect)(freerdp* instance);
typedef boolean (*pPostConnect)(freerdp* instance);
typedef boolean (*pAuthenticate)(freerdp* instance, char** username, char** password, char** domain);
typedef boolean (*pVerifyCertificate)(freerdp* instance, char* subject, char* issuer, char* fingerprint);
typedef boolean (*pVerifyChangedCertificate)(freerdp* instance, char* subject, char* issuer, char* new_fingerprint, char* old_fingerprint);
typedef int (*pSendChannelData)(freerdp* instance, int channelId, uint8* data, int size);
typedef int (*pReceiveChannelData)(freerdp* instance, int channelId, uint8* data, int size, int flags, int total_size);
@@ -163,7 +164,11 @@ struct rdp_freerdp
pVerifyCertificate VerifyCertificate; /**< (offset 51)
Callback for certificate validation.
Used to verify that an unknown certificate is trusted. */
uint32 paddingD[64 - 52]; /* 52 */
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. */
uint32 paddingD[64 - 51]; /* 51 */
pSendChannelData SendChannelData; /* (offset 64)
Callback for sending data to a channel.

View File

@@ -129,6 +129,68 @@ int certificate_data_match(rdpCertificateStore* certificate_store, rdpCertificat
return match;
}
void certificate_data_replace(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
{
FILE* fp;
int length;
char* data;
char* pline;
long int size;
fp = certificate_store->fp;
if (!fp)
return;
// Read the current contents of the file.
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (size < 1)
return;
data = (char*) xmalloc(size + 2);
if (fread(data, size, 1, fp) != 1)
{
xfree(data);
return;
}
// 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
while (pline != NULL)
{
length = strlen(pline);
if (length > 0)
{
char* hostname = pline, *fingerprint;
length = strcspn(pline, " \t");
hostname[length] = '\0';
/* If this is the replaced hostname, use the updated fingerprint. */
if (strcmp(hostname, certificate_data->hostname) == 0)
fingerprint = certificate_data->fingerprint;
else
fingerprint = &hostname[length + 1];
fprintf(fp, "%s %s\n", hostname, fingerprint);
}
pline = strtok(NULL, "\n");
}
fclose(fp);
xfree(data);
}
void certificate_data_print(rdpCertificateStore* certificate_store, rdpCertificateData* certificate_data)
{
FILE* fp;

View File

@@ -434,6 +434,7 @@ boolean tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname)
char* issuer;
char* subject;
char* fingerprint;
freerdp* instance = (freerdp*) tls->settings->instance;
boolean accept_certificate = false;
issuer = crypto_cert_issuer(cert->px509);
@@ -446,9 +447,6 @@ boolean tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname)
if (match == 1)
{
/* no entry was found in known_hosts file, prompt user for manual verification */
freerdp* instance = (freerdp*) tls->settings->instance;
if (!hostname_match)
tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count);
@@ -469,9 +467,23 @@ boolean tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname)
}
else if (match == -1)
{
/* entry was found in known_hosts file, but fingerprint does not match */
/* entry was found in known_hosts file, but fingerprint does not match. ask user to use it */
tls_print_certificate_error(hostname, fingerprint);
verification_status = false; /* failure! */
if (instance->VerifyChangedCertificate)
accept_certificate = instance->VerifyChangedCertificate(instance, subject, issuer, fingerprint, "");
if (!accept_certificate)
{
/* user did not accept, abort and do not change known_hosts file */
verification_status = false; /* failure! */
}
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! */
}
}
else if (match == 0)
{
@@ -518,9 +530,6 @@ void tls_print_certificate_error(char* hostname, char* fingerprint)
printf("It is also possible that a host key has just been changed.\n");
printf("The fingerprint for the host key sent by the remote host is\n%s\n", fingerprint);
printf("Please contact your system administrator.\n");
printf("Add correct host key in ~/.freerdp/known_hosts to get rid of this message.\n");
printf("Host key for %s has changed and you have requested strict checking.\n", hostname);
printf("Host key verification failed.\n");
}
void tls_print_certificate_name_mismatch_error(char* hostname, char* common_name, char** alt_names, int alt_names_count)