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:
parent
28520e41ac
commit
10c9104822
6
config.c
6
config.c
@ -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.
|
||||
*/
|
||||
|
1
putty.h
1
putty.h
@ -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) \
|
||||
|
@ -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[] = {
|
||||
|
70
terminal.c
70
terminal.c
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user