From d5b8585a39931518504f53c6a643cd6a5e4d789d Mon Sep 17 00:00:00 2001 From: davewheel Date: Thu, 21 Jan 2016 00:17:59 +0100 Subject: [PATCH] Allow to specify the raw content of crypto materials Sometime it's possible that your server application doesn't have access to files (when running in a very restricted environment for example). This patch allows to ship the private key and certificate as a string. Sponsored by: Wheel Systems (http://www.wheelsystems.com) --- include/freerdp/crypto/tls.h | 2 +- include/freerdp/settings.h | 9 +++- libfreerdp/common/settings.c | 21 +++++++++ libfreerdp/core/certificate.c | 77 +++++++++++++++++--------------- libfreerdp/core/certificate.h | 1 + libfreerdp/core/nego.c | 2 +- libfreerdp/core/peer.c | 10 ++++- libfreerdp/core/settings.c | 6 +++ libfreerdp/core/transport.c | 4 +- libfreerdp/crypto/tls.c | 82 ++++++++++++++++++++++++++++++++--- 10 files changed, 168 insertions(+), 46 deletions(-) diff --git a/include/freerdp/crypto/tls.h b/include/freerdp/crypto/tls.h index 8d8daeca1..06dc97c68 100644 --- a/include/freerdp/crypto/tls.h +++ b/include/freerdp/crypto/tls.h @@ -89,7 +89,7 @@ struct rdp_tls #endif FREERDP_API int tls_connect(rdpTls* tls, BIO *underlying); -FREERDP_API BOOL tls_accept(rdpTls* tls, BIO *underlying, const char* cert_file, const char* privatekey_file); +FREERDP_API BOOL tls_accept(rdpTls* tls, BIO *underlying, rdpSettings *settings); FREERDP_API BOOL tls_send_alert(rdpTls* tls); FREERDP_API int tls_write_all(rdpTls* tls, const BYTE* data, int length); diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index d96d35293..268c6351e 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -655,6 +655,10 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_RdpServerRsaKey 1413 #define FreeRDP_RdpServerCertificate 1414 #define FreeRDP_ExternalCertificateManagement 1415 +#define FreeRDP_CertificateContent 1416 +#define FreeRDP_PrivateKeyContent 1417 +#define FreeRDP_RdpKeyContent 1418 + #define FreeRDP_Workarea 1536 #define FreeRDP_Fullscreen 1537 #define FreeRDP_PercentScreen 1538 @@ -1071,7 +1075,10 @@ struct rdp_settings ALIGN64 rdpRsaKey* RdpServerRsaKey; /* 1413 */ ALIGN64 rdpCertificate* RdpServerCertificate; /* 1414 */ ALIGN64 BOOL ExternalCertificateManagement; /* 1415 */ - UINT64 padding1472[1472 - 1416]; /* 1416 */ + ALIGN64 char *CertificateContent; /* 1416 */ + ALIGN64 char *PrivateKeyContent; /* 1417 */ + ALIGN64 char* RdpKeyContent; /* 1418 */ + UINT64 padding1472[1472 - 1419]; /* 1419 */ UINT64 padding1536[1536 - 1472]; /* 1472 */ /** diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index b31d3b242..859357a2a 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -2381,6 +2381,15 @@ char* freerdp_get_param_string(rdpSettings* settings, int id) case FreeRDP_RdpKeyFile: return settings->RdpKeyFile; + case FreeRDP_CertificateContent: + return settings->CertificateContent; + + case FreeRDP_PrivateKeyContent: + return settings->PrivateKeyContent; + + case FreeRDP_RdpKeyContent: + return settings->RdpKeyContent; + case FreeRDP_WindowTitle: return settings->WindowTitle; @@ -2551,6 +2560,18 @@ int freerdp_set_param_string(rdpSettings* settings, int id, const char* param) tmp = &settings->PrivateKeyFile; break; + case FreeRDP_CertificateContent: + tmp = &settings->CertificateContent; + break; + + case FreeRDP_PrivateKeyContent: + tmp = &settings->PrivateKeyContent; + break; + + case FreeRDP_RdpKeyContent: + tmp = &settings->RdpKeyContent; + break; + case FreeRDP_RdpKeyFile: tmp = &settings->RdpKeyFile; break; diff --git a/libfreerdp/core/certificate.c b/libfreerdp/core/certificate.c index 3fde935f5..a793af831 100644 --- a/libfreerdp/core/certificate.c +++ b/libfreerdp/core/certificate.c @@ -662,54 +662,22 @@ BOOL certificate_read_server_certificate(rdpCertificate* certificate, BYTE* serv return ret; } -rdpRsaKey* key_new(const char* keyfile) +rdpRsaKey* key_new_from_content(const char *keycontent, const char *keyfile) { BIO* bio = NULL; - FILE* fp = NULL; RSA* rsa = NULL; - int length; - BYTE* buffer = NULL; rdpRsaKey* key = NULL; key = (rdpRsaKey*) calloc(1, sizeof(rdpRsaKey)); - if (!key) return NULL; - fp = fopen(keyfile, "r+b"); - - if (!fp) - { - WLog_ERR(TAG, "unable to open RSA key file %s: %s.", keyfile, strerror(errno)); - goto out_free; - } - - if (fseek(fp, 0, SEEK_END) < 0) - goto out_free; - if ((length = ftell(fp)) < 0) - goto out_free; - if (fseek(fp, 0, SEEK_SET) < 0) - goto out_free; - - buffer = (BYTE*) malloc(length); - - if (!buffer) - goto out_free; - - if (fread((void*) buffer, length, 1, fp) != 1) - goto out_free; - fclose(fp); - fp = NULL; - - bio = BIO_new_mem_buf((void*) buffer, length); - + bio = BIO_new_mem_buf((void *)keycontent, strlen(keycontent)); if (!bio) goto out_free; rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); BIO_free(bio); - free(buffer); - buffer = NULL; if (!rsa) { @@ -764,11 +732,50 @@ out_free_modulus: free(key->Modulus); out_free_rsa: RSA_free(rsa); +out_free: + free(key); + return NULL; +} + + +rdpRsaKey* key_new(const char* keyfile) +{ + FILE* fp = NULL; + int length; + char* buffer = NULL; + rdpRsaKey* key = NULL; + + fp = fopen(keyfile, "r+b"); + if (!fp) + { + WLog_ERR(TAG, "unable to open RSA key file %s: %s.", keyfile, strerror(errno)); + goto out_free; + } + + if (fseek(fp, 0, SEEK_END) < 0) + goto out_free; + if ((length = ftell(fp)) < 0) + goto out_free; + if (fseek(fp, 0, SEEK_SET) < 0) + goto out_free; + + buffer = (char *)malloc(length + 1); + if (!buffer) + goto out_free; + + if (fread((void*) buffer, length, 1, fp) != 1) + goto out_free; + fclose(fp); + buffer[length] = '\0'; + + key = key_new_from_content(buffer, keyfile); + free(buffer); + return key; + out_free: if (fp) fclose(fp); free(buffer); - free(key); return NULL; } diff --git a/libfreerdp/core/certificate.h b/libfreerdp/core/certificate.h index 6b3195f35..a48701ba3 100644 --- a/libfreerdp/core/certificate.h +++ b/libfreerdp/core/certificate.h @@ -59,6 +59,7 @@ rdpCertificate* certificate_new(void); void certificate_free(rdpCertificate* certificate); rdpRsaKey* key_new(const char *keyfile); +rdpRsaKey* key_new_from_content(const char *keycontent, const char *keyfile); void key_free(rdpRsaKey* key); #define CERTIFICATE_TAG FREERDP_TAG("core.certificate") diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 67fcb6392..7cb6209fa 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -1086,7 +1086,7 @@ BOOL nego_send_negotiation_response(rdpNego* nego) settings->EncryptionLevel = ENCRYPTION_LEVEL_NONE; } - if (!settings->RdpServerRsaKey && !settings->RdpKeyFile) + if (!settings->RdpServerRsaKey && !settings->RdpKeyFile && !settings->RdpKeyContent) { WLog_ERR(TAG, "Missing server certificate"); return FALSE; diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c index 84b4dc926..8318c4d8d 100644 --- a/libfreerdp/core/peer.c +++ b/libfreerdp/core/peer.c @@ -219,13 +219,21 @@ static BOOL freerdp_peer_initialize(freerdp_peer* client) if (settings->RdpKeyFile) { settings->RdpServerRsaKey = key_new(settings->RdpKeyFile); - if (!settings->RdpServerRsaKey) { WLog_ERR(TAG, "invalid RDP key file %s", settings->RdpKeyFile); return FALSE; } } + else if (settings->RdpKeyContent) + { + settings->RdpServerRsaKey = key_new_from_content(settings->RdpKeyContent, NULL); + if (!settings->RdpServerRsaKey) + { + WLog_ERR(TAG, "invalid RDP key content"); + return FALSE; + } + } return TRUE; } diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 0ddd7d2c4..bab861255 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -597,6 +597,9 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) CHECKED_STRDUP(CertificateFile); /* 1410 */ CHECKED_STRDUP(PrivateKeyFile); /* 1411 */ CHECKED_STRDUP(RdpKeyFile); /* 1412 */ + CHECKED_STRDUP(CertificateContent); /* 1416 */ + CHECKED_STRDUP(PrivateKeyContent); /* 1417 */ + CHECKED_STRDUP(RdpKeyContent); /* 1418 */ CHECKED_STRDUP(WindowTitle); /* 1542 */ CHECKED_STRDUP(WmClass); /* 1549 */ CHECKED_STRDUP(ComputerName); /* 1664 */ @@ -924,6 +927,9 @@ void freerdp_settings_free(rdpSettings* settings) free(settings->ServerCertificate); free(settings->RdpKeyFile); certificate_free(settings->RdpServerCertificate); + free(settings->CertificateContent); + free(settings->PrivateKeyContent); + free(settings->RdpKeyContent); free(settings->ClientAutoReconnectCookie); free(settings->ServerAutoReconnectCookie); free(settings->ClientTimeZone); diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 0d132fe73..f7678b9fd 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -322,7 +322,7 @@ BOOL transport_accept_tls(rdpTransport* transport) transport->layer = TRANSPORT_LAYER_TLS; - if (!tls_accept(transport->tls, transport->frontBio, settings->CertificateFile, settings->PrivateKeyFile)) + if (!tls_accept(transport->tls, transport->frontBio, settings)) return FALSE; transport->frontBio = transport->tls->bio; @@ -340,7 +340,7 @@ BOOL transport_accept_nla(rdpTransport* transport) transport->layer = TRANSPORT_LAYER_TLS; - if (!tls_accept(transport->tls, transport->frontBio, settings->CertificateFile, settings->PrivateKeyFile)) + if (!tls_accept(transport->tls, transport->frontBio, settings)) return FALSE; transport->frontBio = transport->tls->bio; diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 90de48b2f..14d0c2ce2 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -823,9 +823,12 @@ static void tls_openssl_tlsext_debug_callback(SSL *s, int client_server, } #endif -BOOL tls_accept(rdpTls* tls, BIO* underlying, const char* cert_file, const char* privatekey_file) +BOOL tls_accept(rdpTls* tls, BIO* underlying, rdpSettings *settings) { long options = 0; + BIO *bio; + RSA *rsa; + X509 *x509; /** * SSL_OP_NO_SSLv2: @@ -867,16 +870,85 @@ BOOL tls_accept(rdpTls* tls, BIO* underlying, const char* cert_file, const char* if (!tls_prepare(tls, underlying, SSLv23_server_method(), options, FALSE)) return FALSE; - if (SSL_use_RSAPrivateKey_file(tls->ssl, privatekey_file, SSL_FILETYPE_PEM) <= 0) + if (settings->PrivateKeyFile) { - WLog_ERR(TAG, "SSL_CTX_use_RSAPrivateKey_file failed"); - WLog_ERR(TAG, "PrivateKeyFile: %s", privatekey_file); + bio = BIO_new_file(settings->PrivateKeyFile, "rb+"); + if (!bio) + { + WLog_ERR(TAG, "BIO_new_file failed for private key %s", settings->PrivateKeyFile); + return FALSE; + } + } + else if (settings->PrivateKeyContent) + { + bio = BIO_new_mem_buf(settings->PrivateKeyContent, strlen(settings->PrivateKeyContent)); + if (!bio) + { + WLog_ERR(TAG, "BIO_new_mem_buf failed for private key"); + return FALSE; + } + } + else + { + WLog_ERR(TAG, "no private key defined"); return FALSE; } - if (SSL_use_certificate_file(tls->ssl, cert_file, SSL_FILETYPE_PEM) <= 0) + rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + + if (!rsa) + { + WLog_ERR(TAG, "invalid private key"); + return FALSE; + } + + if (SSL_use_RSAPrivateKey(tls->ssl, rsa) <= 0) + { + WLog_ERR(TAG, "SSL_CTX_use_RSAPrivateKey_file failed"); + RSA_free(rsa); + return FALSE; + } + + + if (settings->CertificateFile) + { + bio = BIO_new_file(settings->CertificateFile, "rb+"); + if (!bio) + { + WLog_ERR(TAG, "BIO_new_file failed for certificate %s", settings->CertificateFile); + return FALSE; + } + } + else if (settings->CertificateContent) + { + bio = BIO_new_mem_buf(settings->CertificateContent, strlen(settings->CertificateContent)); + if (!bio) + { + WLog_ERR(TAG, "BIO_new_mem_buf failed for certificate"); + return FALSE; + } + } + else + { + WLog_ERR(TAG, "no certificate defined"); + return FALSE; + } + + x509 = PEM_read_bio_X509(bio, NULL, NULL, 0); + BIO_free(bio); + + if (!x509) + { + WLog_ERR(TAG, "invalid certificate"); + return FALSE; + } + + + if (SSL_use_certificate(tls->ssl, x509) <= 0) { WLog_ERR(TAG, "SSL_use_certificate_file failed"); + X509_free(x509); return FALSE; }