diff --git a/include/freerdp/crypto/certificate.h b/include/freerdp/crypto/certificate.h index 537829615..537d9210e 100644 --- a/include/freerdp/crypto/certificate.h +++ b/include/freerdp/crypto/certificate.h @@ -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 } diff --git a/include/freerdp/crypto/crypto.h b/include/freerdp/crypto/crypto.h index 44c8eef38..25d98bde3 100644 --- a/include/freerdp/crypto/crypto.h +++ b/include/freerdp/crypto/crypto.h @@ -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 diff --git a/include/freerdp/crypto/tls.h b/include/freerdp/crypto/tls.h index b8b815730..8d8daeca1 100644 --- a/include/freerdp/crypto/tls.h +++ b/include/freerdp/crypto/tls.h @@ -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); diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index c953d8a41..2df5e4c3e 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -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. */ diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 806d9bf03..7f70c75fa 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -28,6 +28,8 @@ #include #endif +#include + #include #include #include diff --git a/libfreerdp/crypto/certificate.c b/libfreerdp/crypto/certificate.c index fbe804d00..c9a8ecbcb 100644 --- a/libfreerdp/crypto/certificate.c +++ b/libfreerdp/crypto/certificate.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -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 #include #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); diff --git a/libfreerdp/crypto/crypto.c b/libfreerdp/crypto/crypto.c index 3c54c2c3b..b82f38b54 100644 --- a/libfreerdp/crypto/crypto.c +++ b/libfreerdp/crypto/crypto.c @@ -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); diff --git a/libfreerdp/crypto/test/CMakeLists.txt b/libfreerdp/crypto/test/CMakeLists.txt index 86f5faf23..acaf43ed7 100644 --- a/libfreerdp/crypto/test/CMakeLists.txt +++ b/libfreerdp/crypto/test/CMakeLists.txt @@ -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 diff --git a/libfreerdp/crypto/test/TestKnownHosts.c b/libfreerdp/crypto/test/TestKnownHosts.c new file mode 100644 index 000000000..0adf02ddf --- /dev/null +++ b/libfreerdp/crypto/test/TestKnownHosts.c @@ -0,0 +1,322 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 Armin Novak + * + * 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 +#include +#include + +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; ibio = 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) diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h index a90df7724..ddcaeaf46 100644 --- a/winpr/include/winpr/string.h +++ b/winpr/include/winpr/string.h @@ -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 diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c index 776167f39..da9185f80 100644 --- a/winpr/libwinpr/crt/string.c +++ b/winpr/libwinpr/crt/string.c @@ -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; +} + +