From 546b2ad80a68e80a737aade06f0685cccb5e9716 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Thu, 2 May 2019 12:32:03 +0200 Subject: [PATCH] Make sure clipboard uses \n line endings This is required by the protocol so we should make sure it is enforced. We are tolerant of clients that violate this though and convert incoming clipboard data. --- common/rfb/CMsgReader.cxx | 6 ++-- common/rfb/CMsgWriter.cxx | 3 ++ common/rfb/SMsgReader.cxx | 6 ++-- common/rfb/SMsgWriter.cxx | 3 ++ common/rfb/VNCServerST.cxx | 2 ++ common/rfb/util.cxx | 56 ++++++++++++++++++++++++++++++ common/rfb/util.h | 4 +++ unix/xserver/hw/vnc/RFBGlue.cc | 16 ++++++++- unix/xserver/hw/vnc/RFBGlue.h | 5 ++- unix/xserver/hw/vnc/vncSelection.c | 22 +++++++++--- vncviewer/Viewport.cxx | 16 +++++---- win/rfb_win32/Clipboard.cxx | 16 ++------- 12 files changed, 123 insertions(+), 32 deletions(-) diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx index 2b5b9fbf..a928eb15 100644 --- a/common/rfb/CMsgReader.cxx +++ b/common/rfb/CMsgReader.cxx @@ -157,10 +157,10 @@ void CMsgReader::readServerCutText() vlog.error("cut text too long (%d bytes) - ignoring",len); return; } - CharArray ca(len+1); - ca.buf[len] = 0; + CharArray ca(len); is->readBytes(ca.buf, len); - handler->serverCutText(ca.buf, len); + CharArray filtered(convertLF(ca.buf, len)); + handler->serverCutText(filtered.buf, strlen(filtered.buf)); } void CMsgReader::readFence() diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx index d357c976..fed0bd27 100644 --- a/common/rfb/CMsgWriter.cxx +++ b/common/rfb/CMsgWriter.cxx @@ -181,6 +181,9 @@ void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask) void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len) { + if (memchr(str, '\r', len) != NULL) + throw Exception("Invalid carriage return in clipboard data"); + startMsg(msgTypeClientCutText); os->pad(3); os->writeU32(len); diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index 200350c1..0c0e8b26 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -212,10 +212,10 @@ void SMsgReader::readClientCutText() vlog.error("Cut text too long (%d bytes) - ignoring", len); return; } - CharArray ca(len+1); - ca.buf[len] = 0; + CharArray ca(len); is->readBytes(ca.buf, len); - handler->clientCutText(ca.buf, len); + CharArray filtered(convertLF(ca.buf, len)); + handler->clientCutText(filtered.buf, strlen(filtered.buf)); } void SMsgReader::readQEMUMessage() diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index 6a2c2ba0..f0748ff2 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -80,6 +80,9 @@ void SMsgWriter::writeBell() void SMsgWriter::writeServerCutText(const char* str, int len) { + if (memchr(str, '\r', len) != NULL) + throw Exception("Invalid carriage return in clipboard data"); + startMsg(msgTypeServerCutText); os->pad(3); os->writeU32(len); diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index c95c14f0..7820aef5 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -342,6 +342,8 @@ void VNCServerST::bell() void VNCServerST::serverCutText(const char* str, int len) { + if (memchr(str, '\r', len) != NULL) + throw Exception("Invalid carriage return in clipboard data"); std::list::iterator ci, ci_next; for (ci = clients.begin(); ci != clients.end(); ci = ci_next) { ci_next = ci; ci_next++; diff --git a/common/rfb/util.cxx b/common/rfb/util.cxx index f52213b3..f43a9453 100644 --- a/common/rfb/util.cxx +++ b/common/rfb/util.cxx @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -107,6 +108,61 @@ namespace rfb { dest[src ? destlen-1 : 0] = 0; } + char* convertLF(const char* src, size_t bytes) + { + char* buffer; + size_t sz; + + char* out; + const char* in; + size_t in_len; + + // Always include space for a NULL + sz = 1; + + // Compute output size + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + if (*in != '\r') { + sz++; + in++; + in_len--; + continue; + } + + if ((in_len == 0) || (*(in+1) != '\n')) + sz++; + + in++; + in_len--; + } + + // Alloc + buffer = new char[sz]; + memset(buffer, 0, sz); + + // And convert + out = buffer; + in = src; + in_len = bytes; + while ((*in != '\0') && (in_len > 0)) { + if (*in != '\r') { + *out++ = *in++; + in_len--; + continue; + } + + if ((in_len == 0) || (*(in+1) != '\n')) + *out++ = '\n'; + + in++; + in_len--; + } + + return buffer; + } + unsigned msBetween(const struct timeval *first, const struct timeval *second) { diff --git a/common/rfb/util.h b/common/rfb/util.h index 9e59bd37..de096692 100644 --- a/common/rfb/util.h +++ b/common/rfb/util.h @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -83,6 +84,9 @@ namespace rfb { // Copies src to dest, up to specified length-1, and guarantees termination void strCopy(char* dest, const char* src, int destlen); + // Makes sure line endings are in a certain format + + char* convertLF(const char* src, size_t bytes = (size_t)-1); // HELPER functions for timeout handling diff --git a/unix/xserver/hw/vnc/RFBGlue.cc b/unix/xserver/hw/vnc/RFBGlue.cc index 160177bd..d9c456e8 100644 --- a/unix/xserver/hw/vnc/RFBGlue.cc +++ b/unix/xserver/hw/vnc/RFBGlue.cc @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2015 Pierre Ossman for Cendio AB + * Copyright 2011-2019 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -210,3 +210,17 @@ int vncIsTCPPortUsed(int port) } return 0; } + +char* vncConvertLF(const char* src, size_t bytes) +{ + try { + return convertLF(src, bytes); + } catch (...) { + return NULL; + } +} + +void vncStrFree(char* str) +{ + strFree(str); +} diff --git a/unix/xserver/hw/vnc/RFBGlue.h b/unix/xserver/hw/vnc/RFBGlue.h index a63afd07..8e70c680 100644 --- a/unix/xserver/hw/vnc/RFBGlue.h +++ b/unix/xserver/hw/vnc/RFBGlue.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2015 Pierre Ossman for Cendio AB + * Copyright 2011-2019 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,6 +49,9 @@ void vncListParams(int width, int nameWidth); int vncGetSocketPort(int fd); int vncIsTCPPortUsed(int port); +char* vncConvertLF(const char* src, size_t bytes); +void vncStrFree(char* str); + #ifdef __cplusplus } #endif diff --git a/unix/xserver/hw/vnc/vncSelection.c b/unix/xserver/hw/vnc/vncSelection.c index 4f3538d4..5ddcaf00 100644 --- a/unix/xserver/hw/vnc/vncSelection.c +++ b/unix/xserver/hw/vnc/vncSelection.c @@ -1,4 +1,4 @@ -/* Copyright 2016 Pierre Ossman for Cendio AB +/* Copyright 2016-2019 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -415,13 +415,22 @@ static void vncHandleSelection(Atom selection, Atom target, else if (vncHasAtom(xaUTF8_STRING, (const Atom*)prop->data, prop->size)) vncSelectionRequest(selection, xaUTF8_STRING); } else if (target == xaSTRING) { + char* filtered; + if (prop->format != 8) return; if (prop->type != xaSTRING) return; - vncServerCutText(prop->data, prop->size); + filtered = vncConvertLF(prop->data, prop->size); + if (filtered == NULL) + return; + + vncServerCutText(filtered, strlen(filtered)); + + vncStrFree(filtered); } else if (target == xaUTF8_STRING) { + char *filtered; unsigned char* buffer; unsigned char* out; size_t len; @@ -470,9 +479,14 @@ static void vncHandleSelection(Atom selection, Atom target, } } - vncServerCutText((const char*)buffer, len); - + filtered = vncConvertLF(buffer, len); free(buffer); + if (filtered == NULL) + return; + + vncServerCutText(filtered, strlen(filtered)); + + vncStrFree(filtered); } } diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 5df5c796..15a3ec42 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2014 Pierre Ossman for Cendio AB + * Copyright 2011-2019 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -549,7 +549,7 @@ void Viewport::resize(int x, int y, int w, int h) int Viewport::handle(int event) { - char *buffer; + char *buffer, *filtered; int ret; int buttonMask, wheelMask; DownMap::const_iterator iter; @@ -564,22 +564,26 @@ int Viewport::handle(int event) ret = fl_utf8toa(Fl::event_text(), Fl::event_length(), buffer, Fl::event_length() + 1); assert(ret < (Fl::event_length() + 1)); + filtered = convertLF(buffer, ret); + delete [] buffer; if (!hasFocus()) { - pendingClientCutText = buffer; + pendingClientCutText = new char[strlen(filtered) + 1]; + strcpy((char*)pendingClientCutText, filtered); + strFree(filtered); return 1; } - vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(buffer)); + vlog.debug("Sending clipboard data (%d bytes)", (int)strlen(filtered)); try { - cc->writer()->writeClientCutText(buffer, ret); + cc->writer()->writeClientCutText(filtered, strlen(filtered)); } catch (rdr::Exception& e) { vlog.error("%s", e.str()); exit_vncviewer(e.str()); } - delete [] buffer; + strFree(filtered); return 1; diff --git a/win/rfb_win32/Clipboard.cxx b/win/rfb_win32/Clipboard.cxx index 0e290623..fca6c1df 100644 --- a/win/rfb_win32/Clipboard.cxx +++ b/win/rfb_win32/Clipboard.cxx @@ -1,4 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2012-2019 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,18 +35,6 @@ static LogWriter vlog("Clipboard"); // -=- CR/LF handlers // -char* -dos2unix(const char* text) { - int len = strlen(text)+1; - char* unix = new char[strlen(text)+1]; - int i, j=0; - for (i=0; inotifyClipboardChanged(0, 0); } else { - CharArray unix_text; - unix_text.buf = dos2unix(clipdata); + CharArray unix_text(convertLF(clipdata, strlen(clipdata))); removeNonISOLatin1Chars(unix_text.buf); notifier->notifyClipboardChanged(unix_text.buf, strlen(unix_text.buf)); } -- 2.39.5