From 81c60631a927dd54afaf974a41aaa840fc143f2c Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Thu, 29 Aug 2019 13:47:53 +0200 Subject: [PATCH 1/4] Reattach to cmd if wfreerdp was started from one. --- client/Windows/wf_client.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/Windows/wf_client.c b/client/Windows/wf_client.c index 78ba61a29..b11a50792 100644 --- a/client/Windows/wf_client.c +++ b/client/Windows/wf_client.c @@ -60,7 +60,7 @@ static int wf_create_console(void) { - if (!AllocConsole()) + if (!AttachConsole(ATTACH_PARENT_PROCESS)) return 1; freopen("CONOUT$", "w", stdout); @@ -916,9 +916,8 @@ static BOOL wfreerdp_client_global_init(void) WSADATA wsaData; WSAStartup(0x101, &wsaData); -#if defined(WITH_DEBUG) || defined(_DEBUG) + wf_create_console(); -#endif freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0); return TRUE; } From 1a322bb98b1fba7ac47ac4493b12efa3c21c93f1 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Thu, 29 Aug 2019 15:17:59 +0200 Subject: [PATCH 2/4] Added command line or dialog certificate handling. --- client/Windows/wf_client.c | 222 ++++++++++++++++++++++++++----------- client/Windows/wf_client.h | 1 + 2 files changed, 158 insertions(+), 65 deletions(-) diff --git a/client/Windows/wf_client.c b/client/Windows/wf_client.c index b11a50792..80be44a65 100644 --- a/client/Windows/wf_client.c +++ b/client/Windows/wf_client.c @@ -58,15 +58,17 @@ #define TAG CLIENT_TAG("windows") -static int wf_create_console(void) +static BOOL wf_create_console(void) { if (!AttachConsole(ATTACH_PARENT_PROCESS)) - return 1; + return FALSE; freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); + WLog_INFO(TAG, "Debug console created."); - return 0; + + return TRUE; } static BOOL wf_end_paint(rdpContext* context) @@ -397,16 +399,15 @@ static BOOL wf_post_connect(freerdp* instance) return TRUE; } -static BOOL wf_post_disconnect(freerdp* instance) +static void wf_post_disconnect(freerdp* instance) { wfContext* wfc; if (!instance || !instance->context || !instance->settings) - return FALSE; + return; wfc = (wfContext*) instance->context; free(wfc->window_title); - return TRUE; } static CREDUI_INFOA wfUiInfo = @@ -433,8 +434,8 @@ static BOOL wf_authenticate_raw(freerdp* instance, const char* title, ZeroMemory(Password, sizeof(Password)); dwFlags = CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_EXCLUDE_CERTIFICATES; status = CredUIPromptForCredentialsA(&wfUiInfo, title, NULL, 0, - UserName, CREDUI_MAX_USERNAME_LENGTH + 1, - Password, CREDUI_MAX_PASSWORD_LENGTH + 1, &fSave, dwFlags); + UserName, CREDUI_MAX_USERNAME_LENGTH + 1, + Password, CREDUI_MAX_PASSWORD_LENGTH + 1, &fSave, dwFlags); if (status != NO_ERROR) { @@ -494,64 +495,149 @@ static BOOL wf_gw_authenticate(freerdp* instance, return wf_authenticate_raw(instance, tmp, username, password, domain); } -static DWORD wf_verify_certificate(freerdp* instance, - const char* common_name, - const char* subject, - const char* issuer, - const char* fingerprint, - BOOL host_mismatch) +static WCHAR* wf_format_text(const WCHAR* fmt, ...) { -#if 0 - DWORD mode; - int read_size; - DWORD read_count; - TCHAR answer[2]; - TCHAR* read_buffer; - HANDLE input_handle; -#endif - WLog_INFO(TAG, "Certificate details:"); - WLog_INFO(TAG, "\tCommonName: %s", common_name); - WLog_INFO(TAG, "\tSubject: %s", subject); - WLog_INFO(TAG, "\tIssuer: %s", issuer); - WLog_INFO(TAG, "\tThumbprint: %s", fingerprint); - WLog_INFO(TAG, "\tHostMismatch: %s", host_mismatch ? "Yes" : "No"); - 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 OpenSSL documentation on how to add a private CA to the store.\n"); - /* TODO: ask for user validation */ -#if 0 - input_handle = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(input_handle, &mode); - mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT; - SetConsoleMode(input_handle, mode); -#endif - /* return 1 to accept and store a certificate, 2 to accept - * a certificate only for this session, 0 otherwise */ - return 2; + int rc; + size_t size = 1024; + WCHAR* buffer = calloc(size, sizeof(WCHAR)); + if (!buffer) + return NULL; + + do + { + WCHAR* tmp; + va_list ap; + va_start(ap, fmt); + rc = vswprintf_s(buffer, size, fmt, ap); + va_end(ap); + if (rc <= 0) + goto fail; + + if ((size_t)rc < size) + return buffer; + + size = (size_t)rc + 1; + tmp = realloc(buffer, size * sizeof(WCHAR)); + if (!tmp) + goto fail; + + buffer = tmp; + } + while(TRUE); + +fail: + free(buffer); + return NULL; } -static DWORD wf_verify_changed_certificate(freerdp* instance, - const char* common_name, - const char* subject, const char* issuer, - const char* fingerprint, - const char* old_subject, const char* old_issuer, - const char* old_fingerprint) +static DWORD wf_verify_certificate_ex(freerdp* instance, + const char* host, + UINT16 port, + const char* common_name, + const char* subject, + const char* issuer, + const char* fingerprint, + DWORD flags) { - WLog_ERR(TAG, "!!! Certificate has changed !!!"); - WLog_ERR(TAG, "New Certificate details:"); - WLog_ERR(TAG, "\tSubject: %s", subject); - WLog_ERR(TAG, "\tIssuer: %s", issuer); - WLog_ERR(TAG, "\tThumbprint: %s", fingerprint); - WLog_ERR(TAG, "Old Certificate details:"); - WLog_ERR(TAG, "\tSubject: %s", old_subject); - WLog_ERR(TAG, "\tIssuer: %s", old_issuer); - WLog_ERR(TAG, "\tThumbprint: %s", old_fingerprint); - WLog_ERR(TAG, - "The above X.509 certificate does not match the certificate used for previous connections. " - "This may indicate that the certificate has been tampered with." - "Please contact the administrator of the RDP server and clarify."); - return 0; + WCHAR* buffer; + WCHAR* caption; + int what = IDCANCEL; + + buffer = wf_format_text(L"Certificate details:\n" + L"\tCommonName: %S\n" + L"\tSubject: %S\n" + L"\tIssuer: %S\n" + L"\tThumbprint: %S\n" + L"\tHostMismatch: %S\n" + L"\n" + L"The above X.509 certificate could not be verified, possibly because you do not have " + L"the CA certificate in your certificate store, or the certificate has expired. " + L"Please look at the OpenSSL documentation on how to add a private CA to the store.\n" + L"\n" + L"YES\tAccept permanently\n" + L"NO\tAccept for this session only\n" + L"CANCEL\tAbort connection\n", + common_name, subject, issuer, fingerprint, flags & VERIFY_CERT_FLAG_MISMATCH ? "Yes" : "No"); + caption = wf_format_text(L"Verify certificate for %S:%hu", host, port); + + if (!buffer || !caption) + goto fail; + + what = MessageBoxW(NULL, buffer, caption, MB_YESNOCANCEL); +fail: + free(buffer); + free(caption); + + /* return 1 to accept and store a certificate, 2 to accept + * a certificate only for this session, 0 otherwise */ + switch(what) + { + case IDOK: + return 1; + case IDNO: + return 2; + default: + return 0; + } +} + +static DWORD wf_verify_changed_certificate_ex(freerdp* instance, + const char* host, + UINT16 port, + const char* common_name, + const char* subject, + const char* issuer, + const char* new_fingerprint, + const char* old_subject, + const char* old_issuer, + const char* old_fingerprint, + DWORD flags) +{ + WCHAR* buffer; + WCHAR* caption; + int what = IDCANCEL; + + buffer = wf_format_text( L"New Certificate details:\n" + L"\tCommonName: %S\n" + L"\tSubject: %S\n" + L"\tIssuer: %S\n" + L"\tThumbprint: %S\n" + L"\tHostMismatch: %S\n" + L"\n" + L"Old Certificate details:\n" + L"\tSubject: %S\n" + L"\tIssuer: %S\n" + L"\tThumbprint: %S" + L"The above X.509 certificate could not be verified, possibly because you do not have " + L"the CA certificate in your certificate store, or the certificate has expired. " + L"Please look at the OpenSSL documentation on how to add a private CA to the store.\n" + L"\n" + L"YES\tAccept permanently\n" + L"NO\tAccept for this session only\n" + L"CANCEL\tAbort connection\n", + common_name, subject, issuer, new_fingerprint, flags & VERIFY_CERT_FLAG_MISMATCH ? "Yes" : "No", + old_subject, old_issuer, old_fingerprint); + caption = wf_format_text(L"Verify certificate change for %S:%hu", host, port); + + if (!buffer || !caption) + goto fail; + + what = MessageBoxW(NULL, buffer, caption, MB_YESNOCANCEL); +fail: + free(buffer); + free(caption); + + /* return 1 to accept and store a certificate, 2 to accept + * a certificate only for this session, 0 otherwise */ + switch(what) + { + case IDOK: + return 1; + case IDNO: + return 2; + default: + return 0; + } } static DWORD WINAPI wf_input_thread(LPVOID arg) @@ -917,7 +1003,6 @@ static BOOL wfreerdp_client_global_init(void) WSAStartup(0x101, &wsaData); - wf_create_console(); freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0); return TRUE; } @@ -929,6 +1014,12 @@ static void wfreerdp_client_global_uninit(void) static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context) { + wfContext* wfc = (wfContext*)context; + if (!wfc) + return FALSE; + + wfc->isConsole = wf_create_console(); + if (!(wfreerdp_client_global_init())) return FALSE; @@ -937,8 +1028,9 @@ static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context) instance->PostDisconnect = wf_post_disconnect; instance->Authenticate = wf_authenticate; instance->GatewayAuthenticate = wf_gw_authenticate; - instance->VerifyCertificate = wf_verify_certificate; - instance->VerifyChangedCertificate = wf_verify_changed_certificate; + instance->VerifyCertificateEx = wf_verify_certificate_ex; + instance->VerifyChangedCertificateEx = wf_verify_changed_certificate_ex; + return TRUE; } diff --git a/client/Windows/wf_client.h b/client/Windows/wf_client.h index 848f3ad00..ad8e32773 100644 --- a/client/Windows/wf_client.h +++ b/client/Windows/wf_client.h @@ -132,6 +132,7 @@ struct wf_context RailClientContext* rail; wHashTable* railWindows; + BOOL isConsole; }; /** From 53ee0fbc3370fc48c501e6d9c437d44548fa83b5 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Thu, 29 Aug 2019 19:28:50 +0200 Subject: [PATCH 3/4] Added GUI/console switch for input callbacks. (disabled until AttachConsole + stdin work) --- client/Windows/wf_client.c | 49 +++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/client/Windows/wf_client.c b/client/Windows/wf_client.c index 80be44a65..93af8ea6e 100644 --- a/client/Windows/wf_client.c +++ b/client/Windows/wf_client.c @@ -65,6 +65,8 @@ static BOOL wf_create_console(void) freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); + freopen("CONIN$", "r", stdin); + clearerr(stdin); WLog_INFO(TAG, "Debug console created."); @@ -422,20 +424,30 @@ static CREDUI_INFOA wfUiInfo = static BOOL wf_authenticate_raw(freerdp* instance, const char* title, char** username, char** password, char** domain) { + wfContext* wfc; BOOL fSave; DWORD status; DWORD dwFlags; - char UserName[CREDUI_MAX_USERNAME_LENGTH + 1]; - char Password[CREDUI_MAX_PASSWORD_LENGTH + 1]; - char User[CREDUI_MAX_USERNAME_LENGTH + 1]; - char Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1]; + char UserName[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 }; + char Password[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 }; + char User[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 }; + char Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 }; + + if (!instance || !instance->context) + return FALSE; + wfc = (wfContext*) instance->context; + fSave = FALSE; - ZeroMemory(UserName, sizeof(UserName)); - ZeroMemory(Password, sizeof(Password)); dwFlags = CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_EXCLUDE_CERTIFICATES; - status = CredUIPromptForCredentialsA(&wfUiInfo, title, NULL, 0, - UserName, CREDUI_MAX_USERNAME_LENGTH + 1, - Password, CREDUI_MAX_PASSWORD_LENGTH + 1, &fSave, dwFlags); + + if (wfc->isConsole) + status = CredUICmdLinePromptForCredentialsA(title, NULL, 0, + UserName, CREDUI_MAX_USERNAME_LENGTH + 1, + Password, CREDUI_MAX_PASSWORD_LENGTH + 1, &fSave, dwFlags); + else + status = CredUIPromptForCredentialsA(&wfUiInfo, title, NULL, 0, + UserName, CREDUI_MAX_USERNAME_LENGTH + 1, + Password, CREDUI_MAX_PASSWORD_LENGTH + 1, &fSave, dwFlags); if (status != NO_ERROR) { @@ -443,8 +455,6 @@ static BOOL wf_authenticate_raw(freerdp* instance, const char* title, return FALSE; } - ZeroMemory(User, sizeof(User)); - ZeroMemory(Domain, sizeof(Domain)); status = CredUIParseUserNameA(UserName, User, sizeof(User), Domain, sizeof(Domain)); //WLog_ERR(TAG, "User: %s Domain: %s Password: %s", User, Domain, Password); @@ -1018,7 +1028,10 @@ static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context) if (!wfc) return FALSE; - wfc->isConsole = wf_create_console(); + // AttachConsole and stdin do not work well. + // Use GUI input dialogs instead of command line ones. + //wfc->isConsole = wf_create_console(); + wf_create_console(); if (!(wfreerdp_client_global_init())) return FALSE; @@ -1028,8 +1041,16 @@ static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context) instance->PostDisconnect = wf_post_disconnect; instance->Authenticate = wf_authenticate; instance->GatewayAuthenticate = wf_gw_authenticate; - instance->VerifyCertificateEx = wf_verify_certificate_ex; - instance->VerifyChangedCertificateEx = wf_verify_changed_certificate_ex; + if (wfc->isConsole) + { + instance->VerifyCertificateEx = client_cli_verify_certificate_ex; + instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex; + } + else + { + instance->VerifyCertificateEx = wf_verify_certificate_ex; + instance->VerifyChangedCertificateEx = wf_verify_changed_certificate_ex; + } return TRUE; } From a5403448b6ce41b57f27a7235fe83aa513e66d68 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Mon, 4 Nov 2019 11:18:53 +0100 Subject: [PATCH 4/4] Fixed review remarks. --- client/Windows/wf_client.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/Windows/wf_client.c b/client/Windows/wf_client.c index 93af8ea6e..f4208aca9 100644 --- a/client/Windows/wf_client.c +++ b/client/Windows/wf_client.c @@ -582,7 +582,7 @@ fail: * a certificate only for this session, 0 otherwise */ switch(what) { - case IDOK: + case IDYES: return 1; case IDNO: return 2; @@ -641,7 +641,7 @@ fail: * a certificate only for this session, 0 otherwise */ switch(what) { - case IDOK: + case IDYES: return 1; case IDNO: return 2; @@ -1030,8 +1030,7 @@ static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context) // AttachConsole and stdin do not work well. // Use GUI input dialogs instead of command line ones. - //wfc->isConsole = wf_create_console(); - wf_create_console(); + wfc->isConsole = wf_create_console(); if (!(wfreerdp_client_global_init())) return FALSE;