diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 6c113e8c..76b50ffc 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -3426,20 +3426,24 @@ GtkWidget *create_message_box( NULL /* action_postproc */, NULL /* postproc_ctx */); } -struct verify_ssh_host_key_result_ctx { +struct verify_ssh_host_key_dialog_ctx { char *host; int port; char *keytype; char *keystr; + char *more_info; void (*callback)(void *callback_ctx, int result); void *callback_ctx; Seat *seat; + + GtkWidget *main_dialog; + GtkWidget *more_info_dialog; }; static void verify_ssh_host_key_result_callback(void *vctx, int result) { - struct verify_ssh_host_key_result_ctx *ctx = - (struct verify_ssh_host_key_result_ctx *)vctx; + struct verify_ssh_host_key_dialog_ctx *ctx = + (struct verify_ssh_host_key_dialog_ctx *)vctx; if (result >= 0) { int logical_result; @@ -3468,12 +3472,51 @@ static void verify_ssh_host_key_result_callback(void *vctx, int result) */ unregister_dialog(ctx->seat, DIALOG_SLOT_NETWORK_PROMPT); + if (ctx->more_info_dialog) + gtk_widget_destroy(ctx->more_info_dialog); + sfree(ctx->host); sfree(ctx->keytype); sfree(ctx->keystr); + sfree(ctx->more_info); sfree(ctx); } +static GtkWidget *add_more_info_button(GtkWidget *w, void *vctx) +{ + GtkWidget *box = gtk_hbox_new(false, 10); + gtk_widget_show(box); + gtk_box_pack_end(GTK_BOX(box), w, false, true, 0); + GtkWidget *button = gtk_button_new_with_label("More info..."); + gtk_widget_show(button); + gtk_box_pack_start(GTK_BOX(box), button, false, true, 0); + *(GtkWidget **)vctx = button; + return box; +} + +static void more_info_closed(void *vctx, int result) +{ + struct verify_ssh_host_key_dialog_ctx *ctx = + (struct verify_ssh_host_key_dialog_ctx *)vctx; + + ctx->more_info_dialog = NULL; +} + +static void more_info_button_clicked(GtkButton *button, gpointer vctx) +{ + struct verify_ssh_host_key_dialog_ctx *ctx = + (struct verify_ssh_host_key_dialog_ctx *)vctx; + + if (ctx->more_info_dialog) + return; + + ctx->more_info_dialog = create_message_box( + ctx->main_dialog, "Host key information", ctx->more_info, + string_width("SHA256 fingerprint: ecdsa-sha2-nistp521 521 " + "abcdefghkmnopqrsuvwxyzABCDEFGHJKLMNOPQRSTUW"), true, + &buttons_ok, more_info_closed, ctx); +} + int gtk_seat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, @@ -3516,7 +3559,7 @@ int gtk_seat_verify_ssh_host_key( char *text; int ret; - struct verify_ssh_host_key_result_ctx *result_ctx; + struct verify_ssh_host_key_dialog_ctx *result_ctx; GtkWidget *mainwin, *msgbox; /* @@ -3533,7 +3576,7 @@ int gtk_seat_verify_ssh_host_key( text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprints[fptype_default]); - result_ctx = snew(struct verify_ssh_host_key_result_ctx); + result_ctx = snew(struct verify_ssh_host_key_dialog_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->host = dupstr(host); @@ -3543,10 +3586,40 @@ int gtk_seat_verify_ssh_host_key( result_ctx->seat = seat; mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); - msgbox = create_message_box( + GtkWidget *more_info_button = NULL; + msgbox = create_message_box_general( mainwin, "PuTTY Security Alert", text, string_width(fingerprints[fptype_default]), true, - &buttons_hostkey, verify_ssh_host_key_result_callback, result_ctx); + &buttons_hostkey, verify_ssh_host_key_result_callback, result_ctx, + add_more_info_button, &more_info_button); + + result_ctx->main_dialog = msgbox; + result_ctx->more_info_dialog = NULL; + + strbuf *sb = strbuf_new(); + if (fingerprints[SSH_FPTYPE_SHA256]) + strbuf_catf(sb, "SHA256 fingerprint: %s\n", + fingerprints[SSH_FPTYPE_SHA256]); + if (fingerprints[SSH_FPTYPE_MD5]) + strbuf_catf(sb, "MD5 fingerprint: %s\n", + fingerprints[SSH_FPTYPE_MD5]); + strbuf_catf(sb, "Full text of host's public key:"); + /* We have to manually wrap the public key, or else the GtkLabel + * will resize itself to accommodate the longest word, which will + * lead to a hilariously wide message box. */ + for (const char *p = keydisp, *q = p + strlen(p); p < q ;) { + size_t linelen = q-p; + if (linelen > 72) + linelen = 72; + put_byte(sb, '\n'); + put_data(sb, p, linelen); + p += linelen; + } + result_ctx->more_info = strbuf_to_str(sb); + + g_signal_connect(G_OBJECT(more_info_button), "clicked", + G_CALLBACK(more_info_button_clicked), result_ctx); + register_dialog(seat, DIALOG_SLOT_NETWORK_PROMPT, msgbox); sfree(text); diff --git a/windows/win_res.h b/windows/win_res.h index 19e2657f..d34f6852 100644 --- a/windows/win_res.h +++ b/windows/win_res.h @@ -15,6 +15,7 @@ #define IDD_LICENCEBOX 113 #define IDD_HK_ABSENT 114 #define IDD_HK_WRONG 115 +#define IDD_HK_MOREINFO 116 #define IDN_LIST 1001 #define IDN_COPY 1002 @@ -36,6 +37,11 @@ #define IDC_HK_ACCEPT 1001 #define IDC_HK_ONCE 1000 #define IDC_HK_FINGERPRINT 1002 +#define IDC_HK_MOREINFO 1003 + +#define IDC_HKI_SHA256 1000 +#define IDC_HKI_MD5 1001 +#define IDC_HKI_PUBKEY 1002 #define ID_CUSTOM_CHMFILE 2000 #define TYPE_CUSTOM_CHMFILE 2000 diff --git a/windows/win_res.rc2 b/windows/win_res.rc2 index cc907f21..ccec3122 100644 --- a/windows/win_res.rc2 +++ b/windows/win_res.rc2 @@ -63,58 +63,75 @@ BEGIN END /* No accelerators used */ -IDD_HK_ABSENT DIALOG DISCARDABLE 50, 50, 300, 148 +IDD_HK_ABSENT DIALOG DISCARDABLE 50, 50, 340, 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 + LTEXT "The server's host key is not cached in the registry. You have no", 100, 40, 20, 300, 8 + LTEXT "guarantee that the server is the computer you think it is.", 101, 40, 28, 300, 8 + LTEXT "The server's {KEYTYPE} key fingerprint is:", 102, 40, 40, 300, 8 + LTEXT "If you trust this host, press ""Accept"" to add the key to {APPNAME}'s", 103, 40, 60, 300, 8 + LTEXT "cache and carry on connecting.", 104, 40, 68, 300, 8 + LTEXT "If you want to carry on connecting just once, without adding the key", 105, 40, 80, 300, 8 + LTEXT "to the cache, press ""Connect Once"".", 106, 40, 88, 300, 8 + LTEXT "If you do not trust this host, press ""Cancel"" to abandon the connection.", 107, 40, 100, 300, 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 "Cancel", IDCANCEL, 288, 128, 40, 14 + PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 128, 40, 14 + PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 128, 64, 14 + PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 128, 64, 14 PUSHBUTTON "Help", IDHELP, 12, 128, 40, 14 - EDITTEXT IDC_HK_FINGERPRINT, 40, 48, 260, 12, ES_READONLY | ES_LEFT, 0 + EDITTEXT IDC_HK_FINGERPRINT, 40, 48, 300, 12, ES_READONLY | ES_LEFT, 0 END /* No accelerators used */ -IDD_HK_WRONG DIALOG DISCARDABLE 50, 50, 300, 188 +IDD_HK_WRONG DIALOG DISCARDABLE 50, 50, 340, 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 "WARNING - POTENTIAL SECURITY BREACH!", IDC_HK_TITLE, 40, 20, 300, 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 + LTEXT "The server's host key does not match the one {APPNAME} has cached in", 100, 40, 36, 300, 8 + LTEXT "the registry. This means that either the server administrator has", 101, 40, 44, 300, 8 + LTEXT "changed the host key, or you have actually connected to another", 102, 40, 52, 300, 8 + LTEXT "computer pretending to be the server.", 103, 40, 60, 300, 8 + LTEXT "The new {KEYTYPE} key fingerprint is:", 104, 40, 72, 300, 8 + LTEXT "If you were expecting this change and trust the new key, press", 105, 40, 92, 300, 8 + LTEXT """Accept"" to update {APPNAME}'s cache and continue connecting.", 106, 40, 100, 300, 8 + LTEXT "If you want to carry on connecting but without updating the cache,", 107, 40, 112, 300, 8 + LTEXT "press ""Connect Once"".", 108, 40, 120, 300, 8 + LTEXT "If you want to abandon the connection completely, press ""Cancel"".", 109, 40, 132, 300, 8 + LTEXT "Pressing ""Cancel"" is the ONLY guaranteed safe choice.", 110, 40, 140, 300, 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 "Cancel", IDCANCEL, 288, 168, 40, 14 + PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 168, 40, 14 + PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 168, 64, 14 + PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 168, 64, 14 PUSHBUTTON "Help", IDHELP, 12, 168, 40, 14 - EDITTEXT IDC_HK_FINGERPRINT, 40, 80, 260, 12, ES_READONLY | ES_LEFT, 0 + EDITTEXT IDC_HK_FINGERPRINT, 40, 80, 300, 12, ES_READONLY | ES_LEFT, 0 +END + +/* Accelerators used: clw */ +IDD_HK_MOREINFO DIALOG DISCARDABLE 140, 40, 400, 156 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY: information about the server's host key" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "SHA256 fingerprint:", 100, 12, 12, 80, 8 + EDITTEXT IDC_HKI_SHA256, 100, 10, 288, 12, ES_READONLY + LTEXT "MD5 fingerprint:", 101, 12, 28, 80, 8 + EDITTEXT IDC_HKI_MD5, 100, 26, 288, 12, ES_READONLY + LTEXT "Full public key:", 102, 12, 44, 376, 8 + EDITTEXT IDC_HKI_PUBKEY, 12, 54, 376, 64, ES_READONLY | ES_MULTILINE | ES_LEFT | ES_AUTOVSCROLL, WS_EX_STATICEDGE + DEFPUSHBUTTON "&Close", IDOK, 176, 130, 48, 14 END #include "version.rc2" diff --git a/windows/windlg.c b/windows/windlg.c index 45b5a217..f10f091a 100644 --- a/windows/windlg.c +++ b/windows/windlg.c @@ -838,11 +838,47 @@ void showabout(HWND hwnd) struct hostkey_dialog_ctx { const char *const *keywords; const char *const *values; - const char *fingerprint; + FingerprintType fptype_default; + char **fingerprints; + const char *keydisp; LPCTSTR iconid; const char *helpctx; }; +static INT_PTR CALLBACK HostKeyMoreInfoProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: { + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *)lParam; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); + + if (ctx->fingerprints[SSH_FPTYPE_SHA256]) + SetDlgItemText(hwnd, IDC_HKI_SHA256, + ctx->fingerprints[SSH_FPTYPE_SHA256]); + if (ctx->fingerprints[SSH_FPTYPE_MD5]) + SetDlgItemText(hwnd, IDC_HKI_MD5, + ctx->fingerprints[SSH_FPTYPE_MD5]); + + SetDlgItemText(hwnd, IDA_TEXT, ctx->keydisp); + + return 1; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, 0); + return 0; + } + return 0; + case WM_CLOSE: + EndDialog(hwnd, 0); + return 0; + } + return 0; +} + static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -878,7 +914,8 @@ static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, } strbuf_free(sb); - SetDlgItemText(hwnd, IDC_HK_FINGERPRINT, ctx->fingerprint); + SetDlgItemText(hwnd, IDC_HK_FINGERPRINT, + ctx->fingerprints[ctx->fptype_default]); MakeDlgItemBorderless(hwnd, IDC_HK_FINGERPRINT); HANDLE icon = LoadImage( @@ -929,6 +966,13 @@ static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, launch_help(hwnd, ctx->helpctx); return 0; } + case IDC_HK_MOREINFO: { + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_HK_MOREINFO), + hwnd, HostKeyMoreInfoProc, (LPARAM)ctx); + } } return 0; case WM_CLOSE: @@ -965,8 +1009,9 @@ int win_seat_verify_ssh_host_key( struct hostkey_dialog_ctx ctx[1]; ctx->keywords = keywords; ctx->values = values; - ctx->fingerprint = fingerprints[ - ssh2_pick_default_fingerprint(fingerprints)]; + ctx->fingerprints = fingerprints; + ctx->fptype_default = ssh2_pick_default_fingerprint(fingerprints); + ctx->keydisp = keydisp; ctx->iconid = (ret == 2 ? IDI_WARNING : IDI_QUESTION); ctx->helpctx = (ret == 2 ? WINHELP_CTX_errors_hostkey_changed : WINHELP_CTX_errors_hostkey_absent);