From 2a5d8e05e822e77f7fc7037e7ae7e7ca9d14516f Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 10 Mar 2019 14:37:11 +0000 Subject: [PATCH] Add a TermWin method to draw a 'trust sigil'. This is not yet used by anything, but the idea is that it'll be a graphic in the terminal window that can't be replicated by a server sending escape sequences, and hence can be used as a reliable indication that the text on a particular terminal line is generated by PuTTY itself and not passed through from the server. This will make it possible to detect a malicious server trying to mimic local prompts to trick you out of information that shouldn't be sent over the wire (such as private-key passphrases). The trust sigil I've picked is a small copy of the PuTTY icon, which is thematically nice (it can be read as if the PuTTY icon is the name of the speaker in a dialogue) and also convenient because we had that graphic available already on all platforms. (Though the contortions I had to go through to make the GTK 1 code draw it were quite annoying.) The trust sigil has the same dimensions as a CJK double-width character, i.e. it's 2 character cells wide by 1 high. --- fuzzterm.c | 5 ++ putty.h | 6 ++ unix/gtkwin.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++ windows/window.c | 19 +++++- 4 files changed, 188 insertions(+), 1 deletion(-) diff --git a/fuzzterm.c b/fuzzterm.c index 83a176e6..fbc4cedf 100644 --- a/fuzzterm.c +++ b/fuzzterm.c @@ -69,6 +69,10 @@ static void fuzz_draw_cursor( } printf("\n"); } +static void fuzz_draw_trust_sigil(TermWin *tw, int x, int y) +{ + printf("TRUST@(%d,%d)\n", x, y); +} static int fuzz_char_width(TermWin *tw, int uc) { return 1; } static void fuzz_free_draw_ctx(TermWin *tw) {} static void fuzz_set_cursor_pos(TermWin *tw, int x, int y) {} @@ -101,6 +105,7 @@ static const TermWinVtable fuzz_termwin_vt = { fuzz_setup_draw_ctx, fuzz_draw_text, fuzz_draw_cursor, + fuzz_draw_trust_sigil, fuzz_char_width, fuzz_free_draw_ctx, fuzz_set_cursor_pos, diff --git a/putty.h b/putty.h index 71d8da76..3ab9a2f8 100644 --- a/putty.h +++ b/putty.h @@ -1099,6 +1099,10 @@ struct TermWinVtable { * redraw it in different colours). */ void (*draw_cursor)(TermWin *, int x, int y, wchar_t *text, int len, unsigned long attrs, int line_attrs, truecolour tc); + /* Draw the sigil indicating that a line of text has come from + * PuTTY itself rather than the far end (defence against end-of- + * authentication spoofing) */ + void (*draw_trust_sigil)(TermWin *, int x, int y); int (*char_width)(TermWin *, int uc); void (*free_draw_ctx)(TermWin *); @@ -1151,6 +1155,8 @@ static inline void win_draw_cursor( TermWin *win, int x, int y, wchar_t *text, int len, unsigned long attrs, int line_attrs, truecolour tc) { win->vt->draw_cursor(win, x, y, text, len, attrs, line_attrs, tc); } +static inline void win_draw_trust_sigil(TermWin *win, int x, int y) +{ win->vt->draw_trust_sigil(win, x, y); } static inline int win_char_width(TermWin *win, int uc) { return win->vt->char_width(win, uc); } static inline void win_free_draw_ctx(TermWin *win) diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 8bee72e8..83aea357 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -88,6 +88,8 @@ struct clipboard_state { #endif }; +typedef struct XpmHolder XpmHolder; /* only used for GTK 1 */ + struct GtkFrontend { GtkWidget *window, *area, *sbar; gboolean sbar_visible; @@ -183,6 +185,12 @@ struct GtkFrontend { #endif bool send_raw_mouse; unifont_drawctx uctx; +#if GTK_CHECK_VERSION(2,0,0) + GdkPixbuf *trust_sigil_pb; +#else + GdkPixmap *trust_sigil_pm; +#endif + int trust_sigil_w, trust_sigil_h; Seat seat; TermWin termwin; @@ -2311,6 +2319,17 @@ static void delete_inst(GtkFrontend *inst) log_free(inst->logctx); inst->logctx = NULL; } +#if GTK_CHECK_VERSION(2,0,0) + if (inst->trust_sigil_pb) { + g_object_unref(G_OBJECT(inst->trust_sigil_pb)); + inst->trust_sigil_pb = NULL; + } +#else + if (inst->trust_sigil_pm) { + gdk_pixmap_unref(inst->trust_sigil_pm); + inst->trust_sigil_pm = NULL; + } +#endif #ifdef JUST_USE_GTK_CLIPBOARD_UTF8 /* @@ -3991,6 +4010,145 @@ static void gtkwin_draw_cursor( #endif } +#if !GTK_CHECK_VERSION(2,0,0) +/* + * For GTK 1, manual code to scale an in-memory XPM, producing a new + * one as output. It will be ugly, but good enough to use as a trust + * sigil. + */ +struct XpmHolder { + char **strings; + size_t nstrings; +}; + +static void xpmholder_free(XpmHolder *xh) +{ + for (size_t i = 0; i < xh->nstrings; i++) + sfree(xh->strings[i]); + sfree(xh->strings); + sfree(xh); +} + +static XpmHolder *xpm_scale(const char *const *xpm, int wo, int ho) +{ + /* Get image dimensions, # colours, and chars-per-pixel */ + int wi = 0, hi = 0, nc = 0, cpp = 0; + int retd = sscanf(xpm[0], "%d %d %d %d", &wi, &hi, &nc, &cpp); + assert(retd == 4); + + /* Make output XpmHolder */ + XpmHolder *xh = snew(XpmHolder); + xh->nstrings = 1 + nc + ho; + xh->strings = snewn(xh->nstrings, char *); + + /* Set up header */ + xh->strings[0] = dupprintf("%d %d %d %d", wo, ho, nc, cpp); + for (int i = 0; i < nc; i++) + xh->strings[1 + i] = dupstr(xpm[1 + i]); + + /* Scale image */ + for (int yo = 0; yo < ho; yo++) { + int yi = yo * hi / ho; + char *ro = snewn(cpp * wo + 1, char); + ro[cpp * wo] = '\0'; + xh->strings[1 + nc + yo] = ro; + const char *ri = xpm[1 + nc + yi]; + + for (int xo = 0; xo < wo; xo++) { + int xi = xo * wi / wo; + memcpy(ro + cpp * xo, ri + cpp * xi, cpp); + } + } + + return xh; +} +#endif /* !GTK_CHECK_VERSION(2,0,0) */ + +static void gtkwin_draw_trust_sigil(TermWin *tw, int cx, int cy) +{ + GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); + + int x = cx * inst->font_width + inst->window_border; + int y = cy * inst->font_height + inst->window_border; + int w = 2*inst->font_width, h = inst->font_height; + + if (inst->trust_sigil_w != w || inst->trust_sigil_h != h || +#if GTK_CHECK_VERSION(2,0,0) + !inst->trust_sigil_pb +#else + !inst->trust_sigil_pm +#endif + ) { + +#if GTK_CHECK_VERSION(2,0,0) + if (inst->trust_sigil_pb) + g_object_unref(G_OBJECT(inst->trust_sigil_pb)); +#else + if (inst->trust_sigil_pm) + gdk_pixmap_unref(inst->trust_sigil_pm); +#endif + + int best_icon_index = 0; + unsigned score = UINT_MAX; + for (int i = 0; i < n_main_icon; i++) { + int iw, ih; + if (sscanf(main_icon[i][0], "%d %d", &iw, &ih) == 2) { + int this_excess = (iw + ih) - (w + h); + unsigned this_score = (abs(this_excess) | + (this_excess > 0 ? 0 : 0x80000000U)); + if (this_score < score) { + best_icon_index = i; + score = this_score; + } + } + } + +#if GTK_CHECK_VERSION(2,0,0) + GdkPixbuf *icon_unscaled = gdk_pixbuf_new_from_xpm_data( + (const gchar **)main_icon[best_icon_index]); + inst->trust_sigil_pb = gdk_pixbuf_scale_simple( + icon_unscaled, w, h, GDK_INTERP_BILINEAR); + g_object_unref(G_OBJECT(icon_unscaled)); +#else + XpmHolder *xh = xpm_scale(main_icon[best_icon_index], w, h); + inst->trust_sigil_pm = gdk_pixmap_create_from_xpm_d( + gtk_widget_get_window(inst->window), NULL, + &inst->cols[258], xh->strings); + xpmholder_free(xh); +#endif + + inst->trust_sigil_w = w; + inst->trust_sigil_h = h; + } + +#ifdef DRAW_TEXT_GDK + if (inst->uctx.type == DRAWTYPE_GDK) { +#if GTK_CHECK_VERSION(2,0,0) + gdk_draw_pixbuf(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, + inst->trust_sigil_pb, 0, 0, x, y, w, h, + GDK_RGB_DITHER_NORMAL, 0, 0); +#else + gdk_draw_pixmap(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, + inst->trust_sigil_pm, 0, 0, x, y, w, h); +#endif + } +#endif +#ifdef DRAW_TEXT_CAIRO + if (inst->uctx.type == DRAWTYPE_CAIRO) { + inst->uctx.u.cairo.widget = GTK_WIDGET(inst->area); + cairo_save(inst->uctx.u.cairo.cr); + cairo_translate(inst->uctx.u.cairo.cr, x, y); + gdk_cairo_set_source_pixbuf(inst->uctx.u.cairo.cr, + inst->trust_sigil_pb, 0, 0); + cairo_rectangle(inst->uctx.u.cairo.cr, 0, 0, w, h); + cairo_fill(inst->uctx.u.cairo.cr); + cairo_restore(inst->uctx.u.cairo.cr); + } +#endif + + draw_update(inst, x, y, w, h); +} + GdkCursor *make_mouse_ptr(GtkFrontend *inst, int cursor_val) { if (cursor_val == -1) { @@ -4964,6 +5122,7 @@ static const TermWinVtable gtk_termwin_vt = { gtkwin_setup_draw_ctx, gtkwin_draw_text, gtkwin_draw_cursor, + gtkwin_draw_trust_sigil, gtkwin_char_width, gtkwin_free_draw_ctx, gtkwin_set_cursor_pos, diff --git a/windows/window.c b/windows/window.c index fbf05cfb..5b4242dd 100644 --- a/windows/window.c +++ b/windows/window.c @@ -231,6 +231,7 @@ static void wintw_draw_text(TermWin *, int x, int y, wchar_t *text, int len, unsigned long attrs, int lattrs, truecolour tc); static void wintw_draw_cursor(TermWin *, int x, int y, wchar_t *text, int len, unsigned long attrs, int lattrs, truecolour tc); +static void wintw_draw_trust_sigil(TermWin *, int x, int y); static int wintw_char_width(TermWin *, int uc); static void wintw_free_draw_ctx(TermWin *); static void wintw_set_cursor_pos(TermWin *, int x, int y); @@ -262,6 +263,7 @@ static const TermWinVtable windows_termwin_vt = { wintw_setup_draw_ctx, wintw_draw_text, wintw_draw_cursor, + wintw_draw_trust_sigil, wintw_char_width, wintw_free_draw_ctx, wintw_set_cursor_pos, @@ -291,6 +293,8 @@ static const TermWinVtable windows_termwin_vt = { static TermWin wintw[1]; static HDC wintw_hdc; +static HICON icon; + const bool share_can_be_downstream = true; const bool share_can_be_upstream = true; @@ -674,6 +678,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) prepare_session(conf); } + icon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON)); + if (!prev) { WNDCLASSW wndclass; @@ -682,7 +688,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = inst; - wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON)); + wndclass.hIcon = icon; wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM); wndclass.hbrBackground = NULL; wndclass.lpszMenuName = NULL; @@ -3986,6 +3992,17 @@ static void wintw_draw_cursor( } } +static void wintw_draw_trust_sigil(TermWin *tw, int x, int y) +{ + x *= font_width; + y *= font_height; + x += offset_width; + y += offset_height; + + DrawIconEx(wintw_hdc, x, y, icon, font_width * 2, font_height, + 0, NULL, DI_NORMAL); +} + /* This function gets the actual width of a character in the normal font. */ static int wintw_char_width(TermWin *tw, int uc)