diff --git a/windows/win_res.h b/windows/win_res.h index 737c3a72..19e2657f 100644 --- a/windows/win_res.h +++ b/windows/win_res.h @@ -13,6 +13,8 @@ #define IDD_ABOUTBOX 111 #define IDD_RECONF 112 #define IDD_LICENCEBOX 113 +#define IDD_HK_ABSENT 114 +#define IDD_HK_WRONG 115 #define IDN_LIST 1001 #define IDN_COPY 1002 @@ -29,6 +31,12 @@ #define IDC_HELPBTN 1005 #define IDC_ABOUT 1006 +#define IDC_HK_ICON 98 +#define IDC_HK_TITLE 99 +#define IDC_HK_ACCEPT 1001 +#define IDC_HK_ONCE 1000 +#define IDC_HK_FINGERPRINT 1002 + #define ID_CUSTOM_CHMFILE 2000 #define TYPE_CUSTOM_CHMFILE 2000 diff --git a/windows/win_res.rc2 b/windows/win_res.rc2 index fc9f6dd9..cc907f21 100644 --- a/windows/win_res.rc2 +++ b/windows/win_res.rc2 @@ -62,4 +62,59 @@ BEGIN EDITTEXT IDA_TEXT, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END +/* No accelerators used */ +IDD_HK_ABSENT DIALOG DISCARDABLE 50, 50, 300, 148 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Security Alert" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "The server's host key is not cached in the registry. You have no", 100, 40, 20, 260, 8 + LTEXT "guarantee that the server is the computer you think it is.", 101, 40, 28, 260, 8 + LTEXT "The server's {KEYTYPE} key fingerprint is:", 102, 40, 40, 260, 8 + LTEXT "If you trust this host, press ""Accept"" to add the key to {APPNAME}'s", 103, 40, 60, 260, 8 + LTEXT "cache and carry on connecting.", 104, 40, 68, 260, 8 + LTEXT "If you want to carry on connecting just once, without adding the key", 105, 40, 80, 260, 8 + LTEXT "to the cache, press ""Connect Once"".", 106, 40, 88, 260, 8 + LTEXT "If you do not trust this host, press ""Cancel"" to abandon the connection.", 107, 40, 100, 260, 8 + + ICON "", IDC_HK_ICON, 10, 18, 0, 0 + + PUSHBUTTON "Cancel", IDCANCEL, 248, 128, 40, 14 + PUSHBUTTON "Accept", IDC_HK_ACCEPT, 128, 128, 40, 14 + PUSHBUTTON "Connect Once", IDC_HK_ONCE, 176, 128, 64, 14 + PUSHBUTTON "Help", IDHELP, 12, 128, 40, 14 + + EDITTEXT IDC_HK_FINGERPRINT, 40, 48, 260, 12, ES_READONLY | ES_LEFT, 0 +END + +/* No accelerators used */ +IDD_HK_WRONG DIALOG DISCARDABLE 50, 50, 300, 188 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Security Alert" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "WARNING - POTENTIAL SECURITY BREACH!", IDC_HK_TITLE, 40, 20, 260, 12 + + LTEXT "The server's host key does not match the one {APPNAME} has cached in", 100, 40, 36, 260, 8 + LTEXT "the registry. This means that either the server administrator has", 101, 40, 44, 260, 8 + LTEXT "changed the host key, or you have actually connected to another", 102, 40, 52, 260, 8 + LTEXT "computer pretending to be the server.", 103, 40, 60, 260, 8 + LTEXT "The new {KEYTYPE} key fingerprint is:", 104, 40, 72, 260, 8 + LTEXT "If you were expecting this change and trust the new key, press", 105, 40, 92, 260, 8 + LTEXT """Accept"" to update {APPNAME}'s cache and continue connecting.", 106, 40, 100, 260, 8 + LTEXT "If you want to carry on connecting but without updating the cache,", 107, 40, 112, 260, 8 + LTEXT "press ""Connect Once"".", 108, 40, 120, 260, 8 + LTEXT "If you want to abandon the connection completely, press ""Cancel"".", 109, 40, 132, 260, 8 + LTEXT "Pressing ""Cancel"" is the ONLY guaranteed safe choice.", 110, 40, 140, 260, 8 + + ICON "", IDC_HK_ICON, 10, 16, 0, 0 + + PUSHBUTTON "Cancel", IDCANCEL, 248, 168, 40, 14 + PUSHBUTTON "Accept", IDC_HK_ACCEPT, 128, 168, 40, 14 + PUSHBUTTON "Connect Once", IDC_HK_ONCE, 176, 168, 64, 14 + PUSHBUTTON "Help", IDHELP, 12, 168, 40, 14 + + EDITTEXT IDC_HK_FINGERPRINT, 40, 80, 260, 12, ES_READONLY | ES_LEFT, 0 +END + #include "version.rc2" diff --git a/windows/windlg.c b/windows/windlg.c index 8f0b6053..f862c5d8 100644 --- a/windows/windlg.c +++ b/windows/windlg.c @@ -835,6 +835,109 @@ void showabout(HWND hwnd) DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); } +struct hostkey_dialog_ctx { + const char *const *keywords; + const char *const *values; + const char *fingerprint; + LPCTSTR iconid; + const char *helpctx; +}; + +static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: { + strbuf *sb = strbuf_new(); + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *)lParam; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); + for (int id = 100;; id++) { + char buf[256]; + + if (!GetDlgItemText(hwnd, id, buf, (int)lenof(buf))) + break; + + strbuf_clear(sb); + for (const char *p = buf; *p ;) { + if (*p == '{') { + for (size_t i = 0; ctx->keywords[i]; i++) { + if (strstartswith(p, ctx->keywords[i])) { + p += strlen(ctx->keywords[i]); + put_datapl(sb, ptrlen_from_asciz(ctx->values[i])); + goto matched; + } + } + } else { + put_byte(sb, *p++); + } + matched:; + } + + SetDlgItemText(hwnd, id, sb->s); + } + strbuf_free(sb); + + SetDlgItemText(hwnd, IDC_HK_FINGERPRINT, ctx->fingerprint); + MakeDlgItemBorderless(hwnd, IDC_HK_FINGERPRINT); + + HANDLE icon = LoadImage( + NULL, ctx->iconid, IMAGE_ICON, + GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), + LR_SHARED); + SendDlgItemMessage(hwnd, IDC_HK_ICON, STM_SETICON, (WPARAM)icon, 0); + + if (!has_help()) { + HWND item = GetDlgItem(hwnd, IDHELP); + if (item) + DestroyWindow(item); + } + + return 1; + } + case WM_CTLCOLORSTATIC: { + HDC hdc = (HDC)wParam; + HWND control = (HWND)lParam; + + if (GetWindowLong(control, GWL_ID) == IDC_HK_TITLE) { + SetBkMode(hdc, TRANSPARENT); + HFONT prev_font = (HFONT)SelectObject( + hdc, (HFONT)GetStockObject(SYSTEM_FONT)); + LOGFONT lf; + if (GetObject(prev_font, sizeof(lf), &lf)) { + lf.lfWeight = FW_BOLD; + lf.lfHeight = lf.lfHeight * 3 / 2; + HFONT bold_font = CreateFontIndirect(&lf); + if (bold_font) + SelectObject(hdc, bold_font); + } + return (INT_PTR)GetSysColorBrush(COLOR_BTNFACE); + } + return 0; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_HK_ACCEPT: + case IDC_HK_ONCE: + case IDCANCEL: + EndDialog(hwnd, LOWORD(wParam)); + return 0; + case IDHELP: { + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + launch_help(hwnd, ctx->helpctx); + return 0; + } + } + return 0; + case WM_CLOSE: + EndDialog(hwnd, IDCANCEL); + return 0; + } + return 0; +} + int win_seat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, char *fingerprint, @@ -842,38 +945,6 @@ int win_seat_verify_ssh_host_key( { int ret; - static const char absentmsg[] = - "The server's host key is not cached in the registry. You\n" - "have no guarantee that the server is the computer you\n" - "think it is.\n" - "The server's %s key fingerprint is:\n" - "%s\n" - "If you trust this host, hit Yes to add the key to\n" - "%s's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, hit No.\n" - "If you do not trust this host, hit Cancel to abandon the\n" - "connection.\n"; - - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "\n" - "The server's host key does not match the one %s has\n" - "cached in the registry. This means that either the\n" - "server administrator has changed the host key, or you\n" - "have actually connected to another computer pretending\n" - "to be the server.\n" - "The new %s key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "hit Yes to update %s's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, hit No.\n" - "If you want to abandon the connection completely, hit\n" - "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n"; - - static const char mbtitle[] = "%s Security Alert"; - WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); /* @@ -883,36 +954,30 @@ int win_seat_verify_ssh_host_key( if (ret == 0) /* success - key matched OK */ return 1; - else if (ret == 2) { /* key was different */ - int mbret; - char *text = dupprintf(wrongmsg, appname, keytype, fingerprint, - appname); - char *caption = dupprintf(mbtitle, appname); - mbret = message_box(wgs->term_hwnd, text, caption, - MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3, - HELPCTXID(errors_hostkey_changed)); - assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL); - sfree(text); - sfree(caption); - if (mbret == IDYES) { + else { + static const char *const keywords[] = + { "{KEYTYPE}", "{APPNAME}", NULL }; + + const char *values[2]; + values[0] = keytype; + values[1] = appname; + + struct hostkey_dialog_ctx ctx[1]; + ctx->keywords = keywords; + ctx->values = values; + ctx->fingerprint = fingerprint; + ctx->iconid = (ret == 2 ? IDI_WARNING : IDI_QUESTION); + ctx->helpctx = (ret == 2 ? WINHELP_CTX_errors_hostkey_changed : + WINHELP_CTX_errors_hostkey_absent); + int dlgid = (ret == 2 ? IDD_HK_WRONG : IDD_HK_ABSENT); + int mbret = DialogBoxParam( + hinst, MAKEINTRESOURCE(dlgid), wgs->term_hwnd, + HostKeyDialogProc, (LPARAM)ctx); + assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL); + if (mbret == IDC_HK_ACCEPT) { store_host_key(host, port, keytype, keystr); return 1; - } else if (mbret == IDNO) - return 1; - } else if (ret == 1) { /* key was absent */ - int mbret; - char *text = dupprintf(absentmsg, keytype, fingerprint, appname); - char *caption = dupprintf(mbtitle, appname); - mbret = message_box(wgs->term_hwnd, text, caption, - MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3, - HELPCTXID(errors_hostkey_absent)); - assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL); - sfree(text); - sfree(caption); - if (mbret == IDYES) { - store_host_key(host, port, keytype, keystr); - return 1; - } else if (mbret == IDNO) + } else if (mbret == IDC_HK_ONCE) return 1; } return 0; /* abandon the connection */