Added command line or dialog certificate handling.
authorArmin Novak <armin.novak@thincast.com>
Thu, 29 Aug 2019 13:17:59 +0000 (15:17 +0200)
committerArmin Novak <armin.novak@thincast.com>
Thu, 29 Aug 2019 17:03:03 +0000 (19:03 +0200)
client/Windows/wf_client.c
client/Windows/wf_client.h

index b11a507..80be44a 100644 (file)
 
 #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
+       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_certificate_ex(freerdp* instance,
+                                      const char* host,
+                                      UINT16 port,
+                                      const char* common_name,
+                                      const char* subject,
+                                      const char* issuer,
+                                      const char* fingerprint,
+                                      DWORD flags)
+{
+       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 */
-       return 2;
+       switch(what)
+       {
+       case IDOK:
+               return 1;
+       case IDNO:
+               return 2;
+       default:
+               return 0;
+       }
 }
 
-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_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)
 {
-       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"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;
 }
 
index 848f3ad..ad8e327 100644 (file)
@@ -132,6 +132,7 @@ struct wf_context
 
        RailClientContext* rail;
        wHashTable* railWindows;
+       BOOL isConsole;
 };
 
 /**