diff --git a/libs/clipboard/src/windows/wf_cliprdr.c b/libs/clipboard/src/windows/wf_cliprdr.c index 1f65b1913b2d..c2b7556a46ab 100644 --- a/libs/clipboard/src/windows/wf_cliprdr.c +++ b/libs/clipboard/src/windows/wf_cliprdr.c @@ -289,6 +289,9 @@ static BOOL try_open_clipboard(HWND hwnd) static HRESULT STDMETHODCALLTYPE CliprdrStream_QueryInterface(IStream *This, REFIID riid, void **ppvObject) { + if (ppvObject == NULL) + return E_INVALIDARG; + if (IsEqualIID(riid, &IID_IStream) || IsEqualIID(riid, &IID_IUnknown)) { IStream_AddRef(This); @@ -364,6 +367,13 @@ static HRESULT STDMETHODCALLTYPE CliprdrStream_Read(IStream *This, void *pv, ULO } *pcbRead = clipboard->req_fsize; + // Check overflow, can not be a real case + if ((instance->m_lOffset.QuadPart + clipboard->req_fsize) < instance->m_lOffset.QuadPart) { + // It's better to crash to release the explorer.exe + // This is a critical error, because the explorer is waiting for the data + // and the m_lOffset is wrong(overflowed) + return S_FALSE; + } instance->m_lOffset.QuadPart += clipboard->req_fsize; if (clipboard->req_fsize < cb) @@ -519,11 +529,17 @@ static HRESULT STDMETHODCALLTYPE CliprdrStream_Clone(IStream *This, IStream **pp static CliprdrStream *CliprdrStream_New(UINT32 connID, ULONG index, void *pData, const FILEDESCRIPTORW *dsc) { - IStream *iStream; + IStream *iStream = NULL; BOOL success = FALSE; BOOL isDir = FALSE; - CliprdrStream *instance; + CliprdrStream *instance = NULL; wfClipboard *clipboard = (wfClipboard *)pData; + + if (!(pData && dsc)) + { + return NULL; + } + instance = (CliprdrStream *)calloc(1, sizeof(CliprdrStream)); if (instance) @@ -876,14 +892,18 @@ static HRESULT STDMETHODCALLTYPE CliprdrDataObject_EnumDAdvise(IDataObject *This static CliprdrDataObject *CliprdrDataObject_New(UINT32 connID, FORMATETC *fmtetc, STGMEDIUM *stgmed, ULONG count, void *data) { - CliprdrDataObject *instance; - IDataObject *iDataObject; + CliprdrDataObject *instance = NULL; + IDataObject *iDataObject = NULL; instance = (CliprdrDataObject *)calloc(1, sizeof(CliprdrDataObject)); if (!instance) goto error; + instance->m_pFormatEtc = NULL; + instance->m_pStgMedium = NULL; + iDataObject = &instance->iDataObject; + iDataObject->lpVtbl = NULL; iDataObject->lpVtbl = (IDataObjectVtbl *)calloc(1, sizeof(IDataObjectVtbl)); if (!iDataObject->lpVtbl) @@ -931,7 +951,24 @@ static CliprdrDataObject *CliprdrDataObject_New(UINT32 connID, FORMATETC *fmtetc return instance; error: - CliprdrDataObject_Delete(instance); + if (iDataObject && iDataObject->lpVtbl) + { + free(iDataObject->lpVtbl); + } + if (instance) + { + if (instance->m_pFormatEtc) + { + free(instance->m_pFormatEtc); + } + + if (instance->m_pStgMedium) + { + free(instance->m_pStgMedium); + } + + CliprdrDataObject_Delete(instance); + } return NULL; } @@ -1012,6 +1049,8 @@ static HRESULT STDMETHODCALLTYPE CliprdrEnumFORMATETC_QueryInterface(IEnumFORMAT REFIID riid, void **ppvObject) { (void)This; + if (!ppvObject) + return E_INVALIDARG; if (IsEqualIID(riid, &IID_IEnumFORMATETC) || IsEqualIID(riid, &IID_IUnknown)) { @@ -1200,6 +1239,7 @@ static UINT32 get_local_format_id_by_name(wfClipboard *clipboard, const TCHAR *f WCHAR *unicode_name; #if !defined(UNICODE) size_t size; + int towchar_count; #endif if (!clipboard || !format_name) @@ -1207,6 +1247,8 @@ static UINT32 get_local_format_id_by_name(wfClipboard *clipboard, const TCHAR *f #if defined(UNICODE) unicode_name = _wcsdup(format_name); + if (!unicode_name) + return 0; #else size = _tcslen(format_name); unicode_name = calloc(size + 1, sizeof(WCHAR)); @@ -1214,11 +1256,13 @@ static UINT32 get_local_format_id_by_name(wfClipboard *clipboard, const TCHAR *f if (!unicode_name) return 0; - MultiByteToWideChar(CP_OEMCP, 0, format_name, strlen(format_name), unicode_name, size); -#endif - - if (!unicode_name) + towchar_count = MultiByteToWideChar(CP_OEMCP, 0, format_name, strlen(format_name), NULL, 0); + if (towchar_count <= 0 || towchar_count > size) + return 0; + towchar_count = MultiByteToWideChar(CP_OEMCP, 0, format_name, strlen(format_name), unicode_name, size); + if (towchar_count <= 0) return 0; +#endif for (i = 0; i < clipboard->map_size; i++) { @@ -1314,6 +1358,9 @@ static UINT cliprdr_send_tempdir(wfClipboard *clipboard) if (!clipboard) return -1; + // to-do: + // Directly use the environment variable `TEMP` is not safe. + // But this function is not used for now. if (GetEnvironmentVariableA("TEMP", tempDirectory.szTempDir, sizeof(tempDirectory.szTempDir)) == 0) return -1; @@ -1446,6 +1493,36 @@ static UINT cliprdr_send_format_list(wfClipboard *clipboard, UINT32 connID) return rc; } +// Ensure the event is not signaled, and reset it if it is. +UINT try_reset_event(HANDLE event) +{ + if (!event) + { + return ERROR_INTERNAL_ERROR; + } + + DWORD result = WaitForSingleObject(event, 0); + if (result == WAIT_OBJECT_0) + { + if (!ResetEvent(event)) + { + return GetLastError(); + } + else + { + return ERROR_SUCCESS; + } + } + else if (result == WAIT_TIMEOUT) + { + return ERROR_SUCCESS; + } + else + { + return ERROR_INTERNAL_ERROR; + } +} + UINT wait_response_event(UINT32 connID, wfClipboard *clipboard, HANDLE event, BOOL* recvedFlag, void **data) { UINT rc = ERROR_SUCCESS; @@ -1470,6 +1547,11 @@ UINT wait_response_event(UINT32 connID, wfClipboard *clipboard, HANDLE event, BO } } + if (!ResetEvent(event)) + { + // NOTE: critical error here, crash may be better + } + if (clipboard->context->IsStopped == TRUE) { wf_do_empty_cliprdr(clipboard); @@ -1481,12 +1563,6 @@ UINT wait_response_event(UINT32 connID, wfClipboard *clipboard, HANDLE event, BO return ERROR_INTERNAL_ERROR; } - if (!ResetEvent(event)) - { - // NOTE: critical error here, crash may be better - rc = ERROR_INTERNAL_ERROR; - } - if ((*data) == NULL) { rc = ERROR_INTERNAL_ERROR; @@ -1530,12 +1606,18 @@ static UINT cliprdr_send_data_request(UINT32 connID, wfClipboard *clipboard, UIN if (!clipboard || !clipboard->context || !clipboard->context->ClientFormatDataRequest) return ERROR_INTERNAL_ERROR; + rc = try_reset_event(clipboard->formatDataRespEvent); + if (rc != ERROR_SUCCESS) + { + return rc; + } + clipboard->formatDataRespReceived = FALSE; + remoteFormatId = get_remote_format_id(clipboard, formatId); formatDataRequest.connID = connID; formatDataRequest.requestedFormatId = remoteFormatId; clipboard->requestedFormatId = formatId; - clipboard->formatDataRespReceived = FALSE; rc = clipboard->context->ClientFormatDataRequest(clipboard->context, &formatDataRequest); if (rc != ERROR_SUCCESS) { @@ -1555,7 +1637,17 @@ UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, co if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsRequest) return ERROR_INTERNAL_ERROR; + rc = try_reset_event(clipboard->req_fevent); + if (rc != ERROR_SUCCESS) + { + return rc; + } + clipboard->req_f_received = FALSE; + fileContentsRequest.connID = connID; + // streamId is `IStream*` pointer, though it is not very good on a 64-bit system. + // But it is OK, because it is only used to check if the stream is the same in + // `wf_cliprdr_server_file_contents_request()` function. fileContentsRequest.streamId = (UINT32)(ULONG_PTR)streamid; fileContentsRequest.listIndex = index; fileContentsRequest.dwFlags = flag; @@ -1564,7 +1656,6 @@ UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 connID, co fileContentsRequest.cbRequested = nreq; fileContentsRequest.clipDataId = 0; fileContentsRequest.msgFlags = 0; - clipboard->req_f_received = FALSE; rc = clipboard->context->ClientFileContentsRequest(clipboard->context, &fileContentsRequest); if (rc != ERROR_SUCCESS) { @@ -1801,6 +1892,7 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM break; case WM_DESTROYCLIPBOARD: + // to-do: clear clipboard data? case WM_ASKCBFORMATNAME: case WM_HSCROLLCLIPBOARD: case WM_PAINTCLIPBOARD: @@ -1917,7 +2009,7 @@ static BOOL wf_cliprdr_get_file_contents(WCHAR *file_name, BYTE *buffer, LONG po LONG positionHigh, DWORD nRequested, DWORD *puSize) { BOOL res = FALSE; - HANDLE hFile; + HANDLE hFile = NULL; DWORD nGet, rc; if (!file_name || !buffer || !puSize) @@ -1945,9 +2037,11 @@ static BOOL wf_cliprdr_get_file_contents(WCHAR *file_name, BYTE *buffer, LONG po res = TRUE; error: - - if (!CloseHandle(hFile)) - res = FALSE; + if (hFile) + { + if (!CloseHandle(hFile)) + res = FALSE; + } if (res) *puSize = nGet; @@ -1958,8 +2052,8 @@ static BOOL wf_cliprdr_get_file_contents(WCHAR *file_name, BYTE *buffer, LONG po /* path_name has a '\' at the end. e.g. c:\newfolder\, file_name is c:\newfolder\new.txt */ static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR *file_name, size_t pathLen) { - HANDLE hFile; - FILEDESCRIPTORW *fd; + HANDLE hFile = NULL; + FILEDESCRIPTORW *fd = NULL; fd = (FILEDESCRIPTORW *)calloc(1, sizeof(FILEDESCRIPTORW)); if (!fd) @@ -1988,7 +2082,16 @@ static FILEDESCRIPTORW *wf_cliprdr_get_file_descriptor(WCHAR *file_name, size_t } fd->nFileSizeLow = GetFileSize(hFile, &fd->nFileSizeHigh); - wcscpy_s(fd->cFileName, sizeof(fd->cFileName) / 2, file_name + pathLen); + if ((wcslen(file_name + pathLen) + 1) > sizeof(fd->cFileName) / sizeof(fd->cFileName[0])) + { + // The file name is too long, which is not a normal case. + // So we just return NULL. + CloseHandle(hFile); + free(fd); + return NULL; + } + + wcsncpy_s(fd->cFileName, sizeof(fd->cFileName) / sizeof(fd->cFileName[0]), file_name + pathLen, wcslen(file_name + pathLen) + 1); CloseHandle(hFile); return fd; @@ -2037,7 +2140,12 @@ static BOOL wf_cliprdr_add_to_file_arrays(wfClipboard *clipboard, WCHAR *full_fi if (!clipboard->file_names[clipboard->nFiles]) return FALSE; - wcscpy_s(clipboard->file_names[clipboard->nFiles], MAX_PATH, full_file_name); + // `MAX_PATH` is long enough for the file name. + // So we just return FALSE if the file name is too long, which is not a normal case. + if ((wcslen(full_file_name) + 1) > MAX_PATH) + return FALSE; + + wcsncpy_s(clipboard->file_names[clipboard->nFiles], MAX_PATH, full_file_name, wcslen(full_file_name) + 1); /* add to descriptor array */ clipboard->fileDescriptor[clipboard->nFiles] = wf_cliprdr_get_file_descriptor(full_file_name, pathLen); @@ -2061,8 +2169,8 @@ static BOOL wf_cliprdr_traverse_directory(wfClipboard *clipboard, WCHAR *Dir, si if (!clipboard || !Dir) return FALSE; - // StringCchCopy(DirSpec, MAX_PATH, Dir); - // StringCchCat(DirSpec, MAX_PATH, TEXT("\\*")); + if (wcslen(Dir) + 3 > MAX_PATH) + return FALSE; StringCchCopyW(DirSpec, MAX_PATH, Dir); StringCchCatW(DirSpec, MAX_PATH, L"\\*"); @@ -2091,9 +2199,8 @@ static BOOL wf_cliprdr_traverse_directory(wfClipboard *clipboard, WCHAR *Dir, si if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) { WCHAR DirAdd[MAX_PATH]; - // StringCchCopy(DirAdd, MAX_PATH, Dir); - // StringCchCat(DirAdd, MAX_PATH, _T("\\")); - // StringCchCat(DirAdd, MAX_PATH, FindFileData.cFileName); + if (wcslen(Dir) + wcslen(FindFileData.cFileName) + 2 > MAX_PATH) + return FALSE; StringCchCopyW(DirAdd, MAX_PATH, Dir); StringCchCatW(DirAdd, MAX_PATH, L"\\"); StringCchCatW(DirAdd, MAX_PATH, FindFileData.cFileName); @@ -2107,10 +2214,8 @@ static BOOL wf_cliprdr_traverse_directory(wfClipboard *clipboard, WCHAR *Dir, si else { WCHAR fileName[MAX_PATH]; - // StringCchCopy(fileName, MAX_PATH, Dir); - // StringCchCat(fileName, MAX_PATH, _T("\\")); - // StringCchCat(fileName, MAX_PATH, FindFileData.cFileName); - + if (wcslen(Dir) + wcslen(FindFileData.cFileName) + 2 > MAX_PATH) + return FALSE; StringCchCopyW(fileName, MAX_PATH, Dir); StringCchCatW(fileName, MAX_PATH, L"\\"); StringCchCatW(fileName, MAX_PATH, FindFileData.cFileName); @@ -2255,9 +2360,11 @@ static UINT wf_cliprdr_server_format_list(CliprdrClientContext *context, if (context->EnableFiles) { UINT32 *p_conn_id = (UINT32 *)calloc(1, sizeof(UINT32)); - *p_conn_id = formatList->connID; - if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, p_conn_id)) - rc = CHANNEL_RC_OK; + if (p_conn_id) { + *p_conn_id = formatList->connID; + if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, p_conn_id)) + rc = CHANNEL_RC_OK; + } } else { @@ -2278,16 +2385,30 @@ static UINT wf_cliprdr_server_format_list(CliprdrClientContext *context, // SetClipboardData(clipboard->format_mappings[i].local_format_id, NULL); FORMAT_IDS *format_ids = (FORMAT_IDS *)calloc(1, sizeof(FORMAT_IDS)); - format_ids->connID = formatList->connID; - format_ids->size = (UINT32)clipboard->map_size; - format_ids->formats = (UINT32 *)calloc(format_ids->size, sizeof(UINT32)); - for (i = 0; i < format_ids->size; ++i) + if (format_ids) { - format_ids->formats[i] = clipboard->format_mappings[i].local_format_id; - } - if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, DELAYED_RENDERING, format_ids)) - { - rc = CHANNEL_RC_OK; + format_ids->connID = formatList->connID; + format_ids->size = (UINT32)clipboard->map_size; + format_ids->formats = (UINT32 *)calloc(format_ids->size, sizeof(UINT32)); + if (format_ids->formats) + { + for (i = 0; i < format_ids->size; ++i) + { + format_ids->formats[i] = clipboard->format_mappings[i].local_format_id; + } + if (PostMessage(clipboard->hwnd, WM_CLIPRDR_MESSAGE, DELAYED_RENDERING, format_ids)) + { + rc = CHANNEL_RC_OK; + } + else + { + rc = ERROR_INTERNAL_ERROR; + } + } + else + { + rc = ERROR_INTERNAL_ERROR; + } } else { @@ -2482,17 +2603,28 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, p += len + 1, clipboard->nFiles++) { int cchWideChar; - WCHAR *wFileName; cchWideChar = MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, NULL, 0); wFileName = (LPWSTR)calloc(cchWideChar, sizeof(WCHAR)); - MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, wFileName, cchWideChar); - wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar); + if (wFileName) + { + MultiByteToWideChar(CP_ACP, MB_COMPOSITE, p, len, wFileName, cchWideChar); + wf_cliprdr_process_filename(clipboard, wFileName, cchWideChar); + free(wFileName); + } + else + { + rc = ERROR_INTERNAL_ERROR; + GlobalUnlock(stg_medium.hGlobal); + ReleaseStgMedium(&stg_medium); + goto exit; + } } } GlobalUnlock(stg_medium.hGlobal); ReleaseStgMedium(&stg_medium); resp: + // size will not overflow, because size type is size_t (unsigned __int64) size = 4 + clipboard->nFiles * sizeof(FILEDESCRIPTORW); groupDsc = (FILEGROUPDESCRIPTORW *)malloc(size); @@ -2532,10 +2664,17 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, globlemem = (char *)GlobalLock(hClipdata); size = (int)GlobalSize(hClipdata); buff = malloc(size); - CopyMemory(buff, globlemem, size); + if (buff) + { + CopyMemory(buff, globlemem, size); + rc = ERROR_SUCCESS; + } + else + { + rc = ERROR_INTERNAL_ERROR; + } GlobalUnlock(hClipdata); CloseClipboard(); - rc = ERROR_SUCCESS; } } else diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 4554617a7d61..21f9e7aea0de 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -326,6 +326,7 @@ enum ClipboardFormat { ImageRgba = 21; ImagePng = 22; ImageSvg = 23; + Special = 31; } message Clipboard { @@ -334,6 +335,8 @@ message Clipboard { int32 width = 3; int32 height = 4; ClipboardFormat format = 5; + // Special format name, only used when format is Special. + string special_name = 6; } message MultiClipboards { repeated Clipboard clipboards = 1; } diff --git a/src/clipboard.rs b/src/clipboard.rs index 0510eca6a2d4..4e6295db9f24 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -12,6 +12,9 @@ pub const CLIPBOARD_INTERVAL: u64 = 333; // This format is used to store the flag in the clipboard. const RUSTDESK_CLIPBOARD_OWNER_FORMAT: &'static str = "dyn.com.rustdesk.owner"; +// Add special format for Excel XML Spreadsheet +const CLIPBOARD_FORMAT_EXCEL_XML_SPREADSHEET: &'static str = "XML Spreadsheet"; + lazy_static::lazy_static! { static ref ARBOARD_MTX: Arc> = Arc::new(Mutex::new(())); // cache the clipboard msg @@ -30,6 +33,7 @@ const SUPPORTED_FORMATS: &[ClipboardFormat] = &[ ClipboardFormat::ImageRgba, ClipboardFormat::ImagePng, ClipboardFormat::ImageSvg, + ClipboardFormat::Special(CLIPBOARD_FORMAT_EXCEL_XML_SPREADSHEET), ClipboardFormat::Special(RUSTDESK_CLIPBOARD_OWNER_FORMAT), ]; @@ -267,8 +271,8 @@ impl ClipboardContext { } if !force { for c in data.iter() { - if let ClipboardData::Special((_, d)) = c { - if side.is_owner(d) { + if let ClipboardData::Special((s, d)) = c { + if s == RUSTDESK_CLIPBOARD_OWNER_FORMAT && side.is_owner(d) { return Ok(vec![]); } } @@ -276,7 +280,10 @@ impl ClipboardContext { } Ok(data .into_iter() - .filter(|c| !matches!(c, ClipboardData::Special(_))) + .filter(|c| match c { + ClipboardData::Special((s, _)) => s != RUSTDESK_CLIPBOARD_OWNER_FORMAT, + _ => true, + }) .collect()) } @@ -454,12 +461,30 @@ mod proto { } } + fn special_to_proto(d: Vec, s: String) -> Clipboard { + let compressed = compress_func(&d); + let compress = compressed.len() < d.len(); + let content = if compress { + compressed + } else { + s.bytes().collect::>() + }; + Clipboard { + compress, + content: content.into(), + format: ClipboardFormat::Special.into(), + special_name: s, + ..Default::default() + } + } + fn clipboard_data_to_proto(data: ClipboardData) -> Option { let d = match data { ClipboardData::Text(s) => plain_to_proto(s, ClipboardFormat::Text), ClipboardData::Rtf(s) => plain_to_proto(s, ClipboardFormat::Rtf), ClipboardData::Html(s) => plain_to_proto(s, ClipboardFormat::Html), ClipboardData::Image(a) => image_to_proto(a), + ClipboardData::Special((s, d)) => special_to_proto(d, s), _ => return None, }; Some(d) @@ -496,6 +521,9 @@ mod proto { Ok(ClipboardFormat::ImageSvg) => Some(ClipboardData::Image(arboard::ImageData::svg( std::str::from_utf8(&data).unwrap_or_default(), ))), + Ok(ClipboardFormat::Special) => { + Some(ClipboardData::Special((clipboard.special_name, data))) + } _ => None, } }