1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Sanitise control characters from paste data by default.

This is a mild security measure against malicious clipboard-writing.
It's only mild, because of course there are situations in which even a
sanitised paste could be successfully malicious (imagine someone
managing to write the traditional 'rm -rf' command into your clipboard
when you were going to paste to a shell prompt); but it at least
allows pasting into typical text editors without also allowing the
control sequence that exits the editor UI and returns to the shell
prompt.

This is a configurable option, because there's no well defined line to
be drawn between acceptable and unacceptable pastes, and it's very
plausible that users will have sensible use cases for pasting things
outside the list of permitted characters, or cases in which they know
they trust the clipboard-writer. I for one certainly find it useful on
occasion to deliberately construct a paste containing control
sequences that automate a terminal-based UI.

While I'm at it, when bracketed paste mode is enabled, we also prevent
pasting of data that includes the 'end bracketed paste' sequence
somewhere in the middle. I really _hope_ nobody was treating bracketed
paste mode as a key part of their security boundary, but then again, I
also can't imagine that anyone had an actually sensible use case for
deliberately making a bracketed paste be only partly bracketed, and
it's an easy change while I'm messing about in this area anyway.
This commit is contained in:
Simon Tatham 2018-03-11 17:40:42 +00:00
parent 28520e41ac
commit 10c9104822
4 changed files with 63 additions and 16 deletions

View File

@ -1975,6 +1975,12 @@ void setup_config_box(struct controlbox *b, int midsession,
HELPCTX(selection_clipactions),
CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom);
s = ctrl_getset(b, "Window/Selection", "paste",
"Control pasting of text");
ctrl_checkbox(s, "Permit control characters in pasted text",
NO_SHORTCUT, HELPCTX(no_help),
conf_checkbox_handler, I(CONF_paste_controls));
/*
* The Window/Selection/Words panel.
*/

View File

@ -895,6 +895,7 @@ void cleanup_exit(int);
/* Selection options */ \
X(INT, NONE, mouse_is_xterm) \
X(INT, NONE, rect_select) \
X(INT, NONE, paste_controls) \
X(INT, NONE, rawcnp) \
X(INT, NONE, rtf_paste) \
X(INT, NONE, mouse_override) \

View File

@ -673,6 +673,7 @@ void save_open_settings(void *sesskey, Conf *conf)
write_setting_i(sesskey, "PasteRTF", conf_get_int(conf, CONF_rtf_paste));
write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm));
write_setting_i(sesskey, "RectSelect", conf_get_int(conf, CONF_rect_select));
write_setting_i(sesskey, "PasteControls", conf_get_int(conf, CONF_paste_controls));
write_setting_i(sesskey, "MouseOverride", conf_get_int(conf, CONF_mouse_override));
for (i = 0; i < 256; i += 32) {
char buf[20], buf2[256];
@ -1088,6 +1089,7 @@ void load_open_settings(void *sesskey, Conf *conf)
gppi(sesskey, "PasteRTF", 0, conf, CONF_rtf_paste);
gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm);
gppi(sesskey, "RectSelect", 0, conf, CONF_rect_select);
gppi(sesskey, "PasteControls", 0, conf, CONF_paste_controls);
gppi(sesskey, "MouseOverride", 1, conf, CONF_mouse_override);
for (i = 0; i < 256; i += 32) {
static const char *const defaults[] = {

View File

@ -6,6 +6,7 @@
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <wchar.h>
#include <time.h>
#include <assert.h>
@ -6145,9 +6146,21 @@ static void term_paste_callback(void *vterm)
term->paste_len = 0;
}
/*
* Specialist string compare function. Returns true if the buffer of
* alen wide characters starting at a has as a prefix the buffer of
* blen characters starting at b.
*/
static int wstartswith(const wchar_t *a, size_t alen,
const wchar_t *b, size_t blen)
{
return alen >= blen && !wcsncmp(a, b, blen);
}
void term_do_paste(Terminal *term, const wchar_t *data, int len)
{
const wchar_t *p, *q;
const wchar_t *p;
int paste_controls = conf_get_int(term->conf, CONF_paste_controls);
/*
* Pasting data into the terminal counts as a keyboard event (for
@ -6168,26 +6181,51 @@ void term_do_paste(Terminal *term, const wchar_t *data, int len)
term->paste_len += 6;
}
p = q = data;
p = data;
while (p < data + len) {
while (p < data + len &&
!(p <= data + len - sel_nl_sz &&
!memcmp(p, sel_nl, sizeof(sel_nl))))
p++;
wchar_t wc = *p++;
{
int i;
for (i = 0; i < p - q; i++) {
term->paste_buffer[term->paste_len++] = q[i];
if (wc == sel_nl[0] &&
wstartswith(p-1, data+len-(p-1), sel_nl, sel_nl_sz)) {
/*
* This is the (platform-dependent) sequence that the host
* OS uses to represent newlines in clipboard data.
* Normalise it to a press of CR.
*/
p += sel_nl_sz - 1;
wc = '\015';
}
if ((wc & ~(wint_t)0x9F) == 0) {
/*
* This is a control code, either in the range 0x00-0x1F
* or 0x80-0x9F. We reject all of these in pastecontrols
* mode, except for a small set of permitted ones.
*/
if (!paste_controls) {
/* In line with xterm 292, accepted control chars are:
* CR, LF, tab, backspace. (And DEL, i.e. 0x7F, but
* that's permitted by virtue of not matching the bit
* mask that got us into this if statement, so we
* don't have to permit it here. */
static const unsigned mask =
(1<<13) | (1<<10) | (1<<9) | (1<<8);
if (wc > 15 || !((mask >> wc) & 1))
continue;
}
if (wc == '\033' && term->bracketed_paste &&
wstartswith(p-1, data+len-(p-1), L"\033[201~", 6)) {
/*
* Also, in bracketed-paste mode, reject the ESC
* character that begins the end-of-paste sequence.
*/
continue;
}
}
if (p <= data + len - sel_nl_sz &&
!memcmp(p, sel_nl, sizeof(sel_nl))) {
term->paste_buffer[term->paste_len++] = '\015';
p += sel_nl_sz;
}
q = p;
term->paste_buffer[term->paste_len++] = wc;
}
if (term->bracketed_paste) {