From b0369cf284dd40b13e7d51c341008244d7bf0554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Mon, 18 Nov 2013 13:54:33 -0500 Subject: [PATCH] libfreerdp-core: add external certificate management, pass X509 PEM certificate through client callback --- include/freerdp/freerdp.h | 9 ++++--- include/freerdp/settings.h | 4 ++- libfreerdp/common/settings.c | 8 ++++++ libfreerdp/core/settings.c | 1 + libfreerdp/crypto/tls.c | 52 ++++++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index a7a4182f0..28064c27e 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -61,6 +61,7 @@ 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 int (*pVerifyX509Certificate)(freerdp* instance, BYTE* data, int length, DWORD flags); typedef int (*pLogonErrorInfo)(freerdp* instance, UINT32 data, UINT32 type); @@ -195,13 +196,15 @@ struct rdp_freerdp Used when a certificate differs from stored fingerprint. If returns TRUE, the new fingerprint will be trusted and old thrown out. */ - ALIGN64 pLogonErrorInfo LogonErrorInfo; /**< (offset 53) Callback for logon error info, important for logon system messages with RemoteApp */ + ALIGN64 pVerifyX509Certificate VerifyX509Certificate; /**< (offset 53) Callback for X509 certificate verification (PEM format) */ - ALIGN64 pPostDisconnect PostDisconnect; /**< (offset 54) + ALIGN64 pLogonErrorInfo LogonErrorInfo; /**< (offset 54) Callback for logon error info, important for logon system messages with RemoteApp */ + + ALIGN64 pPostDisconnect PostDisconnect; /**< (offset 55) Callback for cleaning up resources allocated by connect callbacks. */ - UINT64 paddingD[64 - 55]; /* 55 */ + UINT64 paddingD[64 - 56]; /* 56 */ ALIGN64 pSendChannelData SendChannelData; /* (offset 64) Callback for sending data to a channel. diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 59481d9dd..eaad54584 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -594,6 +594,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_RdpKeyFile 1412 #define FreeRDP_RdpServerRsaKey 1413 #define FreeRDP_RdpServerCertificate 1414 +#define FreeRDP_ExternalCertificateManagement 1415 #define FreeRDP_Workarea 1536 #define FreeRDP_Fullscreen 1537 #define FreeRDP_PercentScreen 1538 @@ -960,7 +961,8 @@ struct rdp_settings ALIGN64 char* RdpKeyFile; /* 1412 */ ALIGN64 rdpRsaKey* RdpServerRsaKey; /* 1413 */ ALIGN64 rdpCertificate* RdpServerCertificate; /* 1414 */ - UINT64 padding1472[1472 - 1350]; /* 1415 */ + ALIGN64 BOOL ExternalCertificateManagement; /* 1415 */ + UINT64 padding1472[1472 - 1416]; /* 1416 */ UINT64 padding1536[1536 - 1472]; /* 1472 */ /** diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index ba0bea378..ef4493f4e 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -665,6 +665,10 @@ BOOL freerdp_get_param_bool(rdpSettings* settings, int id) return settings->IgnoreCertificate; break; + case FreeRDP_ExternalCertificateManagement: + return settings->ExternalCertificateManagement; + break; + case FreeRDP_Workarea: return settings->Workarea; break; @@ -1129,6 +1133,10 @@ int freerdp_set_param_bool(rdpSettings* settings, int id, BOOL param) settings->IgnoreCertificate = param; break; + case FreeRDP_ExternalCertificateManagement: + settings->ExternalCertificateManagement = param; + break; + case FreeRDP_Workarea: settings->Workarea = param; break; diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 5870e49b5..0edabb5c9 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -620,6 +620,7 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) _settings->MstscCookieMode = settings->MstscCookieMode; /* 1152 */ _settings->SendPreconnectionPdu = settings->SendPreconnectionPdu; /* 1156 */ _settings->IgnoreCertificate = settings->IgnoreCertificate; /* 1408 */ + _settings->ExternalCertificateManagement = settings->ExternalCertificateManagement; /* 1415 */ _settings->Workarea = settings->Workarea; /* 1536 */ _settings->Fullscreen = settings->Fullscreen; /* 1537 */ _settings->GrabKeyboard = settings->GrabKeyboard; /* 1539 */ diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 3b97486b5..b7cda06df 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -582,6 +582,58 @@ BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname) BOOL verification_status = FALSE; rdpCertificateData* certificate_data; + if (tls->settings->ExternalCertificateManagement) + { + BIO* bio; + int status; + int length; + int offset; + BYTE* pemCert; + freerdp* instance = (freerdp*) tls->settings->instance; + + /** + * Don't manage certificates internally, leave it up entirely to the external client implementation + */ + + bio = BIO_new(BIO_s_mem()); + + status = PEM_write_bio_X509(bio, cert->px509); + + offset = 0; + length = 2048; + pemCert = (BYTE*) malloc(length + 1); + + status = BIO_read(bio, pemCert, length); + offset += status; + + while (offset >= length) + { + length *= 2; + pemCert = (BYTE*) realloc(pemCert, length + 1); + + status = BIO_read(bio, &pemCert[offset], length); + + if (status < 0) + break; + + offset += status; + } + + length = offset; + pemCert[length] = '\0'; + + status = -1; + + if (instance->VerifyX509Certificate) + { + status = instance->VerifyX509Certificate(instance, pemCert, length, 0); + } + + free(pemCert); + + return (status < 0) ? FALSE : TRUE; + } + /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) return TRUE; /* success! */