diff --git a/.clang-format b/.clang-format index 33a6584b85b6..6791025b7eb6 100644 --- a/.clang-format +++ b/.clang-format @@ -104,7 +104,6 @@ ForEachMacros: ... Language: ObjC PointerBindsToType: false -ObjCSpaceAfterProperty: true SortIncludes: false ObjCBlockIndentWidth: 4 ObjCSpaceAfterProperty: false diff --git a/.travis.yml b/.travis.yml index 615a6d47a6d7..b5dc259aaefd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,7 +41,6 @@ addons: - libgsm1-dev - libavcodec-dev - libavutil-dev - - libx264-dev - libxext-dev - ninja-build - libsystemd-dev diff --git a/CMakeLists.txt b/CMakeLists.txt index 918d211f3002..3d4a5e515bb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,9 +34,9 @@ if(NOT DEFINED FREERDP_VENDOR) set(FREERDP_VENDOR 1) endif() -set(CMAKE_COLOR_MAKEFILE ON) - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) +option(CMAKE_COLOR_MAKEFILE "colorful CMake makefile" ON) +option(CMAKE_VERBOSE_MAKEFILE "verbose CMake makefile" ON) +option(CMAKE_POSITION_INDEPENDENT_CODE "build with position independent code (-fPIC or -fPIE)" ON) # Include our extra modules set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) @@ -85,7 +85,7 @@ if ($ENV{BUILD_NUMBER}) endif() set(WITH_LIBRARY_VERSIONING "ON") -set(RAW_VERSION_STRING "2.4.0") +set(RAW_VERSION_STRING "2.11.8-dev") if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag") file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) elseif(USE_VERSION_FROM_GIT_TAG) @@ -111,6 +111,24 @@ else() endif() message("FREERDP_VERSION=${FREERDP_VERSION_FULL}") +if(EXISTS "${PROJECT_SOURCE_DIR}/.source_version" ) + file(READ ${PROJECT_SOURCE_DIR}/.source_version GIT_REVISION) + + string(STRIP ${GIT_REVISION} GIT_REVISION) +elseif(USE_VERSION_FROM_GIT_TAG) + git_get_exact_tag(GIT_REVISION --tags --always) + + if (${GIT_REVISION} STREQUAL "n/a") + git_rev_parse (GIT_REVISION --short) + endif() +endif() + +if (NOT GIT_REVISION) + set(GIT_REVISION ${FREERDP_VERSION}) +endif() + +message(STATUS "Git Revision ${GIT_REVISION}") + set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/") # Compatibility options @@ -147,25 +165,11 @@ if(CCACHE AND WITH_CCACHE) endif() endif(CCACHE AND WITH_CCACHE) -if(EXISTS "${CMAKE_SOURCE_DIR}/.source_version" ) - file(READ ${CMAKE_SOURCE_DIR}/.source_version GIT_REVISION) - - string(STRIP ${GIT_REVISION} GIT_REVISION) -else() - git_get_exact_tag(GIT_REVISION --tags --always) - - if (${GIT_REVISION} STREQUAL "n/a") - git_rev_parse (GIT_REVISION --short) - endif() -endif() - if(CMAKE_CROSSCOMPILING) SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) endif(CMAKE_CROSSCOMPILING) # /Allow to search the host machine for git/ccache -message(STATUS "Git Revision ${GIT_REVISION}") - # Turn on solution folders (2.8.4+) set_property(GLOBAL PROPERTY USE_FOLDERS ON) @@ -221,7 +225,7 @@ endif() if(MSVC) include(MSVCRuntime) if(NOT DEFINED MSVC_RUNTIME) - set(MSVC_RUNTIME "dynamic") + set(MSVC_RUNTIME "dynamic" CACHE STRING "MSVC runtime type [dynamic|static]") endif() if(MSVC_RUNTIME STREQUAL "static") if(BUILD_SHARED_LIBS) @@ -351,7 +355,7 @@ if(NOT IOS) find_package(Threads REQUIRED) endif() -if(NOT WIN32) +if(NOT WIN32 AND NOT IOS) CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) @@ -528,7 +532,6 @@ add_definitions(-DWINPR_EXPORTS -DFREERDP_EXPORTS) if(NOT IOS) check_include_files(fcntl.h HAVE_FCNTL_H) check_include_files(unistd.h HAVE_UNISTD_H) - check_include_files(execinfo.h HAVE_EXECINFO_H) check_include_files(inttypes.h HAVE_INTTYPES_H) check_include_files(sys/modem.h HAVE_SYS_MODEM_H) check_include_files(sys/filio.h HAVE_SYS_FILIO_H) @@ -536,6 +539,18 @@ if(NOT IOS) check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H) check_include_files(sys/select.h HAVE_SYS_SELECT_H) check_include_files(syslog.h HAVE_SYSLOG_H) + check_include_files(execinfo.h HAVE_EXECINFO_HEADER) + if (HAVE_EXECINFO_HEADER) + check_symbol_exists(backtrace execinfo.h HAVE_EXECINFO_BACKTRACE) + check_symbol_exists(backtrace_symbols execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS) + check_symbol_exists(backtrace_symbols_fd execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD) + + # Some implementations (e.g. Android NDK API < 33) provide execinfo.h but do not define + # the backtrace functions. Disable detection for these cases + if (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS AND HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD) + set(HAVE_EXECINFO_H ON) + endif() + endif() else() set(HAVE_FCNTL_H 1) set(HAVE_UNISTD_H 1) @@ -594,6 +609,11 @@ if(ANDROID) set (WITH_NEON OFF) endif() + if(ANDROID_ABI STREQUAL arm64-v8a) + # https://github.com/android/ndk/issues/910 + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfloat-abi=softfp") + endif() + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") add_definitions(-DNDK_DEBUG=1) @@ -652,6 +672,7 @@ if(UNIX OR CYGWIN) include(CheckFunctionExists) check_function_exists(getlogin_r HAVE_GETLOGIN_R) + check_function_exists(getpwuid_r HAVE_GETPWUID_R) else() set(X11_FEATURE_TYPE "DISABLED") set(WAYLAND_FEATURE_TYPE "DISABLED") @@ -719,10 +740,6 @@ set(JPEG_FEATURE_TYPE "OPTIONAL") set(JPEG_FEATURE_PURPOSE "codec") set(JPEG_FEATURE_DESCRIPTION "use JPEG library") -set(X264_FEATURE_TYPE "OPTIONAL") -set(X264_FEATURE_PURPOSE "codec") -set(X264_FEATURE_DESCRIPTION "use x264 library") - set(OPENH264_FEATURE_TYPE "OPTIONAL") set(OPENH264_FEATURE_PURPOSE "codec") set(OPENH264_FEATURE_DESCRIPTION "use OpenH264 library") @@ -808,7 +825,6 @@ if(ANDROID) set(PULSE_FEATURE_TYPE "DISABLED") set(CUPS_FEATURE_TYPE "DISABLED") set(PCSC_FEATURE_TYPE "DISABLED") - set(FFMPEG_FEATURE_TYPE "DISABLED") set(VAAPI_FEATURE_TYPE "DISABLED") set(OPENSLES_FEATURE_TYPE "REQUIRED") endif() @@ -831,7 +847,6 @@ find_feature(PCSC ${PCSC_FEATURE_TYPE} ${PCSC_FEATURE_PURPOSE} ${PCSC_FEATURE_DE find_feature(FFmpeg ${FFMPEG_FEATURE_TYPE} ${FFMPEG_FEATURE_PURPOSE} ${FFMPEG_FEATURE_DESCRIPTION}) find_feature(JPEG ${JPEG_FEATURE_TYPE} ${JPEG_FEATURE_PURPOSE} ${JPEG_FEATURE_DESCRIPTION}) -find_feature(x264 ${X264_FEATURE_TYPE} ${X264_FEATURE_PURPOSE} ${X264_FEATURE_DESCRIPTION}) find_feature(OpenH264 ${OPENH264_FEATURE_TYPE} ${OPENH264_FEATURE_PURPOSE} ${OPENH264_FEATURE_DESCRIPTION}) find_feature(OpenCL ${OPENCL_FEATURE_TYPE} ${OPENCL_FEATURE_PURPOSE} ${OPENCL_FEATURE_DESCRIPTION}) find_feature(GSM ${GSM_FEATURE_TYPE} ${GSM_FEATURE_PURPOSE} ${GSM_FEATURE_DESCRIPTION}) @@ -857,18 +872,23 @@ if (WITH_DSP_FFMPEG) # Deactivate FFmpeg backend for sound, if the version is too old. # See libfreerdp/codec/dsp_ffmpeg.h file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version.h" AV_VERSION_FILE REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+") + if (EXISTS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h") + file(STRINGS "${AVCODEC_INCLUDE_DIR}/libavcodec/version_major.h" AV_VERSION_FILE2 REGEX "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+") + list(APPEND AV_VERSION_FILE ${AV_VERSION_FILE2}) + endif() + FOREACH(item ${AV_VERSION_FILE}) STRING(REGEX MATCH "LIBAVCODEC_VERSION_M[A-Z]+[\t ]*[0-9]+" litem ${item}) - IF(litem) - string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem}) - list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN) - if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2") - message(ERROR "invalid entry in libavcodec version header ${item}") - endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2") - list(GET VSPLIT_LINE 0 VNAME) - list(GET VSPLIT_LINE 1 VVALUE) - set(${VNAME} ${VVALUE}) - ENDIF(litem) + IF(litem) + string(REGEX REPLACE "[ \t]+" ";" VSPLIT_LINE ${litem}) + list(LENGTH VSPLIT_LINE VSPLIT_LINE_LEN) + if (NOT "${VSPLIT_LINE_LEN}" EQUAL "2") + message(ERROR "invalid entry in libavcodec version header ${item}") + endif(NOT "${VSPLIT_LINE_LEN}" EQUAL "2") + list(GET VSPLIT_LINE 0 VNAME) + list(GET VSPLIT_LINE 1 VVALUE) + set(${VNAME} ${VVALUE}) + ENDIF(litem) ENDFOREACH(item ${AV_VERSION_FILE}) set(AVCODEC_VERSION "${LIBAVCODEC_VERSION_MAJOR}.${LIBAVCODEC_VERSION_MINOR}.${LIBAVCODEC_VERSION_MICRO}") @@ -917,7 +937,7 @@ if(MBEDTLS_FOUND) add_definitions("-DWITH_MBEDTLS") endif() -if (WITH_X264 OR WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG) +if (WITH_OPENH264 OR WITH_MEDIA_FOUNDATION OR WITH_FFMPEG OR WITH_MEDIACODEC) set(WITH_GFX_H264 ON) else() set(WITH_GFX_H264 OFF) @@ -970,7 +990,16 @@ if (APPLE) else (APPLE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) if (NOT FREEBSD) - set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..") + if (NOT BUILTIN_CHANNELS) + if (NOT DEFINED WITH_PLUGIN_RPATH_ONLY) + set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..:\$ORIGIN/../${FREERDP_PLUGIN_PATH}") + else() + # we need to supply this run path, even if not using RPATH in general + set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${FREERDP_PLUGIN_PATH}") + endif() + else() + set(CMAKE_INSTALL_RPATH "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/..") + endif() endif() endif(APPLE) @@ -1033,15 +1062,6 @@ add_subdirectory(include) add_subdirectory(libfreerdp) -if (IOS) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.0") - if (IOS_PLATFORM MATCHES "SIMULATOR") - set(CMAKE_OSX_SYSROOT "iphonesimulator") - else() - set(CMAKE_OSX_SYSROOT "iphoneos") - endif() -endif() - # RdTk include_directories("${CMAKE_SOURCE_DIR}/rdtk/include") include_directories("${CMAKE_BINARY_DIR}/rdtk/include") diff --git a/ChangeLog b/ChangeLog index 1389c23b607e..a7ab1ae20579 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,312 @@ +# 2026-02-25 Version 2.11.8 + +This release will be the last official one for the 2.x series of FreeRDP. +From this point on we will no longer maintain this branch and urge all to +upgrade to FreeRDP 3.x. + +Noteworthy changes: +* Backported #12319 bugfixes from 3.x +* Fix incompatible pointer type issues by @AlessandroBono (#10172) +* X11: fix pointer/integer type mismatch by @floppym (#10207) +* Warn backport by @akallabeth (#10456) +* [core] eliminate rdpRdp::instance by @akallabeth (#10782) +* X11 client: ignore grab related LeaveNotify events by @akallabeth (#10888) +* [winpr,pubsub] add NULL parameter checks by @akallabeth (#10898) +* fix: correct server port assignment logic by @LeeEirc (#11793) + +For a complete and detailed change log since the last release run: +git log 2.11.8...2.11.7 + +# 2024-04-22 Version 2.11.7 + +Noteworthy changes: +* Backported oss-fuzz fixes + +For a complete and detailed change log since the last release run: +git log 2.11.7...2.11.6 + +# 2024-04-17 Version 2.11.6 + +CVE: + CVE-2024-32041 [Low[ OutOfBound Read in zgfx_decompress_segment + CVE-2024-32039 [Moderate] Integer overflow & OutOfBound Write in clear_decompress_residual_data + CVE-2024-32040 [Low] integer underflow in nsc_rle_decode + CVE-2024-32458 [Low] OutOfBound Read in planar_skip_plane_rle + CVE-2024-32459 [Low] OutOfBound Read in ncrush_decompress + CVE-2024-32460 [Low] OutOfBound Read in interleaved_decompress + +Noteworthy changes: +* Backported #10077 + +For a complete and detailed change log since the last release run: +git log 2.11.6...2.11.5 + +# 2024-01-19 Version 2.11.5 + +Noteworthy changes: +* Fix integer overflow in progressive decoder +* Update OpenSSL API usage for compatiblility with newer versions (#9747) +* Prevent NULL dereference for single thread decoder (#9712) + +For a complete and detailed change log since the last release run: +git log 2.11.5...2.11.4 + +# 2023-12-14 Version 2.11.4 + +Notworthy changes: +* fix a typo in unicode commit (#9652) + +For a complete and detailed change log since the last release run: +git log 2.11.4...2.11.3 + +# 2023-12-14 Version 2.11.3 + +Notworthy changes: +* Disabled windows MEDIA FOUNDATION h264 decoder due to reported issues (#9469) +* Fix issues with drive redirection (#9530,9554, #9586, #9617) +* Use endian safe ICU string converter (#9631) +* Improve AAC support (#9577) +* Fix swiss german keyboard layout (#9560) +* Enable rfx-mode:image (#9428) + +For a complete and detailed change log since the last release run: +git log 2.11.3...2.11.2 + +# 2023-09-20 Version 2.11.2 + +Notworthy changes: +* Backported #9378: backported wArrayList (optional) copy on insert +* Backported #9360: backported certificate algorithm detection + +For a complete and detailed change log since the last release run: +git log 2.11.2...2.11.1 + +# 2023-09-04 Version 2.11.1 + +Notworthy changes: +* Backported #9356: Fix issues with order updates + +For a complete and detailed change log since the last release run: +git log 2.11.1..2.11.0 + +# 2023-08-28 Version 2.11.0 + +Noteworthy changes: +* Various input validation fixes +* Added various CMake options #9317 +* LibreSSL build fixes #8709 + +Fixed issues: +* Backported #9233: Big endian support +* Backported #9099: Mouse grabbing support +* Backported #6851: wayland scrolling fix +* Backported #8690: Update h264 to use new FFMPEG API +* Backported #7306: early bail from update_read_window_state_order breaks protocol +* Backported #8903: rdpecam/server: Remove wrong assertion +* Backported #8994: bounds checks for gdi/gfx rectangles +* Backported #9023: enforce rdpdr client side state checks +* Backported #6331: deactivate mouse grabbing by default +* Cherry-pick out of #9172: channels/cliprdr: Fix writing incorrect PDU type for unlock PDUs + +# 2023-02-16 Version 2.10.0 + +Noteworthy changes: +* Fix android build scripts, use CMake from SDK +* Fix connection negotiation with mstsc/msrdc #8426 +* [ntlm]: use rfc5929 binding hash algorithm #8430 +* [channels,printer] Fixed reference counting #8433 +* Fix uwac pixman #8439 +* Fix Rdp security #8457 +* [client,x11] Detect key autorepeat #8522 +* [build] add channel path to RPATH #8551 +* Fix build with BUILTIN_CHANNELS=OFF #8560 +* revert changes so that the osmajortype/osminortype is not overwritten #8571 +* [uwac] do not use iso C functions #8604 +* [winpr,sam] fix inalid NULL arguments #8605 +* Fix incompatible function pointer types #8625 + +Fixed issues: +* Backported #8581: Ignore data PDUs for DVCs that were not opened successfully +* Backported #8498: [channel,urbdrc] fix type of usb hotplug callback +* Backported #8537: Extended info enforce limits +* Backported #8611: [core] add missing redirection fields + +# 2022-11-16 Version 2.9.0 + +Noteworthy changes: +* Backported #8252: Support sending server redirection PDU +* Backported #8406: Ensure X11 client cursor is never smaller 1x1 +* Backported #8403: Fixed multiple client side input validation issues + (CVE-2022-39316, CVE-2022-39317, CVE-2022-39318, CVE-2022-39319, + CVE-2022-39320, CVE-2022-41877, CVE-2022-39347) +* Backported #7282: Proxy server now discards input events sent before + activation was received +* Backported #8324: Internal replacements for md4, md5 and hmac-md5 + For the time being the RDP protocol requires these outdated hash + algorithms. So any distribution that wants to ship a working + FreeRDP should check the options WITH_INTERNAL_MD4 (and depending + on OpenSSL deprecation status WITH_INTERNAL_MD5) + +Fixed issues: +* Backported #8341: Null checks in winpr_Digest_Free +* Backported #8335: Missing NULL return in winpr_Digest_New +* Backported #8192: Support for audin version 2 microphone channel +* Backported #7282: Discard input events before activation (Fixes #8374) + +For a complete and detailed change log since the last release run: +git log 2.8.1..2.9.0 + + +# 2022-10-12 Version 2.8.1 + +Noteworthy changes: +* Fixed CVE-2022-39282 +* Fixed CVE-2022-39283 +* Added missing commit for backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable) +* Added hash checks for android build script dependencies + +Fixed issues: +* Backported #8190: Fix build break with newer FFMPEG versions +* Backported #8234: Updated flatpak with build script +* Backported #8210: Better execinfo support check for android +* Backported #7708: Header now defines DumpThreadHandles +* Backported #8176: Check fullscreen state and not setting +* Backported #8236: Send resize on window state change +* Backported #7611: Audin macOS monterey fix +* Backported #8291: Android build script update + +# 2022-07-28 Version 2.8.0 + +Noteworthy changes: + +* Backported API to get peer accepted channel option flags +* Backported API to get peer accepted channel names +* Backported Stream_CheckAndLogRequiredLength +* Backported #7954: Add server side handling for [MS-RDPET] +* Backported #8010: Add server side handling for [MS-RDPECAM] +* Backported #8041: Remove ALAW/ULAW codecs from linux backends (unreliable) +* Backported #8051: Relieve CLIPRDR filename restriction when connecting to non-MS Windows servers +* Backported #8048: TLS version control +* Backported #7987: Add a new command line arg to enforce tls1.2 + +Fixed issues: + +* Fixed #7837: Prevent out of bound reads for FFMPEG +* Backported #7859 and #7861: Unwind support for backtrace generation +* Backported #7440: wlfreerdp appid +* Backported #7832: RAIL window restore +* Backported #7833: Refactored WinPR thread locking +* Backported #7893: Mac rdpsnd memory leak fixes +* Backported #7895: Mac audin memory leak fixes +* Backported #7898: Automatic android versioning +* Backported #7916: GFX 10.7 capability support +* Backported #7949: Server RDPSND API improvements +* Backported #7957: Server DVC API improvements +* Backported #7760: Fixed osMinorType values +* Backported #8013: Add missing osMajorType values +* Backported #8076: Fix wrong usage of subband diffing flag (tile artifact fix) + +For a complete and detailed change log since the last release run: +git log 2.7.0..2.8.0 + + +# 2022-04-25 Version 2.7.0 + +Noteworthy changes: +* Backported OpenSSL3 gateway support (#7822) +* Backported various NTLM fixes +* Backported WINPR_ASSERT to ease future backports + +Fixed issues: +* Backported #6786: Use /network:auto by default +* Backported #7714: Workaround for broken surface frame marker +* Backported #7733: Support 10bit X11 color (BGRX32 only) +* Backported #7745: GFX progressive double free +* Backported #7808: Disable websockets with /gt:rpc +* Backported #7815: RAIL expect LOGON_MSG_SESSION_CONTINUE + +Important notes: + +For a complete and detailed change log since the last release run: +git log 2.6.1..2.7.0 + +# 2022-03-07 Version 2.6.1 + +Noteworthy changes: + +Fixed issues: +* Backported freerdp_abort_connect during freerdp_connect fix (#7700) +* Backported improved version dection see docs/version_detection.md for details +* Backported various rdpsnd fixes (#7695) + +Important notes: + +For a complete and detailed change log since the last release run: +git log 2.0.0..2.6.1 + +# 2022-02-22 Version 2.6.0 + +Noteworthy changes: +* Backported android FFMPEG build scripts +* Updated android build dependencies + +Fixed issues: +* Backported #7303: Fix PDU length for RDPINPUT_PROTOCOL_V300 +* Backported #7658: Sanitize optional physical monitor size values +* Backported #7426: Wayland memory corruption +* Backported #7293: Remove unused codec x264 +* Backported #7541: Allow resolutions larger 2048x2048 +* Backported #7574: FFMPEG 5.0 support +* Backported #7578: FFMPEG 5.0 support +* Backported #7580: Fixed device hotplugging +* Backported #7583: GetUserNameExA: Prefer getpwuid_r over getlogin_r over getlogin +* Backported #7585: Android Mediacodec support + +Important notes: + +For a complete and detailed change log since the last release run: +git log 2.5.0..2.6.0 + +# 2022-01-12 Version 2.5.0 + +Noteworthy changes: +* Fixed smartcard login in case a redirection occurs the pin was lost +* Backported windows client drawing fixes +* Backported improved macOS keyboard layout detection +* Backported TcpConnectTimeout +* Backported LibreSSL compatibility patches +* Backported signal handler backtrace +* Backported OpenSSL 3.0 support + +Fixed issues: +* Backport #7539: Wayland client clipboard issues +* Backport #7509: Various fixes regarding registry emulation, addin loader + and updated locale detection +* Backport #7466: Android android_register_pointer missing initialization + +Important notes: + +For a complete and detailed change log since the last release run: +git log 2.4.1..2.5.0 + +# 2021-10-20 Version 2.4.1 + +Noteworthy changes: +* Refactored RPC gateway parsing code +* OpenSSL 3.0 compatibility fixes +* USB redirection: fixed transfer lengths + +Fixed issues: +* #7363: Length checks in ConvertUTF8toUTF16 +* #7349: Added checks for bitmap width and heigth values + +Important notes: +* CVE-2021-41159: Improper client input validation for gateway connections allows to overwrite memory +* CVE-2021-41160: Improper region checks in all clients allow out of bound write to memory + +For a complete and detailed change log since the last release run: +git log 2.4.0..2.4.1 + # 2021-07-27 Version 2.4.0 Noteworthy changes: @@ -486,4 +795,3 @@ Virtual Channels: * rdpsnd (Sound Redirection) * alsa support * pulse support - diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000000..e138ec5d6a77 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/channels/ainput/CMakeLists.txt b/channels/ainput/CMakeLists.txt new file mode 100644 index 000000000000..b1d1a6a4a8af --- /dev/null +++ b/channels/ainput/CMakeLists.txt @@ -0,0 +1,27 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Armin Novak +# Copyright 2022 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel("ainput") + +if(WITH_CLIENT_CHANNELS) + add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() + +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/ainput/ChannelOptions.cmake b/channels/ainput/ChannelOptions.cmake new file mode 100644 index 000000000000..eafc6c0f5939 --- /dev/null +++ b/channels/ainput/ChannelOptions.cmake @@ -0,0 +1,13 @@ + +set(OPTION_DEFAULT OFF) +set(OPTION_CLIENT_DEFAULT ON) +set(OPTION_SERVER_DEFAULT ON) + +define_channel_options(NAME "ainput" TYPE "dynamic" + DESCRIPTION "Advanced Input Virtual Channel Extension" + SPECIFICATIONS "[XXXXX]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_client_options(${OPTION_CLIENT_DEFAULT}) +define_channel_server_options(${OPTION_SERVER_DEFAULT}) + diff --git a/channels/ainput/client/CMakeLists.txt b/channels/ainput/client/CMakeLists.txt new file mode 100644 index 000000000000..4cf2d22735b4 --- /dev/null +++ b/channels/ainput/client/CMakeLists.txt @@ -0,0 +1,34 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Armin Novak +# Copyright 2022 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_client("ainput") + +set(${MODULE_PREFIX}_SRCS + ainput_main.c + ainput_main.h) + +include_directories(..) + +add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry") + +if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT BUILTIN_CHANNELS AND BUILD_SHARED_LIBS) + install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols) +endif() + +target_link_libraries(${MODULE_NAME} winpr) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client") diff --git a/channels/ainput/client/ainput_main.c b/channels/ainput/client/ainput_main.c new file mode 100644 index 000000000000..0f44b309213c --- /dev/null +++ b/channels/ainput/client/ainput_main.c @@ -0,0 +1,331 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Advanced Input Virtual Channel Extension + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include + +#include "ainput_main.h" +#include +#include +#include + +#include "../common/ainput_common.h" + +#define TAG CHANNELS_TAG("ainput.client") + +typedef struct AINPUT_CHANNEL_CALLBACK_ AINPUT_CHANNEL_CALLBACK; +struct AINPUT_CHANNEL_CALLBACK_ +{ + IWTSVirtualChannelCallback iface; + + IWTSPlugin* plugin; + IWTSVirtualChannelManager* channel_mgr; + IWTSVirtualChannel* channel; +}; + +typedef struct AINPUT_LISTENER_CALLBACK_ AINPUT_LISTENER_CALLBACK; +struct AINPUT_LISTENER_CALLBACK_ +{ + IWTSListenerCallback iface; + + IWTSPlugin* plugin; + IWTSVirtualChannelManager* channel_mgr; + AINPUT_CHANNEL_CALLBACK* channel_callback; +}; + +typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN; +struct AINPUT_PLUGIN_ +{ + IWTSPlugin iface; + + AINPUT_LISTENER_CALLBACK* listener_callback; + IWTSListener* listener; + UINT32 MajorVersion; + UINT32 MinorVersion; + BOOL initialized; + CRITICAL_SECTION lock; +}; + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data) +{ + UINT16 type; + AINPUT_PLUGIN* ainput; + AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback; + + WINPR_ASSERT(callback); + WINPR_ASSERT(data); + + ainput = (AINPUT_PLUGIN*)callback->plugin; + WINPR_ASSERT(ainput); + + if (Stream_GetRemainingLength(data) < 2) + return ERROR_NO_DATA; + Stream_Read_UINT16(data, type); + switch (type) + { + case MSG_AINPUT_VERSION: + if (Stream_GetRemainingLength(data) < 8) + return ERROR_NO_DATA; + Stream_Read_UINT32(data, ainput->MajorVersion); + Stream_Read_UINT32(data, ainput->MinorVersion); + break; + default: + WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type); + break; + } + + return CHANNEL_RC_OK; +} + +static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y) +{ + AINPUT_PLUGIN* ainput; + AINPUT_CHANNEL_CALLBACK* callback; + BYTE buffer[32] = { 0 }; + UINT64 time; + UINT rc; + wStream sbuffer = { 0 }; + wStream* s = &sbuffer; + + Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); + + WINPR_ASSERT(s); + WINPR_ASSERT(context); + + time = GetTickCount64(); + ainput = (AINPUT_PLUGIN*)context->handle; + WINPR_ASSERT(ainput); + WINPR_ASSERT(ainput->listener_callback); + + if (ainput->MajorVersion != AINPUT_VERSION_MAJOR) + { + WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.", + ainput->MajorVersion, ainput->MinorVersion); + return CHANNEL_RC_UNSUPPORTED_VERSION; + } + + { + char buffer[128] = { 0 }; + WLog_VRB(TAG, "[%s] sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, + __FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y); + } + + /* Message type */ + Stream_Write_UINT16(s, MSG_AINPUT_MOUSE); + + /* Event data */ + Stream_Write_UINT64(s, time); + Stream_Write_UINT64(s, flags); + Stream_Write_INT32(s, x); + Stream_Write_INT32(s, y); + Stream_SealLength(s); + + /* ainput back what we have received. AINPUT does not have any message IDs. */ + EnterCriticalSection(&ainput->lock); + callback = ainput->listener_callback->channel_callback; + WINPR_ASSERT(callback); + WINPR_ASSERT(callback->channel); + WINPR_ASSERT(callback->channel->Write); + rc = callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), Stream_Buffer(s), + NULL); + LeaveCriticalSection(&ainput->lock); + return rc; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback) +{ + AINPUT_CHANNEL_CALLBACK* callback = (AINPUT_CHANNEL_CALLBACK*)pChannelCallback; + + if (callback) + { + AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)callback->plugin; + WINPR_ASSERT(ainput); + + /* Lock here to ensure that no ainput_send_input_event is in progress. */ + EnterCriticalSection(&ainput->lock); + free(callback); + LeaveCriticalSection(&ainput->lock); + } + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_on_new_channel_connection(IWTSListenerCallback* pListenerCallback, + IWTSVirtualChannel* pChannel, BYTE* Data, + BOOL* pbAccept, + IWTSVirtualChannelCallback** ppCallback) +{ + AINPUT_CHANNEL_CALLBACK* callback; + AINPUT_LISTENER_CALLBACK* listener_callback = (AINPUT_LISTENER_CALLBACK*)pListenerCallback; + + WINPR_ASSERT(listener_callback); + WINPR_UNUSED(Data); + WINPR_UNUSED(pbAccept); + + callback = (AINPUT_CHANNEL_CALLBACK*)calloc(1, sizeof(AINPUT_CHANNEL_CALLBACK)); + + if (!callback) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + callback->iface.OnDataReceived = ainput_on_data_received; + callback->iface.OnClose = ainput_on_close; + callback->plugin = listener_callback->plugin; + callback->channel_mgr = listener_callback->channel_mgr; + callback->channel = pChannel; + listener_callback->channel_callback = callback; + + *ppCallback = &callback->iface; + + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr) +{ + UINT status; + AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin; + + WINPR_ASSERT(ainput); + + if (ainput->initialized) + { + WLog_ERR(TAG, "[%s] channel initialized twice, aborting", AINPUT_DVC_CHANNEL_NAME); + return ERROR_INVALID_DATA; + } + ainput->listener_callback = + (AINPUT_LISTENER_CALLBACK*)calloc(1, sizeof(AINPUT_LISTENER_CALLBACK)); + + if (!ainput->listener_callback) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + ainput->listener_callback->iface.OnNewChannelConnection = ainput_on_new_channel_connection; + ainput->listener_callback->plugin = pPlugin; + ainput->listener_callback->channel_mgr = pChannelMgr; + + status = pChannelMgr->CreateListener(pChannelMgr, AINPUT_DVC_CHANNEL_NAME, 0, + &ainput->listener_callback->iface, &ainput->listener); + + ainput->listener->pInterface = ainput->iface.pInterface; + ainput->initialized = status == CHANNEL_RC_OK; + return status; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_plugin_terminated(IWTSPlugin* pPlugin) +{ + AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pPlugin; + if (ainput && ainput->listener_callback) + { + IWTSVirtualChannelManager* mgr = ainput->listener_callback->channel_mgr; + if (mgr) + IFCALL(mgr->DestroyListener, mgr, ainput->listener); + } + if (ainput) + { + DeleteCriticalSection(&ainput->lock); + free(ainput->listener_callback); + free(ainput->iface.pInterface); + } + free(ainput); + + return CHANNEL_RC_OK; +} + +#ifdef BUILTIN_CHANNELS +#define DVCPluginEntry ainput_DVCPluginEntry +#else +#define DVCPluginEntry FREERDP_API DVCPluginEntry +#endif + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) +{ + UINT status = CHANNEL_RC_OK; + AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, "ainput"); + + if (!ainput) + { + AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext)); + ainput = (AINPUT_PLUGIN*)calloc(1, sizeof(AINPUT_PLUGIN)); + + if (!ainput || !context) + { + free(context); + free(ainput); + + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + ainput->iface.Initialize = ainput_plugin_initialize; + ainput->iface.Terminated = ainput_plugin_terminated; + + InitializeCriticalSection(&ainput->lock); + + context->handle = (void*)ainput; + context->AInputSendInputEvent = ainput_send_input_event; + ainput->iface.pInterface = (void*)context; + + status = pEntryPoints->RegisterPlugin(pEntryPoints, AINPUT_CHANNEL_NAME, &ainput->iface); + } + + return status; +} diff --git a/channels/ainput/client/ainput_main.h b/channels/ainput/client/ainput_main.h new file mode 100644 index 000000000000..8a19ad91b7aa --- /dev/null +++ b/channels/ainput/client/ainput_main.h @@ -0,0 +1,43 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Advanced Input Virtual Channel Extension + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H +#define FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#define DVC_TAG CHANNELS_TAG("ainput.client") +#ifdef WITH_DEBUG_DVC +#define DEBUG_DVC(...) WLog_DBG(DVC_TAG, __VA_ARGS__) +#else +#define DEBUG_DVC(...) \ + do \ + { \ + } while (0) +#endif + +#endif /* FREERDP_CHANNEL_AINPUT_CLIENT_MAIN_H */ diff --git a/channels/ainput/common/ainput_common.h b/channels/ainput/common/ainput_common.h new file mode 100644 index 000000000000..34442f7d9561 --- /dev/null +++ b/channels/ainput/common/ainput_common.h @@ -0,0 +1,59 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Audio Input Redirection Virtual Channel + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_INT_AINPUT_COMMON_H +#define FREERDP_INT_AINPUT_COMMON_H + +#include + +#include + +static INLINE const char* ainput_flags_to_string(UINT64 flags, char* buffer, size_t size) +{ + char number[32] = { 0 }; + + if (flags & AINPUT_FLAGS_HAVE_REL) + winpr_str_append("AINPUT_FLAGS_HAVE_REL", buffer, size, "|"); + if (flags & AINPUT_FLAGS_WHEEL) + winpr_str_append("AINPUT_FLAGS_WHEEL", buffer, size, "|"); + if (flags & AINPUT_FLAGS_MOVE) + winpr_str_append("AINPUT_FLAGS_MOVE", buffer, size, "|"); + if (flags & AINPUT_FLAGS_DOWN) + winpr_str_append("AINPUT_FLAGS_DOWN", buffer, size, "|"); + if (flags & AINPUT_FLAGS_REL) + winpr_str_append("AINPUT_FLAGS_REL", buffer, size, "|"); + if (flags & AINPUT_FLAGS_BUTTON1) + winpr_str_append("AINPUT_FLAGS_BUTTON1", buffer, size, "|"); + if (flags & AINPUT_FLAGS_BUTTON2) + winpr_str_append("AINPUT_FLAGS_BUTTON2", buffer, size, "|"); + if (flags & AINPUT_FLAGS_BUTTON3) + winpr_str_append("AINPUT_FLAGS_BUTTON3", buffer, size, "|"); + if (flags & AINPUT_XFLAGS_BUTTON1) + winpr_str_append("AINPUT_XFLAGS_BUTTON1", buffer, size, "|"); + if (flags & AINPUT_XFLAGS_BUTTON2) + winpr_str_append("AINPUT_XFLAGS_BUTTON2", buffer, size, "|"); + + _snprintf(number, sizeof(number), "[0x%08" PRIx64 "]", flags); + winpr_str_append(number, buffer, size, " "); + + return buffer; +} + +#endif /* FREERDP_INT_AINPUT_COMMON_H */ diff --git a/channels/ainput/server/CMakeLists.txt b/channels/ainput/server/CMakeLists.txt new file mode 100644 index 000000000000..59be263a707b --- /dev/null +++ b/channels/ainput/server/CMakeLists.txt @@ -0,0 +1,27 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Armin Novak +# Copyright 2022 Thincast Technologies GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_server("ainput") + +set(${MODULE_PREFIX}_SRCS + ainput_main.c) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +target_link_libraries(${MODULE_NAME} freerdp) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/ainput/server/ainput_main.c b/channels/ainput/server/ainput_main.c new file mode 100644 index 000000000000..fc61f9b00101 --- /dev/null +++ b/channels/ainput/server/ainput_main.c @@ -0,0 +1,587 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Advanced Input Virtual Channel Extension + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../common/ainput_common.h" + +#define TAG CHANNELS_TAG("ainput.server") + +typedef enum +{ + AINPUT_INITIAL, + AINPUT_OPENED, + AINPUT_VERSION_SENT, +} eAInputChannelState; + +typedef struct +{ + ainput_server_context context; + + BOOL opened; + + HANDLE stopEvent; + + HANDLE thread; + void* ainput_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eAInputChannelState state; + + wStream* buffer; +} ainput_server; + +static UINT ainput_server_context_poll(ainput_server_context* context); +static BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle); +static UINT ainput_server_context_poll_int(ainput_server_context* context); + +static BOOL ainput_server_is_open(ainput_server_context* context) +{ + ainput_server* ainput = (ainput_server*)context; + + WINPR_ASSERT(ainput); + return ainput->isOpened; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_server_open_channel(ainput_server* ainput) +{ + DWORD Error; + HANDLE hEvent; + DWORD StartTick; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + + WINPR_ASSERT(ainput); + + if (WTSQuerySessionInformationA(ainput->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + ainput->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + hEvent = WTSVirtualChannelManagerGetEventHandle(ainput->context.vcm); + StartTick = GetTickCount(); + + while (ainput->ainput_channel == NULL) + { + if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED) + { + Error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error); + return Error; + } + + ainput->ainput_channel = WTSVirtualChannelOpenEx(ainput->SessionId, AINPUT_DVC_CHANNEL_NAME, + WTS_CHANNEL_OPTION_DYNAMIC); + + Error = GetLastError(); + + if (Error == ERROR_NOT_FOUND) + { + WLog_DBG(TAG, "Channel %s not found", AINPUT_DVC_CHANNEL_NAME); + break; + } + + if (ainput->ainput_channel) + { + UINT32 channelId; + BOOL status = TRUE; + + channelId = WTSChannelGetIdByHandle(ainput->ainput_channel); + + IFCALLRET(ainput->context.ChannelIdAssigned, status, &ainput->context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + break; + } + + if (GetTickCount() - StartTick > 5000) + { + WLog_WARN(TAG, "Timeout opening channel %s", AINPUT_DVC_CHANNEL_NAME); + break; + } + } + + return ainput->ainput_channel ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; +} + +static UINT ainput_server_send_version(ainput_server* ainput) +{ + ULONG written; + wStream* s; + + WINPR_ASSERT(ainput); + + s = ainput->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + if (!Stream_EnsureCapacity(s, 10)) + { + WLog_WARN(TAG, "[%s] out of memory", AINPUT_DVC_CHANNEL_NAME); + return ERROR_OUTOFMEMORY; + } + + Stream_Write_UINT16(s, MSG_AINPUT_VERSION); + Stream_Write_UINT32(s, AINPUT_VERSION_MAJOR); /* Version (4 bytes) */ + Stream_Write_UINT32(s, AINPUT_VERSION_MINOR); /* Version (4 bytes) */ + + WINPR_ASSERT(Stream_GetPosition(s) <= ULONG_MAX); + if (!WTSVirtualChannelWrite(ainput->ainput_channel, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + return ERROR_INTERNAL_ERROR; + } + + return CHANNEL_RC_OK; +} + +static UINT ainput_server_recv_mouse_event(ainput_server* ainput, wStream* s) +{ + UINT error = CHANNEL_RC_OK; + UINT64 flags, time; + INT32 x, y; + char buffer[128] = { 0 }; + + WINPR_ASSERT(ainput); + WINPR_ASSERT(s); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 24)) + return ERROR_NO_DATA; + + Stream_Read_UINT64(s, time); + Stream_Read_UINT64(s, flags); + Stream_Read_INT32(s, x); + Stream_Read_INT32(s, y); + + WLog_VRB(TAG, "[%s] received: time=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, + __FUNCTION__, time, ainput_flags_to_string(flags, buffer, sizeof(buffer)), x, y); + IFCALLRET(ainput->context.MouseEvent, error, &ainput->context, time, flags, x, y); + + return error; +} + +static HANDLE ainput_server_get_channel_handle(ainput_server* ainput) +{ + BYTE* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(ainput); + + if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualEventHandle, (void**)&buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI ainput_server_thread_func(LPVOID arg) +{ + DWORD nCount; + HANDLE events[2] = { 0 }; + ainput_server* ainput = (ainput_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status; + + WINPR_ASSERT(ainput); + + nCount = 0; + events[nCount++] = ainput->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (ainput->state) + { + case AINPUT_OPENED: + events[1] = ainput_server_get_channel_handle(ainput); + nCount = 2; + status = WaitForMultipleObjects(nCount, events, FALSE, 100); + switch (status) + { + case WAIT_TIMEOUT: + case WAIT_OBJECT_0 + 1: + case WAIT_OBJECT_0: + error = ainput_server_context_poll_int(&ainput->context); + break; + + case WAIT_FAILED: + default: + WLog_WARN(TAG, "[%s] Wait for open failed", AINPUT_DVC_CHANNEL_NAME); + error = ERROR_INTERNAL_ERROR; + break; + } + break; + case AINPUT_VERSION_SENT: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_TIMEOUT: + case WAIT_OBJECT_0 + 1: + case WAIT_OBJECT_0: + error = ainput_server_context_poll_int(&ainput->context); + break; + case WAIT_FAILED: + default: + WLog_WARN(TAG, "[%s] Wait for version failed", AINPUT_DVC_CHANNEL_NAME); + error = ERROR_INTERNAL_ERROR; + break; + } + break; + default: + error = ainput_server_context_poll_int(&ainput->context); + break; + } + } + + WTSVirtualChannelClose(ainput->ainput_channel); + ainput->ainput_channel = NULL; + + if (error && ainput->context.rdpcontext) + setChannelError(ainput->context.rdpcontext, error, + "ainput_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_server_open(ainput_server_context* context) +{ + ainput_server* ainput = (ainput_server*)context; + + WINPR_ASSERT(ainput); + + if (!ainput->externalThread && (ainput->thread == NULL)) + { + ainput->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!ainput->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + ainput->thread = CreateThread(NULL, 0, ainput_server_thread_func, ainput, 0, NULL); + if (!ainput->thread) + { + WLog_ERR(TAG, "CreateEvent failed!"); + CloseHandle(ainput->stopEvent); + ainput->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + ainput->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT ainput_server_close(ainput_server_context* context) +{ + UINT error = CHANNEL_RC_OK; + ainput_server* ainput = (ainput_server*)context; + + WINPR_ASSERT(ainput); + + if (!ainput->externalThread && ainput->thread) + { + SetEvent(ainput->stopEvent); + + if (WaitForSingleObject(ainput->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(ainput->thread); + CloseHandle(ainput->stopEvent); + ainput->thread = NULL; + ainput->stopEvent = NULL; + } + if (ainput->externalThread) + { + if (ainput->state != AINPUT_INITIAL) + { + WTSVirtualChannelClose(ainput->ainput_channel); + ainput->ainput_channel = NULL; + ainput->state = AINPUT_INITIAL; + } + } + ainput->isOpened = FALSE; + + return error; +} + +static UINT ainput_server_initialize(ainput_server_context* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + ainput_server* ainput = (ainput_server*)context; + + WINPR_ASSERT(ainput); + + if (ainput->isOpened) + { + WLog_WARN(TAG, "Application error: AINPUT channel already initialized, calling in this " + "state is not possible!"); + return ERROR_INVALID_STATE; + } + ainput->externalThread = externalThread; + return error; +} + +ainput_server_context* ainput_server_context_new(HANDLE vcm) +{ + ainput_server* ainput = (ainput_server*)calloc(1, sizeof(ainput_server)); + + if (!ainput) + return NULL; + + ainput->context.vcm = vcm; + ainput->context.Open = ainput_server_open; + ainput->context.IsOpen = ainput_server_is_open; + ainput->context.Close = ainput_server_close; + ainput->context.Initialize = ainput_server_initialize; + ainput->context.Poll = ainput_server_context_poll; + ainput->context.ChannelHandle = ainput_server_context_handle; + + ainput->buffer = Stream_New(NULL, 4096); + if (!ainput->buffer) + goto fail; + return &ainput->context; +fail: + ainput_server_context_free(&ainput->context); + return NULL; +} + +void ainput_server_context_free(ainput_server_context* context) +{ + ainput_server* ainput = (ainput_server*)context; + if (ainput) + { + ainput_server_close(context); + Stream_Free(ainput->buffer, TRUE); + } + free(ainput); +} + +static UINT ainput_process_message(ainput_server* ainput) +{ + BOOL rc; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned, ActualBytesReturned; + UINT16 MessageId; + wStream* s; + + WINPR_ASSERT(ainput); + WINPR_ASSERT(ainput->ainput_channel); + + s = ainput->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(ainput->ainput_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 2) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(ainput->ainput_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &ActualBytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + if (BytesReturned != ActualBytesReturned) + { + WLog_ERR(TAG, "WTSVirtualChannelRead size mismatch %" PRId32 ", expected %" PRId32, + ActualBytesReturned, BytesReturned); + goto out; + } + + Stream_SetLength(s, ActualBytesReturned); + Stream_Read_UINT16(s, MessageId); + + switch (MessageId) + { + case MSG_AINPUT_MOUSE: + error = ainput_server_recv_mouse_event(ainput, s); + break; + + default: + WLog_ERR(TAG, "audin_server_thread_func: unknown MessageId %" PRIu8 "", MessageId); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +BOOL ainput_server_context_handle(ainput_server_context* context, HANDLE* handle) +{ + ainput_server* ainput = (ainput_server*)context; + WINPR_ASSERT(ainput); + WINPR_ASSERT(handle); + + if (!ainput->externalThread) + { + WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME); + return FALSE; + } + if (ainput->state == AINPUT_INITIAL) + { + WLog_WARN(TAG, "[%s] state fail!", AINPUT_DVC_CHANNEL_NAME); + return FALSE; + } + *handle = ainput_server_get_channel_handle(ainput); + return TRUE; +} + +UINT ainput_server_context_poll_int(ainput_server_context* context) +{ + ainput_server* ainput = (ainput_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(ainput); + + switch (ainput->state) + { + case AINPUT_INITIAL: + error = ainput_server_open_channel(ainput); + if (error) + WLog_ERR(TAG, "ainput_server_open_channel failed with error %" PRIu32 "!", error); + else + ainput->state = AINPUT_OPENED; + break; + case AINPUT_OPENED: + { + BYTE* buffer = NULL; + DWORD BytesReturned = 0; + + if (WTSVirtualChannelQuery(ainput->ainput_channel, WTSVirtualChannelReady, + (void**)&buffer, &BytesReturned) != TRUE) + { + WLog_ERR(TAG, "WTSVirtualChannelReady failed,"); + } + else + { + if (*buffer != 0) + { + error = ainput_server_send_version(ainput); + if (error) + WLog_ERR(TAG, "audin_server_send_version failed with error %" PRIu32 "!", + error); + else + ainput->state = AINPUT_VERSION_SENT; + } + else + error = CHANNEL_RC_OK; + } + WTSFreeMemory(buffer); + } + break; + case AINPUT_VERSION_SENT: + error = ainput_process_message(ainput); + break; + + default: + WLog_ERR(TAG, "AINPUT chanel is in invalid state %d", ainput->state); + break; + } + + return error; +} + +UINT ainput_server_context_poll(ainput_server_context* context) +{ + ainput_server* ainput = (ainput_server*)context; + + WINPR_ASSERT(ainput); + if (!ainput->externalThread) + { + WLog_WARN(TAG, "[%s] externalThread fail!", AINPUT_DVC_CHANNEL_NAME); + return ERROR_INTERNAL_ERROR; + } + return ainput_server_context_poll_int(context); +} diff --git a/channels/audin/client/alsa/audin_alsa.c b/channels/audin/client/alsa/audin_alsa.c index 60e9fd8d4f34..53d4e9bc03c2 100644 --- a/channels/audin/client/alsa/audin_alsa.c +++ b/channels/audin/client/alsa/audin_alsa.c @@ -242,10 +242,6 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device, const AUDIO_FORMAT break; - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - return TRUE; - default: return FALSE; } diff --git a/channels/audin/client/audin_main.c b/channels/audin/client/audin_main.c index a5b941679dbf..23561b153a83 100644 --- a/channels/audin/client/audin_main.c +++ b/channels/audin/client/audin_main.c @@ -43,13 +43,18 @@ #include "audin_main.h" -#define MSG_SNDIN_VERSION 0x01 -#define MSG_SNDIN_FORMATS 0x02 -#define MSG_SNDIN_OPEN 0x03 -#define MSG_SNDIN_OPEN_REPLY 0x04 -#define MSG_SNDIN_DATA_INCOMING 0x05 -#define MSG_SNDIN_DATA 0x06 -#define MSG_SNDIN_FORMATCHANGE 0x07 +#define SNDIN_VERSION 0x02 + +enum +{ + MSG_SNDIN_VERSION = 0x01, + MSG_SNDIN_FORMATS = 0x02, + MSG_SNDIN_OPEN = 0x03, + MSG_SNDIN_OPEN_REPLY = 0x04, + MSG_SNDIN_DATA_INCOMING = 0x05, + MSG_SNDIN_DATA = 0x06, + MSG_SNDIN_FORMATCHANGE = 0x07 +} MSG_SNDIN_CMD; typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK; struct _AUDIN_LISTENER_CALLBACK @@ -105,6 +110,7 @@ struct _AUDIN_PLUGIN IWTSListener* listener; BOOL initialized; + UINT32 version; }; static BOOL audin_process_addin_args(AUDIN_PLUGIN* audin, ADDIN_ARGV* args); @@ -138,7 +144,7 @@ static UINT audin_channel_write_and_free(AUDIN_CHANNEL_CALLBACK* callback, wStre static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callback, wStream* s) { wStream* out; - const UINT32 ClientVersion = 0x01; + const UINT32 ClientVersion = SNDIN_VERSION; UINT32 ServerVersion; if (Stream_GetRemainingLength(s) < 4) @@ -149,7 +155,7 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c ServerVersion, ClientVersion); /* Do not answer server packet, we do not support the channel version. */ - if (ServerVersion != ClientVersion) + if (ServerVersion > ClientVersion) { WLog_Print(audin->log, WLOG_WARN, "Incompatible channel version server=%" PRIu32 @@ -157,6 +163,7 @@ static UINT audin_process_version(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* c ServerVersion, ClientVersion); return CHANNEL_RC_OK; } + audin->version = ServerVersion; out = Stream_New(NULL, 5); @@ -444,11 +451,8 @@ static BOOL audin_open_device(AUDIN_PLUGIN* audin, AUDIN_CHANNEL_CALLBACK* callb return FALSE; } - if (!supported) - { - if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format)) - return FALSE; - } + if (!freerdp_dsp_context_reset(audin->dsp_context, audin->format)) + return FALSE; IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback); diff --git a/channels/audin/client/mac/audin_mac.m b/channels/audin/client/mac/audin_mac.m index ecf3c8cc0011..b81e551d53c6 100644 --- a/channels/audin/client/mac/audin_mac.m +++ b/channels/audin/client/mac/audin_mac.m @@ -154,12 +154,16 @@ static UINT audin_mac_set_format(IAudinDevice *device, const AUDIO_FORMAT *forma if (format->wBitsPerSample == 0) mac->audioFormat.mBitsPerChannel = 16; - mac->audioFormat.mBytesPerFrame = 0; - mac->audioFormat.mBytesPerPacket = 0; mac->audioFormat.mChannelsPerFrame = mac->format.nChannels; + mac->audioFormat.mFramesPerPacket = 1; + + mac->audioFormat.mBytesPerFrame = + mac->audioFormat.mChannelsPerFrame * (mac->audioFormat.mBitsPerChannel / 8); + mac->audioFormat.mBytesPerPacket = + mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket; + mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format); mac->audioFormat.mFormatID = audin_mac_get_format(format); - mac->audioFormat.mFramesPerPacket = 1; mac->audioFormat.mReserved = 0; mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec; return CHANNEL_RC_OK; @@ -423,32 +427,34 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn #if defined(MAC_OS_X_VERSION_10_14) if (@available(macOS 10.14, *)) { - AVAuthorizationStatus status = - [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; - switch (status) - { - case AVAuthorizationStatusAuthorized: - mac->isAuthorized = TRUE; - break; - case AVAuthorizationStatusNotDetermined: - [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio - completionHandler:^(BOOL granted) { - if (granted == YES) - { - mac->isAuthorized = TRUE; - } - else - WLog_WARN(TAG, "Microphone access denied by user"); - }]; - break; - case AVAuthorizationStatusRestricted: - WLog_WARN(TAG, "Microphone access restricted by policy"); - break; - case AVAuthorizationStatusDenied: - WLog_WARN(TAG, "Microphone access denied by policy"); - break; - default: - break; + @autoreleasepool { + AVAuthorizationStatus status = + [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; + switch (status) + { + case AVAuthorizationStatusAuthorized: + mac->isAuthorized = TRUE; + break; + case AVAuthorizationStatusNotDetermined: + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio + completionHandler:^(BOOL granted) { + if (granted == YES) + { + mac->isAuthorized = TRUE; + } + else + WLog_WARN(TAG, "Microphone access denied by user"); + }]; + break; + case AVAuthorizationStatusRestricted: + WLog_WARN(TAG, "Microphone access restricted by policy"); + break; + case AVAuthorizationStatusDenied: + WLog_WARN(TAG, "Microphone access denied by policy"); + break; + default: + break; + } } } #endif diff --git a/channels/audin/client/opensles/audin_opensl_es.c b/channels/audin/client/opensles/audin_opensl_es.c index 4e3efdeb2f97..a3b7c3cedcc8 100644 --- a/channels/audin/client/opensles/audin_opensl_es.c +++ b/channels/audin/client/opensles/audin_opensl_es.c @@ -215,7 +215,7 @@ static UINT audin_opensles_open(IAudinDevice* device, AudinReceive receive, void opensles->user_data = user_data; return CHANNEL_RC_OK; error_out: - audin_opensles_close(opensles); + audin_opensles_close(device); return ERROR_INTERNAL_ERROR; } @@ -248,7 +248,7 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, ADDIN_A { UINT status; DWORD flags; - COMMAND_LINE_ARGUMENT_A* arg; + const COMMAND_LINE_ARGUMENT_A* arg; AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device; COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, diff --git a/channels/audin/client/oss/audin_oss.c b/channels/audin/client/oss/audin_oss.c index 97eb3496ed83..305686c75c04 100644 --- a/channels/audin/client/oss/audin_oss.c +++ b/channels/audin/client/oss/audin_oss.c @@ -113,10 +113,6 @@ static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT* break; - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - return TRUE; - default: return FALSE; } diff --git a/channels/audin/client/pulse/audin_pulse.c b/channels/audin/client/pulse/audin_pulse.c index 8a5c3beb6dcb..fb8fdb711213 100644 --- a/channels/audin/client/pulse/audin_pulse.c +++ b/channels/audin/client/pulse/audin_pulse.c @@ -236,17 +236,6 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, const AUDIO_FORMA break; - case WAVE_FORMAT_ALAW: /* A-LAW */ - case WAVE_FORMAT_MULAW: /* U-LAW */ - if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) && - (format->wBitsPerSample == 8) && - (format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX)) - { - return TRUE; - } - - break; - default: return FALSE; } diff --git a/channels/audin/client/winmm/audin_winmm.c b/channels/audin/client/winmm/audin_winmm.c index 92a778504ee0..753867284717 100644 --- a/channels/audin/client/winmm/audin_winmm.c +++ b/channels/audin/client/winmm/audin_winmm.c @@ -345,7 +345,9 @@ static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* for if (ppwfx->nBlockAlign != 2) { ppwfx->nBlockAlign = 2; + ppwfx->nAvgBytesPerSec = ppwfx->nSamplesPerSec * ppwfx->nBlockAlign; } + if (!test_format_supported(ppwfx)) return ERROR_INVALID_PARAMETER; winmm->pwfx_cur = ppwfx; diff --git a/channels/audin/server/audin.c b/channels/audin/server/audin.c index e5c51bbf221d..8252236f8d8e 100644 --- a/channels/audin/server/audin.c +++ b/channels/audin/server/audin.c @@ -162,9 +162,6 @@ static UINT audin_server_send_formats(audin_server* audin, wStream* s) for (i = 0; i < audin->context.num_server_formats; i++) { AUDIO_FORMAT format = audin->context.server_formats[i]; - // TODO: Eliminate this - format.nAvgBytesPerSec = - format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8; if (!audio_format_write(s, &format)) { @@ -560,6 +557,8 @@ static BOOL audin_server_open(audin_server_context* context) PULONG pSessionId = NULL; DWORD BytesReturned = 0; audin->SessionId = WTS_CURRENT_SESSION; + UINT32 channelId; + BOOL status = TRUE; if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId, &BytesReturned)) @@ -577,6 +576,15 @@ static BOOL audin_server_open(audin_server_context* context) return FALSE; } + channelId = WTSChannelGetIdByHandle(audin->audin_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { WLog_ERR(TAG, "CreateEvent failed!"); diff --git a/channels/client/addin.c b/channels/client/addin.c index cd6f90e991c3..db32ff83cc28 100644 --- a/channels/client/addin.c +++ b/channels/client/addin.c @@ -231,10 +231,8 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS do { - char* p[5]; - FREERDP_ADDIN* pAddin; - nDashes = 0; - pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN)); + BOOL used = FALSE; + FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN)); if (!pAddin) { @@ -242,57 +240,116 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS goto error_out; } + nDashes = 0; for (index = 0; FindData.cFileName[index]; index++) nDashes += (FindData.cFileName[index] == '-') ? 1 : 0; if (nDashes == 1) { + size_t len; + char* p[2] = { 0 }; /* -client. */ p[0] = FindData.cFileName; p[1] = strchr(p[0], '-') + 1; - strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1); + + len = p[1] - p[0]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1)); + pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_NAME; ppAddins[nAddins++] = pAddin; + + used = TRUE; } else if (nDashes == 2) { + size_t len; + char* p[4] = { 0 }; /* -client-. */ p[0] = FindData.cFileName; p[1] = strchr(p[0], '-') + 1; p[2] = strchr(p[1], '-') + 1; p[3] = strchr(p[2], '.') + 1; - strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1); - strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1); + + len = p[1] - p[0]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1)); + + len = p[3] - p[2]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1)); + pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_NAME; pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM; ppAddins[nAddins++] = pAddin; + + used = TRUE; } else if (nDashes == 3) { + size_t len; + char* p[5] = { 0 }; /* -client--. */ p[0] = FindData.cFileName; p[1] = strchr(p[0], '-') + 1; p[2] = strchr(p[1], '-') + 1; p[3] = strchr(p[2], '-') + 1; p[4] = strchr(p[3], '.') + 1; - strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1); - strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1); - strncpy(pAddin->cType, p[3], (p[4] - p[3]) - 1); + + len = p[1] - p[0]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1)); + + len = p[3] - p[2]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1)); + + len = p[4] - p[3]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1)); + pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_NAME; pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM; pAddin->dwFlags |= FREERDP_ADDIN_TYPE; ppAddins[nAddins++] = pAddin; + + used = TRUE; } - else - { + + skip: + if (!used) free(pAddin); - } + } while (FindNextFileA(hFind, &FindData)); FindClose(hFind); diff --git a/channels/cliprdr/client/cliprdr_format.c b/channels/cliprdr/client/cliprdr_format.c index 4c31a1b0810f..0b6111b963f8 100644 --- a/channels/cliprdr/client/cliprdr_format.c +++ b/channels/cliprdr/client/cliprdr_format.c @@ -173,198 +173,3 @@ UINT cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, wStream* s, UI return error; } - -static UINT64 filetime_to_uint64(FILETIME value) -{ - UINT64 converted = 0; - converted |= (UINT32)value.dwHighDateTime; - converted <<= 32; - converted |= (UINT32)value.dwLowDateTime; - return converted; -} - -static FILETIME uint64_to_filetime(UINT64 value) -{ - FILETIME converted; - converted.dwLowDateTime = (UINT32)(value >> 0); - converted.dwHighDateTime = (UINT32)(value >> 32); - return converted; -} - -#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520) - -/** - * Parse a packed file list. - * - * The resulting array must be freed with the `free()` function. - * - * @param [in] format_data packed `CLIPRDR_FILELIST` to parse. - * @param [in] format_data_length length of `format_data` in bytes. - * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs. - * @param [out] file_descriptor_count number of elements in `file_descriptor_array`. - * - * @returns 0 on success, otherwise a Win32 error code. - */ -UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, - FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count) -{ - UINT result = NO_ERROR; - UINT32 i; - UINT32 count = 0; - wStream* s = NULL; - - if (!format_data || !file_descriptor_array || !file_descriptor_count) - return ERROR_BAD_ARGUMENTS; - - s = Stream_New((BYTE*)format_data, format_data_length); - if (!s) - return ERROR_NOT_ENOUGH_MEMORY; - - if (Stream_GetRemainingLength(s) < 4) - { - WLog_ERR(TAG, "invalid packed file list"); - - result = ERROR_INCORRECT_SIZE; - goto out; - } - - Stream_Read_UINT32(s, count); /* cItems (4 bytes) */ - - if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count) - { - WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz, - ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s)); - - result = ERROR_INCORRECT_SIZE; - goto out; - } - - *file_descriptor_count = count; - *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW)); - if (!*file_descriptor_array) - { - result = ERROR_NOT_ENOUGH_MEMORY; - goto out; - } - - for (i = 0; i < count; i++) - { - int c; - UINT64 lastWriteTime; - FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]); - - Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */ - Stream_Seek(s, 32); /* reserved1 (32 bytes) */ - Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ - Stream_Seek(s, 16); /* reserved2 (16 bytes) */ - Stream_Read_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */ - file->ftLastWriteTime = uint64_to_filetime(lastWriteTime); - Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ - Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ - for (c = 0; c < 260; c++) /* cFileName (520 bytes) */ - Stream_Read_UINT16(s, file->cFileName[c]); - } - - if (Stream_GetRemainingLength(s) > 0) - WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes", - Stream_GetRemainingLength(s)); -out: - Stream_Free(s, FALSE); - - return result; -} - -#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024) - -/** - * Serialize a packed file list. - * - * The resulting format data must be freed with the `free()` function. - * - * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize. - * @param [in] file_descriptor_count number of elements in `file_descriptor_array`. - * @param [out] format_data serialized CLIPRDR_FILELIST. - * @param [out] format_data_length length of `format_data` in bytes. - * - * @returns 0 on success, otherwise a Win32 error code. - */ -UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, BYTE** format_data, - UINT32* format_data_length) -{ - return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array, - file_descriptor_count, format_data, format_data_length); -} - -UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, BYTE** format_data, - UINT32* format_data_length) -{ - UINT result = NO_ERROR; - UINT32 i; - wStream* s = NULL; - - if (!file_descriptor_array || !format_data || !format_data_length) - return ERROR_BAD_ARGUMENTS; - - if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0) - { - WLog_WARN(TAG, "No file clipboard support annouonced!"); - return ERROR_BAD_ARGUMENTS; - } - - s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE); - if (!s) - return ERROR_NOT_ENOUGH_MEMORY; - - Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */ - - for (i = 0; i < file_descriptor_count; i++) - { - int c; - UINT64 lastWriteTime; - const FILEDESCRIPTORW* file = &file_descriptor_array[i]; - - /* - * There is a known issue with Windows server getting stuck in - * an infinite loop when downloading files that are larger than - * 2 gigabytes. Do not allow clients to send such file lists. - * - * https://support.microsoft.com/en-us/help/2258090 - */ - if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0) - { - if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE)) - { - WLog_ERR(TAG, "cliprdr does not support files over 2 GB"); - result = ERROR_FILE_TOO_LARGE; - goto error; - } - } - - Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */ - Stream_Zero(s, 32); /* reserved1 (32 bytes) */ - Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ - Stream_Zero(s, 16); /* reserved2 (16 bytes) */ - lastWriteTime = filetime_to_uint64(file->ftLastWriteTime); - Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */ - Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ - Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ - for (c = 0; c < 260; c++) /* cFileName (520 bytes) */ - Stream_Write_UINT16(s, file->cFileName[c]); - } - - Stream_SealLength(s); - - Stream_GetBuffer(s, *format_data); - Stream_GetLength(s, *format_data_length); - - Stream_Free(s, FALSE); - - return result; - -error: - Stream_Free(s, TRUE); - - return result; -} diff --git a/channels/cliprdr/cliprdr_common.c b/channels/cliprdr/cliprdr_common.c index 69157adaaca2..8472fdeb197b 100644 --- a/channels/cliprdr/cliprdr_common.c +++ b/channels/cliprdr/cliprdr_common.c @@ -138,7 +138,7 @@ cliprdr_packet_unlock_clipdata_new(const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockCl if (!unlockClipboardData) return NULL; - s = cliprdr_packet_new(CB_LOCK_CLIPDATA, 0, 4); + s = cliprdr_packet_new(CB_UNLOCK_CLIPDATA, 0, 4); if (!s) return NULL; diff --git a/channels/disp/server/disp_main.c b/channels/disp/server/disp_main.c index 5340165b937b..9caec45e7e41 100644 --- a/channels/disp/server/disp_main.c +++ b/channels/disp/server/disp_main.c @@ -58,7 +58,7 @@ static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length) } header.type = type; - header.length = length; + header.length = DISPLAY_CONTROL_HEADER_LENGTH + length; if ((error = disp_write_header(s, &header))) { @@ -72,6 +72,24 @@ static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length) return NULL; } +static void disp_server_sanitize_monitor_layout(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor) +{ + if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH || + monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH || + monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT || + monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT) + { + if (monitor->PhysicalWidth != 0 || monitor->PhysicalHeight != 0) + WLog_DBG( + TAG, + "Sanitizing invalid physical monitor size. Old physical monitor size: [%" PRIu32 + ", %" PRIu32 "]", + monitor->PhysicalWidth, monitor->PhysicalHeight); + + monitor->PhysicalWidth = monitor->PhysicalHeight = 0; + } +} + static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor) { if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH || @@ -88,22 +106,6 @@ static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT* return FALSE; } - if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH || - monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH) - { - WLog_WARN(TAG, "Received invalid value for monitor->PhysicalWidth: %" PRIu32 "", - monitor->PhysicalWidth); - return FALSE; - } - - if (monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT || - monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT) - { - WLog_WARN(TAG, "Received invalid value for monitor->Height: %" PRIu32 "", - monitor->PhysicalHeight); - return FALSE; - } - switch (monitor->Orientation) { case ORIENTATION_LANDSCAPE: @@ -183,6 +185,8 @@ static UINT disp_recv_display_control_monitor_layout_pdu(wStream* s, DispServerC Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */ Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */ Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */ + + disp_server_sanitize_monitor_layout(monitor); WLog_DBG(TAG, "\t%d : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32 ") W/H=%" PRIu32 "x%" PRIu32 ")", @@ -374,6 +378,8 @@ static UINT disp_server_open(DispServerContext* context) void* buffer; buffer = NULL; priv->SessionId = WTS_CURRENT_SESSION; + UINT32 channelId; + BOOL status = TRUE; if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId, &BytesReturned) == FALSE) @@ -395,6 +401,16 @@ static UINT disp_server_open(DispServerContext* context) goto out_close; } + channelId = WTSChannelGetIdByHandle(priv->disp_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + rc = ERROR_INTERNAL_ERROR; + goto out_close; + } + /* Query for channel event handle */ if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) || diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c index 3054385933ca..1ea4ab9dabf0 100644 --- a/channels/drive/client/drive_file.c +++ b/channels/drive/client/drive_file.c @@ -61,10 +61,14 @@ } while (0) #endif -static void drive_file_fix_path(WCHAR* path) +static BOOL drive_file_fix_path(WCHAR* path, size_t length) { size_t i; - size_t length = _wcslen(path); + + if ((length == 0) || (length > UINT32_MAX)) + return FALSE; + + WINPR_ASSERT(path); for (i = 0; i < length; i++) { @@ -75,58 +79,82 @@ static void drive_file_fix_path(WCHAR* path) #ifdef WIN32 if ((length == 3) && (path[1] == L':') && (path[2] == L'/')) - return; + return FALSE; #else if ((length == 1) && (path[0] == L'/')) - return; + return FALSE; #endif if ((length > 0) && (path[length - 1] == L'/')) path[length - 1] = L'\0'; + + return TRUE; } static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path, - size_t PathLength) + size_t PathWCharLength) { - WCHAR* fullpath; - size_t base_path_length; + BOOL ok = FALSE; + WCHAR* fullpath = NULL; + size_t length; - if (!base_path || (!path && (PathLength > 0))) - return NULL; + if (!base_path || (!path && (PathWCharLength > 0))) + goto fail; - base_path_length = _wcslen(base_path) * 2; - fullpath = (WCHAR*)calloc(1, base_path_length + PathLength + sizeof(WCHAR)); + const size_t base_path_length = _wcsnlen(base_path, MAX_PATH); + length = base_path_length + PathWCharLength + 1; + fullpath = (WCHAR*)calloc(length, sizeof(WCHAR)); if (!fullpath) + goto fail; + + CopyMemory(fullpath, base_path, base_path_length * sizeof(WCHAR)); + if (path) + CopyMemory(&fullpath[base_path_length], path, PathWCharLength * sizeof(WCHAR)); + + if (!drive_file_fix_path(fullpath, length)) + goto fail; + + /* Ensure the path does not contain sequences like '..' */ + const WCHAR dotdot[] = { '.', '.', '\0' }; + if (_wcsstr(&fullpath[base_path_length], dotdot)) { - WLog_ERR(TAG, "malloc failed!"); - return NULL; + char abuffer[MAX_PATH] = { 0 }; + ConvertFromUnicode(CP_UTF8, 0, &fullpath[base_path_length], -1, (char**)&abuffer, + ARRAYSIZE(abuffer) - 1, NULL, NULL); + + WLog_WARN(TAG, "[rdpdr] received invalid file path '%s' from server, aborting!", + &abuffer[base_path_length]); + goto fail; } - CopyMemory(fullpath, base_path, base_path_length); - if (path) - CopyMemory((char*)fullpath + base_path_length, path, PathLength); - drive_file_fix_path(fullpath); + ok = TRUE; +fail: + if (!ok) + { + free(fullpath); + fullpath = NULL; + } return fullpath; } static BOOL drive_file_remove_dir(const WCHAR* path) { - WIN32_FIND_DATAW findFileData; + WIN32_FIND_DATAW findFileData = { 0 }; BOOL ret = TRUE; - HANDLE dir; - WCHAR* fullpath; - WCHAR* path_slash; - size_t base_path_length; + HANDLE dir = INVALID_HANDLE_VALUE; + WCHAR* fullpath = NULL; + WCHAR* path_slash = NULL; + size_t base_path_length = 0; if (!path) return FALSE; - base_path_length = _wcslen(path) * 2; - path_slash = (WCHAR*)calloc(1, base_path_length + sizeof(WCHAR) * 3); + base_path_length = _wcslen(path); + path_slash = (WCHAR*)calloc(base_path_length + 3, sizeof(WCHAR)); if (!path_slash) { @@ -134,12 +162,11 @@ static BOOL drive_file_remove_dir(const WCHAR* path) return FALSE; } - CopyMemory(path_slash, path, base_path_length); - path_slash[base_path_length / 2] = L'/'; - path_slash[base_path_length / 2 + 1] = L'*'; + CopyMemory(path_slash, path, base_path_length * sizeof(WCHAR)); + path_slash[base_path_length] = L'/'; + path_slash[base_path_length + 1] = L'*'; DEBUG_WSTR("Search in %s", path_slash); dir = FindFirstFileW(path_slash, &findFileData); - path_slash[base_path_length / 2 + 1] = 0; if (dir == INVALID_HANDLE_VALUE) { @@ -149,7 +176,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path) do { - size_t len = _wcslen(findFileData.cFileName); + const size_t len = _wcsnlen(findFileData.cFileName, ARRAYSIZE(findFileData.cFileName)); if ((len == 1 && findFileData.cFileName[0] == L'.') || (len == 2 && findFileData.cFileName[0] == L'.' && findFileData.cFileName[1] == L'.')) @@ -157,7 +184,7 @@ static BOOL drive_file_remove_dir(const WCHAR* path) continue; } - fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len * 2); + fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len); DEBUG_WSTR("Delete %s", fullpath); if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) @@ -333,13 +360,13 @@ static BOOL drive_file_init(DRIVE_FILE* file) return file->file_handle != INVALID_HANDLE_VALUE; } -DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id, - UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions, - UINT32 FileAttributes, UINT32 SharedAccess) +DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength, + UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition, + UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess) { DRIVE_FILE* file; - if (!base_path || (!path && (PathLength > 0))) + if (!base_path || (!path && (PathWCharLength > 0))) return NULL; file = (DRIVE_FILE*)calloc(1, sizeof(DRIVE_FILE)); @@ -359,7 +386,7 @@ DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 Pat file->CreateDisposition = CreateDisposition; file->CreateOptions = CreateOptions; file->SharedAccess = SharedAccess; - drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathLength)); + drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathWCharLength)); if (!drive_file_init(file)) { @@ -714,13 +741,10 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN return FALSE; fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input), - FileNameLength); + FileNameLength / sizeof(WCHAR)); if (!fullpath) - { - WLog_ERR(TAG, "drive_file_combine_fullpath failed!"); return FALSE; - } #ifdef _WIN32 @@ -759,7 +783,7 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN } BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery, - const WCHAR* path, UINT32 PathLength, wStream* output) + const WCHAR* path, UINT32 PathWCharLength, wStream* output) { size_t length; WCHAR* ent_path; @@ -773,7 +797,7 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT if (file->find_handle != INVALID_HANDLE_VALUE) FindClose(file->find_handle); - ent_path = drive_file_combine_fullpath(file->basepath, path, PathLength); + ent_path = drive_file_combine_fullpath(file->basepath, path, PathWCharLength); /* open new search handle and retrieve the first entry */ file->find_handle = FindFirstFileW(ent_path, &file->find_data); free(ent_path); diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h index ed789d6f09be..6d3bd7045cd2 100644 --- a/channels/drive/client/drive_file.h +++ b/channels/drive/client/drive_file.h @@ -51,9 +51,9 @@ struct _DRIVE_FILE UINT32 CreateOptions; }; -DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id, - UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions, - UINT32 FileAttributes, UINT32 SharedAccess); +DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathWCharLength, + UINT32 id, UINT32 DesiredAccess, UINT32 CreateDisposition, + UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess); BOOL drive_file_free(DRIVE_FILE* file); BOOL drive_file_open(DRIVE_FILE* file); @@ -64,6 +64,6 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, wStream* input); BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery, - const WCHAR* path, UINT32 PathLength, wStream* output); + const WCHAR* path, UINT32 PathWCharLength, wStream* output); #endif /* FREERDP_CHANNEL_DRIVE_FILE_H */ diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c index 1b5422522587..b6cf2ad32bc8 100644 --- a/channels/drive/client/drive_main.c +++ b/channels/drive/client/drive_main.c @@ -184,8 +184,8 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp) path = (const WCHAR*)Stream_Pointer(irp->input); FileId = irp->devman->id_sequence++; - file = drive_file_new(drive->path, path, PathLength, FileId, DesiredAccess, CreateDisposition, - CreateOptions, FileAttributes, SharedAccess); + file = drive_file_new(drive->path, path, PathLength / sizeof(WCHAR), FileId, DesiredAccess, + CreateDisposition, CreateOptions, FileAttributes, SharedAccess); if (!file) { @@ -629,6 +629,9 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp) Stream_Read_UINT32(irp->input, PathLength); Stream_Seek(irp->input, 23); /* Padding */ path = (WCHAR*)Stream_Pointer(irp->input); + if (!Stream_CheckAndLogRequiredLength(TAG, irp->input, PathLength)) + return ERROR_INVALID_DATA; + file = drive_get_file_by_id(drive, irp->FileId); if (file == NULL) @@ -636,8 +639,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp) irp->IoStatus = STATUS_UNSUCCESSFUL; Stream_Write_UINT32(irp->output, 0); /* Length */ } - else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, PathLength, - irp->output)) + else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, + PathLength / sizeof(WCHAR), irp->output)) { irp->IoStatus = drive_map_windows_err(GetLastError()); } diff --git a/channels/echo/server/echo_main.c b/channels/echo/server/echo_main.c index 55e2fa237c66..1da2894d3213 100644 --- a/channels/echo/server/echo_main.c +++ b/channels/echo/server/echo_main.c @@ -91,7 +91,21 @@ static UINT echo_server_open_channel(echo_server* echo) WTSVirtualChannelOpenEx(echo->SessionId, "ECHO", WTS_CHANNEL_OPTION_DYNAMIC); if (echo->echo_channel) + { + UINT32 channelId; + BOOL status = TRUE; + + channelId = WTSChannelGetIdByHandle(echo->echo_channel); + + IFCALLRET(echo->context.ChannelIdAssigned, status, &echo->context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + break; + } Error = GetLastError(); diff --git a/channels/gfxredir/CMakeLists.txt b/channels/gfxredir/CMakeLists.txt new file mode 100644 index 000000000000..bfea7571cf16 --- /dev/null +++ b/channels/gfxredir/CMakeLists.txt @@ -0,0 +1,23 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2020 Microsoft +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(WITH_CHANNEL_GFXREDIR) + define_channel("gfxredir") + if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) + endif() +endif() diff --git a/channels/gfxredir/ChannelOptions.cmake b/channels/gfxredir/ChannelOptions.cmake new file mode 100644 index 000000000000..62cdceba6306 --- /dev/null +++ b/channels/gfxredir/ChannelOptions.cmake @@ -0,0 +1,12 @@ +if(WITH_CHANNEL_GFXREDIR) + set(OPTION_DEFAULT OFF) + set(OPTION_CLIENT_DEFAULT OFF) + set(OPTION_SERVER_DEFAULT ON) + + define_channel_options(NAME "gfxredir" TYPE "dynamic" + DESCRIPTION "Graphics Redirection Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPXXXX]" + DEFAULT ${OPTION_DEFAULT}) + + define_channel_server_options(${OPTION_SERVER_DEFAULT}) +endif() diff --git a/channels/gfxredir/gfxredir_common.c b/channels/gfxredir/gfxredir_common.c new file mode 100644 index 000000000000..b8a298f14421 --- /dev/null +++ b/channels/gfxredir/gfxredir_common.c @@ -0,0 +1,60 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#define TAG CHANNELS_TAG("gfxredir.common") + +#include "gfxredir_common.h" + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT gfxredir_read_header(wStream* s, GFXREDIR_HEADER* header) +{ + if (Stream_GetRemainingLength(s) < 8) + { + WLog_ERR(TAG, "header parsing failed: not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, header->cmdId); + Stream_Read_UINT32(s, header->length); + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT gfxredir_write_header(wStream* s, const GFXREDIR_HEADER* header) +{ + Stream_Write_UINT32(s, header->cmdId); + Stream_Write_UINT32(s, header->length); + return CHANNEL_RC_OK; +} diff --git a/channels/gfxredir/gfxredir_common.h b/channels/gfxredir/gfxredir_common.h new file mode 100644 index 000000000000..18710a9ab2bd --- /dev/null +++ b/channels/gfxredir/gfxredir_common.h @@ -0,0 +1,32 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_GFXREDIR_COMMON_H +#define FREERDP_CHANNEL_GFXREDIR_COMMON_H + +#include +#include + +#include +#include + +FREERDP_LOCAL UINT gfxredir_read_header(wStream* s, GFXREDIR_HEADER* header); +FREERDP_LOCAL UINT gfxredir_write_header(wStream* s, const GFXREDIR_HEADER* header); + +#endif /* FREERDP_CHANNEL_GFXREDIR_COMMON_H */ diff --git a/channels/gfxredir/server/CMakeLists.txt b/channels/gfxredir/server/CMakeLists.txt new file mode 100644 index 000000000000..41cf225e6eb5 --- /dev/null +++ b/channels/gfxredir/server/CMakeLists.txt @@ -0,0 +1,32 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2020 Microsoft +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_server("gfxredir") + +set(${MODULE_PREFIX}_SRCS + gfxredir_main.c + gfxredir_main.h + ../gfxredir_common.c + ../gfxredir_common.h + ) + +include_directories(..) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +target_link_libraries(${MODULE_NAME} freerdp) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/gfxredir/server/gfxredir_main.c b/channels/gfxredir/server/gfxredir_main.c new file mode 100644 index 000000000000..2379b1d98de2 --- /dev/null +++ b/channels/gfxredir/server/gfxredir_main.c @@ -0,0 +1,793 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gfxredir_main.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../gfxredir_common.h" + +#define TAG CHANNELS_TAG("gfxredir.server") + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_recv_legacy_caps_pdu(wStream* s, GfxRedirServerContext* context) +{ + UINT32 error = CHANNEL_RC_OK; + GFXREDIR_LEGACY_CAPS_PDU pdu; + + if (Stream_GetRemainingLength(s) < 2) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT16(s, pdu.version); /* version (2 bytes) */ + + if (context) + IFCALLRET(context->GraphicsRedirectionLegacyCaps, error, context, &pdu); + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_recv_caps_advertise_pdu(wStream* s, UINT32 length, GfxRedirServerContext* context) +{ + UINT32 error = CHANNEL_RC_OK; + GFXREDIR_CAPS_ADVERTISE_PDU pdu; + + if (Stream_GetRemainingLength(s) < length) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + pdu.length = length; + Stream_GetPointer(s, pdu.caps); + Stream_Seek(s, length); + + if (!context->GraphicsRedirectionCapsAdvertise) + { + WLog_ERR(TAG, "server does not support CapsAdvertise PDU!"); + return ERROR_NOT_SUPPORTED; + } + else if (context) + { + IFCALLRET(context->GraphicsRedirectionCapsAdvertise, error, context, &pdu); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_recv_present_buffer_ack_pdu(wStream* s, GfxRedirServerContext* context) +{ + UINT32 error = CHANNEL_RC_OK; + GFXREDIR_PRESENT_BUFFER_ACK_PDU pdu; + + if (Stream_GetRemainingLength(s) < 16) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */ + Stream_Read_UINT64(s, pdu.presentId); /* presentId (8 bytes) */ + + if (context) + { + IFCALLRET(context->PresentBufferAck, error, context, &pdu); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_receive_pdu(GfxRedirServerContext* context, wStream* s) +{ + UINT error = CHANNEL_RC_OK; + size_t beg, end; + GFXREDIR_HEADER header; + beg = Stream_GetPosition(s); + + if ((error = gfxredir_read_header(s, &header))) + { + WLog_ERR(TAG, "gfxredir_read_header failed with error %" PRIu32 "!", error); + return error; + } + + switch (header.cmdId) + { + case GFXREDIR_CMDID_LEGACY_CAPS: + if ((error = gfxredir_recv_legacy_caps_pdu(s, context))) + WLog_ERR(TAG, + "gfxredir_recv_legacy_caps_pdu " + "failed with error %" PRIu32 "!", + error); + + break; + + case GFXREDIR_CMDID_CAPS_ADVERTISE: + if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context))) + WLog_ERR(TAG, + "gfxredir_recv_caps_advertise " + "failed with error %" PRIu32 "!", + error); + break; + + case GFXREDIR_CMDID_PRESENT_BUFFER_ACK: + if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context))) + WLog_ERR(TAG, + "gfxredir_recv_present_buffer_ask_pdu " + "failed with error %" PRIu32 "!", + error); + break; + + default: + error = CHANNEL_RC_BAD_PROC; + WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.cmdId); + break; + } + + end = Stream_GetPosition(s); + + if (end != (beg + header.length)) + { + WLog_ERR(TAG, "Unexpected GFXREDIR pdu end: Actual: %d, Expected: %" PRIu32 "", end, + (beg + header.length)); + Stream_SetPosition(s, (beg + header.length)); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context) +{ + DWORD BytesReturned; + void* buffer; + UINT ret = CHANNEL_RC_OK; + GfxRedirServerPrivate* priv = context->priv; + wStream* s = priv->input_stream; + + /* Check whether the dynamic channel is ready */ + if (!priv->isReady) + { + if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer, + &BytesReturned) == FALSE) + { + if (GetLastError() == ERROR_NO_DATA) + return ERROR_NO_DATA; + + WLog_ERR(TAG, "WTSVirtualChannelQuery failed"); + return ERROR_INTERNAL_ERROR; + } + + priv->isReady = *((BOOL*)buffer); + WTSFreeMemory(buffer); + } + + /* Consume channel event only after the dynamic channel is ready */ + if (priv->isReady) + { + Stream_SetPosition(s, 0); + + if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0, NULL, 0, &BytesReturned)) + { + if (GetLastError() == ERROR_NO_DATA) + return ERROR_NO_DATA; + + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + return ERROR_INTERNAL_ERROR; + } + + if (BytesReturned < 1) + return CHANNEL_RC_OK; + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s), Stream_Capacity(s), + &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + return ERROR_INTERNAL_ERROR; + } + + Stream_SetLength(s, BytesReturned); + Stream_SetPosition(s, 0); + + while (Stream_GetPosition(s) < Stream_Length(s)) + { + if ((ret = gfxredir_server_receive_pdu(context, s))) + { + WLog_ERR(TAG, + "gfxredir_server_receive_pdu " + "failed with error %" PRIu32 "!", + ret); + return ret; + } + } + } + + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg) +{ + GfxRedirServerContext* context = (GfxRedirServerContext*)arg; + GfxRedirServerPrivate* priv = context->priv; + DWORD status; + DWORD nCount; + HANDLE events[8]; + UINT error = CHANNEL_RC_OK; + nCount = 0; + events[nCount++] = priv->stopEvent; + events[nCount++] = priv->channelEvent; + + while (TRUE) + { + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + if (status == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error); + break; + } + + /* Stop Event */ + if (status == WAIT_OBJECT_0) + break; + + if ((error = gfxredir_server_handle_messages(context))) + { + WLog_ERR(TAG, "gfxredir_server_handle_messages failed with error %" PRIu32 "", error); + break; + } + } + + ExitThread(error); + return error; +} + +/** + * Function description + * Create new stream for single gfxredir packet. The new stream length + * would be required data length + header. The header will be written + * to the stream before return. + * + * @param cmdId + * @param length - data length without header + * + * @return new stream + */ +static wStream* gfxredir_server_single_packet_new(UINT32 cmdId, UINT32 length) +{ + UINT error; + GFXREDIR_HEADER header; + wStream* s = Stream_New(NULL, GFXREDIR_HEADER_SIZE + length); + + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto error; + } + + header.cmdId = cmdId; + header.length = GFXREDIR_HEADER_SIZE + length; + + if ((error = gfxredir_write_header(s, &header))) + { + WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error); + goto error; + } + + return s; +error: + Stream_Free(s, TRUE); + return NULL; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_packet_send(GfxRedirServerContext* context, wStream* s) +{ + UINT ret; + ULONG written; + + if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + ret = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written, + Stream_GetPosition(s)); + } + + ret = CHANNEL_RC_OK; +out: + Stream_Free(s, TRUE); + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_error(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU *error) +{ + wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, error->errorCode); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context, const GFXREDIR_CAPS_CONFIRM_PDU *graphicsCapsConfirm) +{ + UINT ret; + wStream* s; + + if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE) + { + WLog_ERR(TAG, "length must be greater than header size, failed!"); + return ERROR_INVALID_DATA; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE); + Stream_Write_UINT32(s, graphicsCapsConfirm->version); + Stream_Write_UINT32(s, graphicsCapsConfirm->length); + if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE) + Stream_Write(s, graphicsCapsConfirm->capsData, graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE); + ret = gfxredir_server_packet_send(context, s); + if (ret == CHANNEL_RC_OK) + context->confirmedCapsVersion = graphicsCapsConfirm->version; + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_open_pool(GfxRedirServerContext* context, const GFXREDIR_OPEN_POOL_PDU *openPool) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "open_pool is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + if (openPool->sectionNameLength == 0 || openPool->sectionName == NULL) { + WLog_ERR(TAG, "section name must be provided!"); + return ERROR_INVALID_DATA; + } + + /* make sure null-terminate */ + if (openPool->sectionName[openPool->sectionNameLength-1] != 0) { + WLog_ERR(TAG, "section name must be terminated with NULL!"); + return ERROR_INVALID_DATA; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL, 20 + (openPool->sectionNameLength * 2)); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, openPool->poolId); + Stream_Write_UINT64(s, openPool->poolSize); + Stream_Write_UINT32(s, openPool->sectionNameLength); + Stream_Write(s, openPool->sectionName, (openPool->sectionNameLength * 2)); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_close_pool(GfxRedirServerContext* context, const GFXREDIR_CLOSE_POOL_PDU *closePool) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "close_pool is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, closePool->poolId); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context, const GFXREDIR_CREATE_BUFFER_PDU *createBuffer) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "create_buffer is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, createBuffer->poolId); + Stream_Write_UINT64(s, createBuffer->bufferId); + Stream_Write_UINT64(s, createBuffer->offset); + Stream_Write_UINT32(s, createBuffer->stride); + Stream_Write_UINT32(s, createBuffer->width); + Stream_Write_UINT32(s, createBuffer->height); + Stream_Write_UINT32(s, createBuffer->format); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context, const GFXREDIR_DESTROY_BUFFER_PDU *destroyBuffer) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "destroy_buffer is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, destroyBuffer->bufferId); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context, const GFXREDIR_PRESENT_BUFFER_PDU *presentBuffer) +{ + UINT32 length; + wStream* s; + RECTANGLE_32 dummyRect = {0, 0, 0, 0}; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "present_buffer is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS) + { + WLog_ERR(TAG, "numOpaqueRects is larger than its limit!"); + return ERROR_INVALID_DATA; + } + + length = 64 + + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) * sizeof(RECTANGLE_32)); + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, presentBuffer->timestamp); + Stream_Write_UINT64(s, presentBuffer->presentId); + Stream_Write_UINT64(s, presentBuffer->windowId); + Stream_Write_UINT64(s, presentBuffer->bufferId); + Stream_Write_UINT32(s, presentBuffer->orientation); + Stream_Write_UINT32(s, presentBuffer->targetWidth); + Stream_Write_UINT32(s, presentBuffer->targetHeight); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.left); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.top); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.width); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.height); + Stream_Write_UINT32(s, presentBuffer->numOpaqueRects); + if (presentBuffer->numOpaqueRects) + Stream_Write(s, presentBuffer->opaqueRects, (presentBuffer->numOpaqueRects * sizeof(RECTANGLE_32))); + else + Stream_Write(s, &dummyRect, sizeof(RECTANGLE_32)); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_open(GfxRedirServerContext* context) +{ + UINT rc = ERROR_INTERNAL_ERROR; + GfxRedirServerPrivate* priv = context->priv; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + void* buffer; + buffer = NULL; + priv->SessionId = WTS_CURRENT_SESSION; + + if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + rc = ERROR_INTERNAL_ERROR; + goto out_close; + } + + priv->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + priv->gfxredir_channel = (HANDLE)WTSVirtualChannelOpenEx(priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME, + WTS_CHANNEL_OPTION_DYNAMIC); + + if (!priv->gfxredir_channel) + { + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!"); + rc = GetLastError(); + goto out_close; + } + + /* Query for channel event handle */ + if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) || + (BytesReturned != sizeof(HANDLE))) + { + WLog_ERR(TAG, + "WTSVirtualChannelQuery failed " + "or invalid returned size(%" PRIu32 ")", + BytesReturned); + + if (buffer) + WTSFreeMemory(buffer); + + rc = ERROR_INTERNAL_ERROR; + goto out_close; + } + + CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE)); + WTSFreeMemory(buffer); + + if (priv->thread == NULL) + { + if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + WLog_ERR(TAG, "CreateEvent failed!"); + rc = ERROR_INTERNAL_ERROR; + } + + if (!(priv->thread = + CreateThread(NULL, 0, gfxredir_server_thread_func, (void*)context, 0, NULL))) + { + WLog_ERR(TAG, "CreateEvent failed!"); + CloseHandle(priv->stopEvent); + priv->stopEvent = NULL; + rc = ERROR_INTERNAL_ERROR; + } + } + + return CHANNEL_RC_OK; +out_close: + WTSVirtualChannelClose(priv->gfxredir_channel); + priv->gfxredir_channel = NULL; + priv->channelEvent = NULL; + return rc; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_close(GfxRedirServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + GfxRedirServerPrivate* priv = context->priv; + + if (priv->thread) + { + SetEvent(priv->stopEvent); + + if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(priv->thread); + CloseHandle(priv->stopEvent); + priv->thread = NULL; + priv->stopEvent = NULL; + } + + if (priv->gfxredir_channel) + { + WTSVirtualChannelClose(priv->gfxredir_channel); + priv->gfxredir_channel = NULL; + } + + return error; +} + +GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm) +{ + GfxRedirServerContext* context; + GfxRedirServerPrivate* priv; + context = (GfxRedirServerContext*)calloc(1, sizeof(GfxRedirServerContext)); + + if (!context) + { + WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerContext failed!"); + return NULL; + } + + priv = context->priv = (GfxRedirServerPrivate*)calloc(1, sizeof(GfxRedirServerPrivate)); + + if (!context->priv) + { + WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!"); + goto out_free; + } + + priv->input_stream = Stream_New(NULL, 4); + + if (!priv->input_stream) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto out_free_priv; + } + + context->vcm = vcm; + context->Open = gfxredir_server_open; + context->Close = gfxredir_server_close; + context->Error = gfxredir_send_error; + context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm; + context->OpenPool = gfxredir_send_open_pool; + context->ClosePool = gfxredir_send_close_pool; + context->CreateBuffer = gfxredir_send_create_buffer; + context->DestroyBuffer = gfxredir_send_destroy_buffer; + context->PresentBuffer = gfxredir_send_present_buffer; + context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1; + priv->isReady = FALSE; + return context; +out_free_priv: + free(context->priv); +out_free: + free(context); + return NULL; +} + +void gfxredir_server_context_free(GfxRedirServerContext* context) +{ + if (!context) + return; + + gfxredir_server_close(context); + + if (context->priv) + { + Stream_Free(context->priv->input_stream, TRUE); + free(context->priv); + } + + free(context); +} diff --git a/channels/gfxredir/server/gfxredir_main.h b/channels/gfxredir/server/gfxredir_main.h new file mode 100644 index 000000000000..e44e16a56b4e --- /dev/null +++ b/channels/gfxredir/server/gfxredir_main.h @@ -0,0 +1,37 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_GFXREDIR_SERVER_MAIN_H +#define FREERDP_CHANNEL_GFXREDIR_SERVER_MAIN_H + +#include + +struct _gfxredir_server_private +{ + BOOL isReady; + wStream* input_stream; + HANDLE channelEvent; + HANDLE thread; + HANDLE stopEvent; + DWORD SessionId; + + void* gfxredir_channel; +}; + +#endif /* FREERDP_CHANNEL_GFXREDIR_SERVER_MAIN_H */ diff --git a/channels/parallel/client/parallel_main.c b/channels/parallel/client/parallel_main.c index af3e82703a60..993605a65e23 100644 --- a/channels/parallel/client/parallel_main.c +++ b/channels/parallel/client/parallel_main.c @@ -159,7 +159,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp) return ERROR_INVALID_DATA; Stream_Read_UINT32(irp->input, Length); Stream_Read_UINT64(irp->input, Offset); - buffer = (BYTE*)malloc(Length); + buffer = (BYTE*)calloc(Length, sizeof(BYTE)); if (!buffer) { @@ -178,6 +178,7 @@ static UINT parallel_process_irp_read(PARALLEL_DEVICE* parallel, IRP* irp) } else { + Length = status; } Stream_Write_UINT32(irp->output, Length); diff --git a/channels/printer/client/cups/printer_cups.c b/channels/printer/client/cups/printer_cups.c index baa46e5d2e17..ed0eb90e948d 100644 --- a/channels/printer/client/cups/printer_cups.c +++ b/channels/printer/client/cups/printer_cups.c @@ -403,8 +403,8 @@ FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void) uniq_cups_driver->driver.ReleaseRef = printer_cups_release_ref_driver; uniq_cups_driver->id_sequence = 1; - uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver); } + uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver); return &uniq_cups_driver->driver; } diff --git a/channels/printer/client/win/printer_win.c b/channels/printer/client/win/printer_win.c index 86b5e66d9240..81c7055529cc 100644 --- a/channels/printer/client/win/printer_win.c +++ b/channels/printer/client/win/printer_win.c @@ -452,8 +452,8 @@ FREERDP_API rdpPrinterDriver* freerdp_printer_client_subsystem_entry(void) win_driver->driver.ReleaseRef = printer_win_release_ref_driver; win_driver->id_sequence = 1; - win_driver->driver.AddRef(&win_driver->driver); } + win_driver->driver.AddRef(&win_driver->driver); return &win_driver->driver; } diff --git a/channels/rail/client/rail_orders.c b/channels/rail/client/rail_orders.c index c394a5925bc7..4070b0fe29db 100644 --- a/channels/rail/client/rail_orders.c +++ b/channels/rail/client/rail_orders.c @@ -32,6 +32,8 @@ #include "rail_orders.h" +static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask); + /** * Function description * @@ -39,6 +41,7 @@ */ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType) { + char buffer[128] = { 0 }; UINT16 orderLength; if (!rail || !s) @@ -49,7 +52,7 @@ UINT rail_send_pdu(railPlugin* rail, wStream* s, UINT16 orderType) rail_write_pdu_header(s, orderType, orderLength); Stream_SetPosition(s, orderLength); WLog_Print(rail->log, WLOG_DEBUG, "Sending %s PDU, length: %" PRIu16 "", - rail_get_order_type_string(orderType), orderLength); + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); return rail_send_channel_data(rail, s); } @@ -388,7 +391,46 @@ static UINT rail_recv_handshake_order(railPlugin* rail, wStream* s) return error; } -static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask) +static UINT rail_read_compartment_info_order(wStream* s, + RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo) +{ + if (!Stream_CheckAndLogRequiredLength(TAG, s, RAIL_COMPARTMENT_INFO_ORDER_LENGTH)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, compartmentInfo->ImeState); /* ImeState (4 bytes) */ + Stream_Read_UINT32(s, compartmentInfo->ImeConvMode); /* ImeConvMode (4 bytes) */ + Stream_Read_UINT32(s, compartmentInfo->ImeSentenceMode); /* ImeSentenceMode (4 bytes) */ + Stream_Read_UINT32(s, compartmentInfo->KanaMode); /* KANAMode (4 bytes) */ + return CHANNEL_RC_OK; +} + +static UINT rail_recv_compartmentinfo_order(railPlugin* rail, wStream* s) +{ + RailClientContext* context = rail_get_client_interface(rail); + RAIL_COMPARTMENT_INFO_ORDER pdu = { 0 }; + UINT error; + + if (!context || !s) + return ERROR_INVALID_PARAMETER; + + if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED)) + return ERROR_BAD_CONFIGURATION; + + if ((error = rail_read_compartment_info_order(s, &pdu))) + return error; + + if (context->custom) + { + IFCALLRET(context->ClientCompartmentInfo, error, context, &pdu); + + if (error) + WLog_ERR(TAG, "context.ClientCompartmentInfo failed with error %" PRIu32 "", error); + } + + return error; +} + +BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureMask) { UINT32 supported, masked; @@ -400,7 +442,15 @@ static BOOL rail_is_feature_supported(const rdpContext* context, UINT32 featureM masked = (supported & featureMask); if (masked != featureMask) + { + char mask[256] = { 0 }; + char actual[256] = { 0 }; + + WLog_WARN(TAG, "[%s] have %s, require %s", __func__, + freerdp_rail_support_flags_to_string(supported, actual, sizeof(actual)), + freerdp_rail_support_flags_to_string(featureMask, mask, sizeof(mask))); return FALSE; + } return TRUE; } @@ -904,6 +954,73 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai return error; } +static UINT rail_read_textscaleinfo_order(wStream* s, UINT32* pTextScaleFactor) +{ + WINPR_ASSERT(pTextScaleFactor); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, *pTextScaleFactor); + return CHANNEL_RC_OK; +} + +static UINT rail_recv_textscaleinfo_order(railPlugin* rail, wStream* s) +{ + RailClientContext* context = rail_get_client_interface(rail); + UINT32 TextScaleFactor = 0; + UINT error; + + if (!context) + return ERROR_INVALID_PARAMETER; + + if ((error = rail_read_textscaleinfo_order(s, &TextScaleFactor))) + return error; + + if (context->custom) + { + IFCALLRET(context->ClientTextScale, error, context, TextScaleFactor); + + if (error) + WLog_ERR(TAG, "context.ClientTextScale failed with error %" PRIu32 "", error); + } + + return error; +} + +static UINT rail_read_caretblinkinfo_order(wStream* s, UINT32* pCaretBlinkRate) +{ + WINPR_ASSERT(pCaretBlinkRate); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT32(s, *pCaretBlinkRate); + return CHANNEL_RC_OK; +} + +static UINT rail_recv_caretblinkinfo_order(railPlugin* rail, wStream* s) +{ + RailClientContext* context = rail_get_client_interface(rail); + UINT32 CaretBlinkRate = 0; + UINT error; + + if (!context) + return ERROR_INVALID_PARAMETER; + if ((error = rail_read_caretblinkinfo_order(s, &CaretBlinkRate))) + return error; + + if (context->custom) + { + IFCALLRET(context->ClientCaretBlinkRate, error, context, CaretBlinkRate); + + if (error) + WLog_ERR(TAG, "context.ClientCaretBlinkRate failed with error %" PRIu32 "", error); + } + + return error; +} + /** * Function description * @@ -911,9 +1028,10 @@ static UINT rail_recv_get_application_id_extended_response_order(railPlugin* rai */ UINT rail_order_recv(railPlugin* rail, wStream* s) { + char buffer[128] = { 0 }; UINT16 orderType; UINT16 orderLength; - UINT error; + UINT error = CHANNEL_RC_OK; if (!rail || !s) return ERROR_INVALID_PARAMETER; @@ -925,55 +1043,88 @@ UINT rail_order_recv(railPlugin* rail, wStream* s) } WLog_Print(rail->log, WLOG_DEBUG, "Received %s PDU, length:%" PRIu16 "", - rail_get_order_type_string(orderType), orderLength); + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); switch (orderType) { case TS_RAIL_ORDER_HANDSHAKE: - return rail_recv_handshake_order(rail, s); + error = rail_recv_handshake_order(rail, s); + break; + + case TS_RAIL_ORDER_COMPARTMENTINFO: + error = rail_recv_compartmentinfo_order(rail, s); + break; case TS_RAIL_ORDER_HANDSHAKE_EX: - return rail_recv_handshake_ex_order(rail, s); + error = rail_recv_handshake_ex_order(rail, s); + break; case TS_RAIL_ORDER_EXEC_RESULT: - return rail_recv_exec_result_order(rail, s); + error = rail_recv_exec_result_order(rail, s); + break; case TS_RAIL_ORDER_SYSPARAM: - return rail_recv_server_sysparam_order(rail, s); + error = rail_recv_server_sysparam_order(rail, s); + break; case TS_RAIL_ORDER_MINMAXINFO: - return rail_recv_server_minmaxinfo_order(rail, s); + error = rail_recv_server_minmaxinfo_order(rail, s); + break; case TS_RAIL_ORDER_LOCALMOVESIZE: - return rail_recv_server_localmovesize_order(rail, s); + error = rail_recv_server_localmovesize_order(rail, s); + break; case TS_RAIL_ORDER_GET_APPID_RESP: - return rail_recv_server_get_appid_resp_order(rail, s); + error = rail_recv_server_get_appid_resp_order(rail, s); + break; case TS_RAIL_ORDER_LANGBARINFO: - return rail_recv_langbar_info_order(rail, s); + error = rail_recv_langbar_info_order(rail, s); + break; case TS_RAIL_ORDER_TASKBARINFO: - return rail_recv_taskbar_info_order(rail, s); + error = rail_recv_taskbar_info_order(rail, s); + break; case TS_RAIL_ORDER_ZORDER_SYNC: - return rail_recv_zorder_sync_order(rail, s); + error = rail_recv_zorder_sync_order(rail, s); + break; case TS_RAIL_ORDER_CLOAK: - return rail_recv_cloak_order(rail, s); + error = rail_recv_cloak_order(rail, s); + break; case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST: - return rail_recv_power_display_request_order(rail, s); + error = rail_recv_power_display_request_order(rail, s); + break; case TS_RAIL_ORDER_GET_APPID_RESP_EX: - return rail_recv_get_application_id_extended_response_order(rail, s); + error = rail_recv_get_application_id_extended_response_order(rail, s); + break; + + case TS_RAIL_ORDER_TEXTSCALEINFO: + error = rail_recv_textscaleinfo_order(rail, s); + break; + + case TS_RAIL_ORDER_CARETBLINKINFO: + error = rail_recv_caretblinkinfo_order(rail, s); + break; default: - WLog_ERR(TAG, "Unknown RAIL PDU order reveived."); + WLog_ERR(TAG, "Unknown RAIL PDU %s received.", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer))); return ERROR_INVALID_DATA; } - return CHANNEL_RC_OK; + if (error != CHANNEL_RC_OK) + { + char buffer[128] = { 0 }; + WLog_Print(rail->log, WLOG_ERROR, "Failed to process rail %s PDU, length:%" PRIu16 "", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); + } + + return error; } /** @@ -1358,6 +1509,9 @@ UINT rail_send_client_compartment_info_order(railPlugin* rail, if (!rail || !compartmentInfo) return ERROR_INVALID_PARAMETER; + if (!rail_is_feature_supported(rail->rdpcontext, RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED)) + return ERROR_BAD_CONFIGURATION; + s = rail_pdu_init(RAIL_COMPARTMENT_INFO_ORDER_LENGTH); if (!s) diff --git a/channels/rail/rail_common.c b/channels/rail/rail_common.c index b6d69b799105..e77616d1b7f0 100644 --- a/channels/rail/rail_common.c +++ b/channels/rail/rail_common.c @@ -27,37 +27,72 @@ #define TAG CHANNELS_TAG("rail.common") -static const char* const RAIL_ORDER_TYPE_STRINGS[] = { "", - "Execute", - "Activate", - "System Parameters Update", - "System Command", - "Handshake", - "Notify Event", - "", - "Window Move", - "Local Move/Size", - "Min Max Info", - "Client Status", - "System Menu", - "Language Bar Info", - "Get Application ID Request", - "Get Application ID Response", - "Execute Result", - "", - "", - "", - "", - "", - "" }; - const char* rail_get_order_type_string(UINT16 orderType) { - UINT32 index = ((orderType & 0xF0) >> 3) + (orderType & 0x0F); - if (index >= ARRAYSIZE(RAIL_ORDER_TYPE_STRINGS)) - return "UNKNOWN"; + switch (orderType) + { + case TS_RAIL_ORDER_EXEC: + return "TS_RAIL_ORDER_EXEC"; + case TS_RAIL_ORDER_ACTIVATE: + return "TS_RAIL_ORDER_ACTIVATE"; + case TS_RAIL_ORDER_SYSPARAM: + return "TS_RAIL_ORDER_SYSPARAM"; + case TS_RAIL_ORDER_SYSCOMMAND: + return "TS_RAIL_ORDER_SYSCOMMAND"; + case TS_RAIL_ORDER_HANDSHAKE: + return "TS_RAIL_ORDER_HANDSHAKE"; + case TS_RAIL_ORDER_NOTIFY_EVENT: + return "TS_RAIL_ORDER_NOTIFY_EVENT"; + case TS_RAIL_ORDER_WINDOWMOVE: + return "TS_RAIL_ORDER_WINDOWMOVE"; + case TS_RAIL_ORDER_LOCALMOVESIZE: + return "TS_RAIL_ORDER_LOCALMOVESIZE"; + case TS_RAIL_ORDER_MINMAXINFO: + return "TS_RAIL_ORDER_MINMAXINFO"; + case TS_RAIL_ORDER_CLIENTSTATUS: + return "TS_RAIL_ORDER_CLIENTSTATUS"; + case TS_RAIL_ORDER_SYSMENU: + return "TS_RAIL_ORDER_SYSMENU"; + case TS_RAIL_ORDER_LANGBARINFO: + return "TS_RAIL_ORDER_LANGBARINFO"; + case TS_RAIL_ORDER_GET_APPID_REQ: + return "TS_RAIL_ORDER_GET_APPID_REQ"; + case TS_RAIL_ORDER_GET_APPID_RESP: + return "TS_RAIL_ORDER_GET_APPID_RESP"; + case TS_RAIL_ORDER_TASKBARINFO: + return "TS_RAIL_ORDER_TASKBARINFO"; + case TS_RAIL_ORDER_LANGUAGEIMEINFO: + return "TS_RAIL_ORDER_LANGUAGEIMEINFO"; + case TS_RAIL_ORDER_COMPARTMENTINFO: + return "TS_RAIL_ORDER_COMPARTMENTINFO"; + case TS_RAIL_ORDER_HANDSHAKE_EX: + return "TS_RAIL_ORDER_HANDSHAKE_EX"; + case TS_RAIL_ORDER_ZORDER_SYNC: + return "TS_RAIL_ORDER_ZORDER_SYNC"; + case TS_RAIL_ORDER_CLOAK: + return "TS_RAIL_ORDER_CLOAK"; + case TS_RAIL_ORDER_POWER_DISPLAY_REQUEST: + return "TS_RAIL_ORDER_POWER_DISPLAY_REQUEST"; + case TS_RAIL_ORDER_SNAP_ARRANGE: + return "TS_RAIL_ORDER_SNAP_ARRANGE"; + case TS_RAIL_ORDER_GET_APPID_RESP_EX: + return "TS_RAIL_ORDER_GET_APPID_RESP_EX"; + case TS_RAIL_ORDER_EXEC_RESULT: + return "TS_RAIL_ORDER_EXEC_RESULT"; + case TS_RAIL_ORDER_TEXTSCALEINFO: + return "TS_RAIL_ORDER_TEXTSCALEINFO"; + case TS_RAIL_ORDER_CARETBLINKINFO: + return "TS_RAIL_ORDER_CARETBLINKINFO"; + default: + return "TS_RAIL_ORDER_UNKNOWN"; + } +} - return RAIL_ORDER_TYPE_STRINGS[index]; +const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length) +{ + _snprintf(buffer, length, "%s[0x%04" PRIx16 "]", rail_get_order_type_string(orderType), + orderType); + return buffer; } /** diff --git a/channels/rail/rail_common.h b/channels/rail/rail_common.h index e13412df9257..34b6fa05d15a 100644 --- a/channels/rail/rail_common.h +++ b/channels/rail/rail_common.h @@ -71,5 +71,6 @@ UINT rail_write_sysparam_order(wStream* s, const RAIL_SYSPARAM_ORDER* sysparam, BOOL extendedSpiSupported); BOOL rail_is_extended_spi_supported(UINT32 channelsFlags); const char* rail_get_order_type_string(UINT16 orderType); +const char* rail_get_order_type_string_full(UINT16 orderType, char* buffer, size_t length); #endif /* FREERDP_CHANNEL_RAIL_COMMON_H */ diff --git a/channels/rail/server/rail_main.c b/channels/rail/server/rail_main.c index db389282c47f..4949fb775c0a 100644 --- a/channels/rail/server/rail_main.c +++ b/channels/rail/server/rail_main.c @@ -62,6 +62,7 @@ static UINT rail_send(RailServerContext* context, wStream* s, ULONG length) */ static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 orderType) { + char buffer[128] = { 0 }; UINT16 orderLength; if (!context || !s) @@ -71,8 +72,8 @@ static UINT rail_server_send_pdu(RailServerContext* context, wStream* s, UINT16 Stream_SetPosition(s, 0); rail_write_pdu_header(s, orderType, orderLength); Stream_SetPosition(s, orderLength); - WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "", rail_get_order_type_string(orderType), - orderLength); + WLog_DBG(TAG, "Sending %s PDU, length: %" PRIu16 "", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); return rail_send(context, s, orderLength); } @@ -1535,6 +1536,7 @@ void rail_server_set_handshake_ex_flags(RailServerContext* context, DWORD flags) UINT rail_server_handle_messages(RailServerContext* context) { + char buffer[128] = { 0 }; UINT status = CHANNEL_RC_OK; DWORD bytesReturned; UINT16 orderType; @@ -1584,8 +1586,8 @@ UINT rail_server_handle_messages(RailServerContext* context) return ERROR_INTERNAL_ERROR; } - WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "", rail_get_order_type_string(orderType), - orderLength); + WLog_DBG(TAG, "Received %s PDU, length:%" PRIu16 "", + rail_get_order_type_string_full(orderType, buffer, sizeof(buffer)), orderLength); switch (orderType) { diff --git a/channels/rdp2tcp/client/CMakeLists.txt b/channels/rdp2tcp/client/CMakeLists.txt index 2e510200efdf..6c9e2fcd3685 100644 --- a/channels/rdp2tcp/client/CMakeLists.txt +++ b/channels/rdp2tcp/client/CMakeLists.txt @@ -22,6 +22,6 @@ set(${MODULE_PREFIX}_SRCS add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx") -target_link_libraries(${MODULE_NAME} freerdp) +target_link_libraries(${MODULE_NAME} winpr freerdp) set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client") diff --git a/channels/rdpdr/client/devman.c b/channels/rdpdr/client/devman.c index af3cdd6b268d..841e928fec62 100644 --- a/channels/rdpdr/client/devman.c +++ b/channels/rdpdr/client/devman.c @@ -41,6 +41,8 @@ #include "devman.h" +#define TAG CHANNELS_TAG("rdpdr.client") + static void devman_device_free(void* obj) { DEVICE* device = (DEVICE*)obj; @@ -62,7 +64,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr) if (!devman) { - WLog_INFO(TAG, "calloc failed!"); + WLog_Print(rdpdr->log, WLOG_INFO, "calloc failed!"); return NULL; } @@ -72,7 +74,7 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr) if (!devman->devices) { - WLog_INFO(TAG, "ListDictionary_New failed!"); + WLog_Print(rdpdr->log, WLOG_INFO, "ListDictionary_New failed!"); free(devman); return NULL; } diff --git a/channels/rdpdr/client/irp.c b/channels/rdpdr/client/irp.c index b818b67ac35b..b1278103cbb4 100644 --- a/channels/rdpdr/client/irp.c +++ b/channels/rdpdr/client/irp.c @@ -76,13 +76,13 @@ static UINT irp_complete(IRP* irp) return irp_free(irp); } -IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error) +IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error) { IRP* irp; DEVICE* device; UINT32 DeviceId; - if (Stream_GetRemainingLength(s) < 20) + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 20)) { if (error) *error = ERROR_INVALID_DATA; @@ -94,7 +94,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error) if (!device) { - WLog_WARN(TAG, "devman_get_device_by_id failed!"); + WLog_Print(log, WLOG_WARN, "devman_get_device_by_id failed!"); if (error) *error = CHANNEL_RC_OK; @@ -105,7 +105,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error) if (!irp) { - WLog_ERR(TAG, "_aligned_malloc failed!"); + WLog_Print(log, WLOG_ERROR, "_aligned_malloc failed!"); if (error) *error = CHANNEL_RC_NO_MEMORY; return NULL; @@ -125,7 +125,7 @@ IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error) irp->output = Stream_New(NULL, 256); if (!irp->output) { - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(log, WLOG_ERROR, "Stream_New failed!"); _aligned_free(irp); if (error) *error = CHANNEL_RC_NO_MEMORY; diff --git a/channels/rdpdr/client/irp.h b/channels/rdpdr/client/irp.h index 17d75acde6ee..46a67be2bc24 100644 --- a/channels/rdpdr/client/irp.h +++ b/channels/rdpdr/client/irp.h @@ -21,8 +21,9 @@ #ifndef FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H #define FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H +#include #include "rdpdr_main.h" -IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error); +IRP* irp_new(DEVMAN* devman, wStream* s, wLog* log, UINT* error); #endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */ diff --git a/channels/rdpdr/client/rdpdr_capabilities.c b/channels/rdpdr/client/rdpdr_capabilities.c index 97c876b0e3b9..0e6baf8306ae 100644 --- a/channels/rdpdr/client/rdpdr_capabilities.c +++ b/channels/rdpdr/client/rdpdr_capabilities.c @@ -35,6 +35,8 @@ #include "rdpdr_main.h" #include "rdpdr_capabilities.h" +#define RDPDR_CAPABILITY_HEADER_LENGTH 8 + /* Output device redirection capability set header */ static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 capabilityLength, UINT32 version) @@ -48,39 +50,59 @@ static void rdpdr_write_capset_header(wStream* s, UINT16 capabilityType, UINT16 static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s) { WINPR_UNUSED(rdpdr); - rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, 44, GENERAL_CAPABILITY_VERSION_02); - Stream_Write_UINT32(s, 0); /* osType, ignored on receipt */ - Stream_Write_UINT32(s, 0); /* osVersion, unused and must be set to zero */ - Stream_Write_UINT16(s, 1); /* protocolMajorVersion, must be set to 1 */ - Stream_Write_UINT16(s, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinorVersion */ - Stream_Write_UINT32(s, 0x0000FFFF); /* ioCode1 */ - Stream_Write_UINT32(s, 0); /* ioCode2, must be set to zero, reserved for future use */ - Stream_Write_UINT32(s, RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | - RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */ - Stream_Write_UINT32(s, ENABLE_ASYNCIO); /* extraFlags1 */ - Stream_Write_UINT32(s, 0); /* extraFlags2, must be set to zero, reserved for future use */ + + const UINT32 ioCode1 = rdpdr->clientIOCode1 & rdpdr->serverIOCode1; + const UINT32 ioCode2 = rdpdr->clientIOCode2 & rdpdr->serverIOCode2; + + rdpdr_write_capset_header(s, CAP_GENERAL_TYPE, RDPDR_CAPABILITY_HEADER_LENGTH + 36, + GENERAL_CAPABILITY_VERSION_02); + Stream_Write_UINT32(s, rdpdr->clientOsType); /* osType, ignored on receipt */ + Stream_Write_UINT32(s, rdpdr->clientOsVersion); /* osVersion, unused and must be set to zero */ + Stream_Write_UINT16(s, rdpdr->clientVersionMajor); /* protocolMajorVersion, must be set to 1 */ + Stream_Write_UINT16(s, rdpdr->clientVersionMinor); /* protocolMinorVersion */ + Stream_Write_UINT32(s, ioCode1); /* ioCode1 */ + Stream_Write_UINT32(s, ioCode2); /* ioCode2, must be set to zero, reserved for future use */ + Stream_Write_UINT32(s, rdpdr->clientExtendedPDU); /* extendedPDU */ + Stream_Write_UINT32(s, rdpdr->clientExtraFlags1); /* extraFlags1 */ + Stream_Write_UINT32( + s, + rdpdr->clientExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */ Stream_Write_UINT32( - s, 0); /* SpecialTypeDeviceCap, number of special devices to be redirected before logon */ + s, rdpdr->clientSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to + be redirected before logon */ } /* Process device direction general capability set */ -static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s) +static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength) { - UINT16 capabilityLength; WINPR_UNUSED(rdpdr); - if (Stream_GetRemainingLength(s) < 2) - return ERROR_INVALID_DATA; - - Stream_Read_UINT16(s, capabilityLength); - - if (capabilityLength < 4) + if (capabilityLength != 36) + { + WLog_Print(rdpdr->log, WLOG_ERROR, + "CAP_GENERAL_TYPE::CapabilityLength expected 36, got %" PRIu32, + capabilityLength); return ERROR_INVALID_DATA; + } - if (Stream_GetRemainingLength(s) < capabilityLength - 4U) + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength)) return ERROR_INVALID_DATA; - Stream_Seek(s, capabilityLength - 4U); + Stream_Read_UINT32(s, rdpdr->serverOsType); /* osType, ignored on receipt */ + Stream_Read_UINT32(s, rdpdr->serverOsVersion); /* osVersion, unused and must be set to zero */ + Stream_Read_UINT16(s, rdpdr->serverVersionMajor); /* protocolMajorVersion, must be set to 1 */ + Stream_Read_UINT16(s, rdpdr->serverVersionMinor); /* protocolMinorVersion */ + Stream_Read_UINT32(s, rdpdr->serverIOCode1); /* ioCode1 */ + Stream_Read_UINT32( + s, rdpdr->serverIOCode2); /* ioCode2, must be set to zero, reserved for future use */ + Stream_Read_UINT32(s, rdpdr->serverExtendedPDU); /* extendedPDU */ + Stream_Read_UINT32(s, rdpdr->serverExtraFlags1); /* extraFlags1 */ + Stream_Read_UINT32( + s, + rdpdr->serverExtraFlags2); /* extraFlags2, must be set to zero, reserved for future use */ + Stream_Read_UINT32( + s, rdpdr->serverSpecialTypeDeviceCap); /* SpecialTypeDeviceCap, number of special devices to + be redirected before logon */ return CHANNEL_RC_OK; } @@ -92,23 +114,14 @@ static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s) } /* Process printer direction capability set */ -static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s) +static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength) { - UINT16 capabilityLength; WINPR_UNUSED(rdpdr); - if (Stream_GetRemainingLength(s) < 2) + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength)) return ERROR_INVALID_DATA; - Stream_Read_UINT16(s, capabilityLength); - - if (capabilityLength < 4) - return ERROR_INVALID_DATA; - - if (Stream_GetRemainingLength(s) < capabilityLength - 4U) - return ERROR_INVALID_DATA; - - Stream_Seek(s, capabilityLength - 4U); + Stream_Seek(s, capabilityLength); return CHANNEL_RC_OK; } @@ -120,23 +133,14 @@ static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s) } /* Process port redirection capability set */ -static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s) +static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength) { - UINT16 capabilityLength; WINPR_UNUSED(rdpdr); - if (Stream_GetRemainingLength(s) < 2) - return ERROR_INVALID_DATA; - - Stream_Read_UINT16(s, capabilityLength); - - if (capabilityLength < 4U) - return ERROR_INVALID_DATA; - - if (Stream_GetRemainingLength(s) < capabilityLength - 4U) + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength)) return ERROR_INVALID_DATA; - Stream_Seek(s, capabilityLength - 4U); + Stream_Seek(s, capabilityLength); return CHANNEL_RC_OK; } @@ -148,23 +152,14 @@ static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s) } /* Process drive redirection capability set */ -static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s) +static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength) { - UINT16 capabilityLength; WINPR_UNUSED(rdpdr); - if (Stream_GetRemainingLength(s) < 2) + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength)) return ERROR_INVALID_DATA; - Stream_Read_UINT16(s, capabilityLength); - - if (capabilityLength < 4) - return ERROR_INVALID_DATA; - - if (Stream_GetRemainingLength(s) < capabilityLength - 4U) - return ERROR_INVALID_DATA; - - Stream_Seek(s, capabilityLength - 4U); + Stream_Seek(s, capabilityLength); return CHANNEL_RC_OK; } @@ -176,23 +171,14 @@ static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s) } /* Process smartcard redirection capability set */ -static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s) +static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s, UINT16 capabilityLength) { - UINT16 capabilityLength; WINPR_UNUSED(rdpdr); - if (Stream_GetRemainingLength(s) < 2) - return ERROR_INVALID_DATA; - - Stream_Read_UINT16(s, capabilityLength); - - if (capabilityLength < 4) + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, capabilityLength)) return ERROR_INVALID_DATA; - if (Stream_GetRemainingLength(s) < capabilityLength - 4U) - return ERROR_INVALID_DATA; - - Stream_Seek(s, capabilityLength - 4U); + Stream_Seek(s, capabilityLength); return CHANNEL_RC_OK; } @@ -201,12 +187,14 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s) UINT status = CHANNEL_RC_OK; UINT16 i; UINT16 numCapabilities; - UINT16 capabilityType; if (!rdpdr || !s) return CHANNEL_RC_NULL_DATA; - if (Stream_GetRemainingLength(s) < 4) + WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_SERVER_CAPS); + rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_CLIENT_CAPS); + + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 4)) return ERROR_INVALID_DATA; Stream_Read_UINT16(s, numCapabilities); @@ -214,31 +202,44 @@ UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s) for (i = 0; i < numCapabilities; i++) { - if (Stream_GetRemainingLength(s) < sizeof(UINT16)) + UINT16 capabilityType; + UINT16 capabilityLength; + UINT32 version = 0; + + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, + 2 * sizeof(UINT16) + sizeof(UINT32))) return ERROR_INVALID_DATA; Stream_Read_UINT16(s, capabilityType); + Stream_Read_UINT16(s, capabilityLength); + Stream_Read_UINT32(s, version); + + if (capabilityLength < 8) + { + return ERROR_INVALID_DATA; + } + capabilityLength -= 8; switch (capabilityType) { case CAP_GENERAL_TYPE: - status = rdpdr_process_general_capset(rdpdr, s); + status = rdpdr_process_general_capset(rdpdr, s, capabilityLength); break; case CAP_PRINTER_TYPE: - status = rdpdr_process_printer_capset(rdpdr, s); + status = rdpdr_process_printer_capset(rdpdr, s, capabilityLength); break; case CAP_PORT_TYPE: - status = rdpdr_process_port_capset(rdpdr, s); + status = rdpdr_process_port_capset(rdpdr, s, capabilityLength); break; case CAP_DRIVE_TYPE: - status = rdpdr_process_drive_capset(rdpdr, s); + status = rdpdr_process_drive_capset(rdpdr, s, capabilityLength); break; case CAP_SMARTCARD_TYPE: - status = rdpdr_process_smartcard_capset(rdpdr, s); + status = rdpdr_process_smartcard_capset(rdpdr, s, capabilityLength); break; default: @@ -264,7 +265,7 @@ UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr) if (!s) { - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } diff --git a/channels/rdpdr/client/rdpdr_main.c b/channels/rdpdr/client/rdpdr_main.c index 617ea4287052..a9ac6eca72ca 100644 --- a/channels/rdpdr/client/rdpdr_main.c +++ b/channels/rdpdr/client/rdpdr_main.c @@ -31,8 +31,11 @@ #include #include +#include #include +#include + #include #include #include @@ -76,12 +79,142 @@ struct _DEVICE_DRIVE_EXT BOOL automount; }; +static const char* rdpdr_packetid_string(UINT16 packetid) +{ + switch (packetid) + { + case PAKID_CORE_SERVER_ANNOUNCE: + return "PAKID_CORE_SERVER_ANNOUNCE"; + case PAKID_CORE_CLIENTID_CONFIRM: + return "PAKID_CORE_CLIENTID_CONFIRM"; + case PAKID_CORE_CLIENT_NAME: + return "PAKID_CORE_CLIENT_NAME"; + case PAKID_CORE_DEVICELIST_ANNOUNCE: + return "PAKID_CORE_DEVICELIST_ANNOUNCE"; + case PAKID_CORE_DEVICE_REPLY: + return "PAKID_CORE_DEVICE_REPLY"; + case PAKID_CORE_DEVICE_IOREQUEST: + return "PAKID_CORE_DEVICE_IOREQUEST"; + case PAKID_CORE_DEVICE_IOCOMPLETION: + return "PAKID_CORE_DEVICE_IOCOMPLETION"; + case PAKID_CORE_SERVER_CAPABILITY: + return "PAKID_CORE_SERVER_CAPABILITY"; + case PAKID_CORE_CLIENT_CAPABILITY: + return "PAKID_CORE_CLIENT_CAPABILITY"; + case PAKID_CORE_DEVICELIST_REMOVE: + return "PAKID_CORE_DEVICELIST_REMOVE"; + case PAKID_CORE_USER_LOGGEDON: + return "PAKID_CORE_USER_LOGGEDON"; + case PAKID_PRN_CACHE_DATA: + return "PAKID_PRN_CACHE_DATA"; + case PAKID_PRN_USING_XPS: + return "PAKID_PRN_USING_XPS"; + default: + return "UNKNOWN"; + } +} + +static const char* rdpdr_state_str(enum RDPDR_CHANNEL_STATE state) +{ + switch (state) + { + case RDPDR_CHANNEL_STATE_INITIAL: + return "RDPDR_CHANNEL_STATE_INITIAL"; + case RDPDR_CHANNEL_STATE_ANNOUNCE: + return "RDPDR_CHANNEL_STATE_ANNOUNCE"; + case RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY: + return "RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY"; + case RDPDR_CHANNEL_STATE_NAME_REQUEST: + return "RDPDR_CHANNEL_STATE_NAME_REQUEST"; + case RDPDR_CHANNEL_STATE_SERVER_CAPS: + return "RDPDR_CHANNEL_STATE_SERVER_CAPS"; + case RDPDR_CHANNEL_STATE_CLIENT_CAPS: + return "RDPDR_CHANNEL_STATE_CLIENT_CAPS"; + case RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM: + return "RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM"; + case RDPDR_CHANNEL_STATE_READY: + return "RDPDR_CHANNEL_STATE_READY"; + case RDPDR_CHANNEL_STATE_USER_LOGGEDON: + return "RDPDR_CHANNEL_STATE_USER_LOGGEDON"; + default: + return "RDPDR_CHANNEL_STATE_UNKNOWN"; + } +} + +static const char* rdpdr_device_type_string(UINT32 type) +{ + switch (type) + { + case RDPDR_DTYP_SERIAL: + return "serial"; + case RDPDR_DTYP_PRINT: + return "printer"; + case RDPDR_DTYP_FILESYSTEM: + return "drive"; + case RDPDR_DTYP_SMARTCARD: + return "smartcard"; + case RDPDR_DTYP_PARALLEL: + return "parallel"; + default: + return "UNKNOWN"; + } +} + +static const char* support_str(BOOL val) +{ + if (val) + return "supported"; + return "not found"; +} + +static const char* rdpdr_caps_pdu_str(UINT32 flag) +{ + switch (flag) + { + case RDPDR_DEVICE_REMOVE_PDUS: + return "RDPDR_USER_LOGGEDON_PDU"; + case RDPDR_CLIENT_DISPLAY_NAME_PDU: + return "RDPDR_CLIENT_DISPLAY_NAME_PDU"; + case RDPDR_USER_LOGGEDON_PDU: + return "RDPDR_USER_LOGGEDON_PDU"; + default: + return "RDPDR_UNKNONW"; + } +} + +static BOOL rdpdr_check_extended_pdu_flag(rdpdrPlugin* rdpdr, UINT32 flag) +{ + WINPR_ASSERT(rdpdr); + + const BOOL client = (rdpdr->clientExtendedPDU & flag) != 0; + const BOOL server = (rdpdr->serverExtendedPDU & flag) != 0; + + if (!client || !server) + { + WLog_Print(rdpdr->log, WLOG_WARN, "Checking ExtendedPDU::%s, client %s, server %s", + rdpdr_caps_pdu_str(flag), support_str(client), support_str(server)); + return FALSE; + } + return TRUE; +} + +BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next) +{ + WINPR_ASSERT(rdpdr); + + if (next != rdpdr->state) + WLog_Print(rdpdr->log, WLOG_DEBUG, "[RDPDR] transition from %s to %s", + rdpdr_state_str(rdpdr->state), rdpdr_state_str(next)); + rdpdr->state = next; + return TRUE; +} + /** * Function description * * @return 0 on success, otherwise a Win32 error code */ -static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL userLoggedOn); +static UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr); /** * Function description @@ -92,11 +225,21 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou { UINT32 i; wStream* s; + + WINPR_ASSERT(rdpdr); + WINPR_ASSERT(ids || (count == 0)); + + if (count == 0) + return CHANNEL_RC_OK; + + if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_DEVICE_REMOVE_PDUS)) + return CHANNEL_RC_OK; + s = Stream_New(NULL, count * sizeof(UINT32) + 8); if (!s) { - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } @@ -204,13 +347,12 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) drive.Type = RDPDR_DTYP_FILESYSTEM; drive.Path = drive_path; - drive_path[1] = '\0'; drive.automount = TRUE; drive.Name = drive_name; devman_load_device_service(rdpdr->devman, (const RDPDR_DEVICE*)&drive, rdpdr->rdpcontext); - rdpdr_send_device_list_announce_request(rdpdr, TRUE); + rdpdr_try_send_device_list_announce_request(rdpdr); } } @@ -259,8 +401,8 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) rdpdr, 1, ids))) { // dont end on error, just report ? - WLog_ERR( - TAG, + WLog_Print( + rdpdr->log, WLOG_ERROR, "rdpdr_send_device_list_remove_request failed " "with error %" PRIu32 "!", error); @@ -356,7 +498,7 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr) if (rdpdr->hotplug_wnd && !PostMessage(rdpdr->hotplug_wnd, WM_QUIT, 0, 0)) { error = GetLastError(); - WLog_ERR(TAG, "PostMessage failed with error %" PRIu32 "", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "PostMessage failed with error %" PRIu32 "", error); } return error; @@ -473,9 +615,9 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr) if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids))) { - WLog_ERR(TAG, - "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!", + error); goto cleanup; } } @@ -504,7 +646,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr) if ((error = devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE*)&drive, rdpdr->rdpcontext))) { - WLog_ERR(TAG, "devman_load_device_service failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "devman_load_device_service failed!"); error = CHANNEL_RC_NO_MEMORY; goto cleanup; } @@ -538,10 +680,11 @@ static void drive_hotplug_fsevent_callback(ConstFSEventStreamRef streamRef, { if ((error = handle_hotplug(rdpdr))) { - WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", + error); } else - rdpdr_send_device_list_announce_request(rdpdr, TRUE); + rdpdr_try_send_device_list_announce_request(rdpdr); return; } @@ -554,7 +697,7 @@ void first_hotplug(rdpdrPlugin* rdpdr) if ((error = handle_hotplug(rdpdr))) { - WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", error); } } @@ -583,7 +726,6 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg) #else - static const char* automountLocations[] = { "/run/user/%lu/gvfs", "/run/media/%s", "/media/%s", "/media", "/mnt" }; @@ -591,16 +733,13 @@ static BOOL isAutomountLocation(const char* path) { const size_t nrLocations = sizeof(automountLocations) / sizeof(automountLocations[0]); size_t x; - char buffer[MAX_PATH]; + char buffer[MAX_PATH] = { 0 }; uid_t uid = getuid(); char uname[MAX_PATH] = { 0 }; + ULONG size = sizeof(uname) - 1; -#ifndef HAVE_GETLOGIN_R - strncpy(uname, getlogin(), sizeof(uname)); -#else - if (getlogin_r(uname, sizeof(uname)) != 0) + if (!GetUserNameExA(NameSamCompatible, uname, &size)) return FALSE; -#endif if (!path) return FALSE; @@ -656,21 +795,21 @@ static void handle_mountpoint(hotplug_dev* dev_array, size_t* size, const char* if (isAutomountLocation(mountpoint) && (*size < MAX_USB_DEVICES)) { dev_array[*size].path = _strdup(mountpoint); - dev_array[*size + 1].to_add = TRUE; + dev_array[*size].to_add = TRUE; (*size)++; } } #ifdef __sun #include -static UINT handle_platform_mounts_sun(hotplug_dev* dev_array, size_t* size) +static UINT handle_platform_mounts_sun(wLog* log, hotplug_dev* dev_array, size_t* size) { FILE* f; struct mnttab ent; f = fopen("/etc/mnttab", "r"); if (f == NULL) { - WLog_ERR(TAG, "fopen failed!"); + WLog_Print(log, WLOG_ERROR, "fopen failed!"); return ERROR_OPEN_FAILED; } while (getmntent(f, &ent) == 0) @@ -684,7 +823,7 @@ static UINT handle_platform_mounts_sun(hotplug_dev* dev_array, size_t* size) #if defined(__FreeBSD__) || defined(__OpenBSD__) #include -static UINT handle_platform_mounts_bsd(hotplug_dev* dev_array, size_t* size) +static UINT handle_platform_mounts_bsd(wLog* log, hotplug_dev* dev_array, size_t* size) { int mntsize; size_t idx; @@ -694,7 +833,7 @@ static UINT handle_platform_mounts_bsd(hotplug_dev* dev_array, size_t* size) if (!mntsize) { /* TODO: handle 'errno' */ - WLog_ERR(TAG, "getmntinfo failed!"); + WLog_Print(log, WLOG_ERROR, "getmntinfo failed!"); return ERROR_OPEN_FAILED; } for (idx = 0; idx < (size_t)mntsize; idx++) @@ -708,14 +847,14 @@ static UINT handle_platform_mounts_bsd(hotplug_dev* dev_array, size_t* size) #if defined(__LINUX__) || defined(__linux__) #include -static UINT handle_platform_mounts_linux(hotplug_dev* dev_array, size_t* size) +static UINT handle_platform_mounts_linux(wLog* log, hotplug_dev* dev_array, size_t* size) { FILE* f; struct mntent* ent; f = fopen("/proc/mounts", "r"); if (f == NULL) { - WLog_ERR(TAG, "fopen failed!"); + WLog_Print(log, WLOG_ERROR, "fopen failed!"); return ERROR_OPEN_FAILED; } while ((ent = getmntent(f)) != NULL) @@ -727,14 +866,14 @@ static UINT handle_platform_mounts_linux(hotplug_dev* dev_array, size_t* size) } #endif -static UINT handle_platform_mounts(hotplug_dev* dev_array, size_t* size) +static UINT handle_platform_mounts(wLog* log, hotplug_dev* dev_array, size_t* size) { #ifdef __sun - return handle_platform_mounts_sun(dev_array, size); + return handle_platform_mounts_sun(log, dev_array, size); #elif defined(__FreeBSD__) || defined(__OpenBSD__) - return handle_platform_mounts_bsd(dev_array, size); + return handle_platform_mounts_bsd(log, dev_array, size); #elif defined(__LINUX__) || defined(__linux__) - return handle_platform_mounts_linux(dev_array, size); + return handle_platform_mounts_linux(log, dev_array, size); #endif return ERROR_CALL_NOT_IMPLEMENTED; } @@ -792,7 +931,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr) UINT32 ids[1]; UINT error = ERROR_SUCCESS; - error = handle_platform_mounts(dev_array, &size); + error = handle_platform_mounts(rdpdr->log, dev_array, &size); /* delete removed devices */ count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys); @@ -836,9 +975,9 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr) if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids))) { - WLog_ERR(TAG, - "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!", + error); goto cleanup; } } @@ -860,7 +999,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr) if (!drive.Name) { - WLog_ERR(TAG, "_strdup failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "_strdup failed!"); error = CHANNEL_RC_NO_MEMORY; goto cleanup; } @@ -868,7 +1007,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr) if ((error = devman_load_device_service(rdpdr->devman, (const RDPDR_DEVICE*)&drive, rdpdr->rdpcontext))) { - WLog_ERR(TAG, "devman_load_device_service failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "devman_load_device_service failed!"); goto cleanup; } } @@ -889,7 +1028,7 @@ static void first_hotplug(rdpdrPlugin* rdpdr) if ((error = handle_hotplug(rdpdr))) { - WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", error); } } @@ -907,7 +1046,7 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg) if (mfd < 0) { - WLog_ERR(TAG, "ERROR: Unable to open /proc/mounts."); + WLog_Print(rdpdr->log, WLOG_ERROR, "ERROR: Unable to open /proc/mounts."); error = ERROR_INTERNAL_ERROR; goto out; } @@ -924,7 +1063,8 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg) if (status == WAIT_FAILED) { error = GetLastError(); - WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!", + error); goto out; } @@ -936,11 +1076,12 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg) /* file /proc/mounts changed, handle this */ if ((error = handle_hotplug(rdpdr))) { - WLog_ERR(TAG, "handle_hotplug failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", + error); goto out; } else - rdpdr_send_device_list_announce_request(rdpdr, TRUE); + rdpdr_try_send_device_list_announce_request(rdpdr); } FD_ZERO(&rfds); @@ -980,7 +1121,8 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr) if (WaitForSingleObject(rdpdr->hotplugThread, INFINITE) == WAIT_FAILED) { error = GetLastError(); - WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!", + error); return error; } @@ -1009,7 +1151,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr) if (!rdpdr->devman) { - WLog_ERR(TAG, "devman_new failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "devman_new failed!"); return CHANNEL_RC_NO_MEMORY; } @@ -1038,7 +1180,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr) if (!(rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { - WLog_ERR(TAG, "CreateEvent failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "CreateEvent failed!"); return ERROR_INTERNAL_ERROR; } @@ -1047,7 +1189,7 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr) if (!(rdpdr->hotplugThread = CreateThread(NULL, 0, drive_hotplug_thread_func, rdpdr, 0, NULL))) { - WLog_ERR(TAG, "CreateThread failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "CreateThread failed!"); #ifndef _WIN32 CloseHandle(rdpdr->stopEvent); rdpdr->stopEvent = NULL; @@ -1061,7 +1203,8 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr) if ((error = devman_load_device_service(rdpdr->devman, device, rdpdr->rdpcontext))) { - WLog_ERR(TAG, "devman_load_device_service failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "devman_load_device_service failed with error %" PRIu32 "!", error); return error; } } @@ -1074,10 +1217,19 @@ static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s if (Stream_GetRemainingLength(s) < 8) return ERROR_INVALID_DATA; - Stream_Read_UINT16(s, rdpdr->versionMajor); - Stream_Read_UINT16(s, rdpdr->versionMinor); + Stream_Read_UINT16(s, rdpdr->serverVersionMajor); + Stream_Read_UINT16(s, rdpdr->serverVersionMinor); Stream_Read_UINT32(s, rdpdr->clientID); rdpdr->sequenceId++; + + rdpdr->clientVersionMajor = MIN(RDPDR_VERSION_MAJOR, rdpdr->serverVersionMajor); + rdpdr->clientVersionMinor = MIN(RDPDR_VERSION_MINOR_RDP10X, rdpdr->serverVersionMinor); + WLog_Print(rdpdr->log, WLOG_DEBUG, + "[rdpdr] server announces version %" PRIu32 ".%" PRIu32 ", client uses %" PRIu32 + ".%" PRIu32, + rdpdr->serverVersionMajor, rdpdr->serverVersionMinor, rdpdr->clientVersionMajor, + rdpdr->clientVersionMinor); + return CHANNEL_RC_OK; } @@ -1089,18 +1241,23 @@ static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s static UINT rdpdr_send_client_announce_reply(rdpdrPlugin* rdpdr) { wStream* s; + + WINPR_ASSERT(rdpdr); + WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE); + rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY); + s = Stream_New(NULL, 12); if (!s) { - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } Stream_Write_UINT16(s, RDPDR_CTYP_CORE); /* Component (2 bytes) */ Stream_Write_UINT16(s, PAKID_CORE_CLIENTID_CONFIRM); /* PacketId (2 bytes) */ - Stream_Write_UINT16(s, rdpdr->versionMajor); - Stream_Write_UINT16(s, rdpdr->versionMinor); + Stream_Write_UINT16(s, rdpdr->clientVersionMajor); + Stream_Write_UINT16(s, rdpdr->clientVersionMinor); Stream_Write_UINT32(s, (UINT32)rdpdr->clientID); return rdpdr_send(rdpdr, s); } @@ -1116,8 +1273,15 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr) WCHAR* computerNameW = NULL; size_t computerNameLenW; + WINPR_ASSERT(rdpdr); + WINPR_ASSERT(rdpdr->state == RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY); + rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_NAME_REQUEST); + if (!rdpdr->computerName[0]) - gethostname(rdpdr->computerName, sizeof(rdpdr->computerName) - 1); + { + DWORD size = sizeof(rdpdr->computerName) - 1; + GetComputerNameA(rdpdr->computerName, &size); + } computerNameLenW = ConvertToUnicode(CP_UTF8, 0, rdpdr->computerName, -1, &computerNameW, 0) * 2; s = Stream_New(NULL, 16 + computerNameLenW + 2); @@ -1125,7 +1289,7 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr) if (!s) { free(computerNameW); - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } @@ -1146,17 +1310,20 @@ static UINT rdpdr_process_server_clientid_confirm(rdpdrPlugin* rdpdr, wStream* s UINT16 versionMinor; UINT32 clientID; - if (Stream_GetRemainingLength(s) < 8) + WINPR_ASSERT(rdpdr); + WINPR_ASSERT(s); + + if (!Stream_CheckAndLogRequiredLengthWLog(rdpdr->log, s, 8)) return ERROR_INVALID_DATA; Stream_Read_UINT16(s, versionMajor); Stream_Read_UINT16(s, versionMinor); Stream_Read_UINT32(s, clientID); - if (versionMajor != rdpdr->versionMajor || versionMinor != rdpdr->versionMinor) + if (versionMajor != rdpdr->clientVersionMajor || versionMinor != rdpdr->clientVersionMinor) { - rdpdr->versionMajor = versionMajor; - rdpdr->versionMinor = versionMinor; + rdpdr->clientVersionMajor = versionMajor; + rdpdr->clientVersionMinor = versionMinor; } if (clientID != rdpdr->clientID) @@ -1183,11 +1350,15 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use DEVICE* device; int keyCount; ULONG_PTR* pKeys = NULL; + + if (userLoggedOn) + rdpdr->userLoggedOn = TRUE; + s = Stream_New(NULL, 256); if (!s) { - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } @@ -1210,8 +1381,8 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use * 3. other devices are sent only after user_loggedon */ - if ((rdpdr->versionMinor == 0x0005) || (device->type == RDPDR_DTYP_SMARTCARD) || - userLoggedOn) + if ((rdpdr->clientVersionMinor == RDPDR_VERSION_MINOR_RDP51) || + (device->type == RDPDR_DTYP_SMARTCARD) || userLoggedOn) { data_len = (device->data == NULL ? 0 : Stream_GetPosition(device->data)); @@ -1219,7 +1390,7 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use { free(pKeys); Stream_Free(s, TRUE); - WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!"); return ERROR_INVALID_DATA; } @@ -1243,8 +1414,9 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use Stream_Write(s, Stream_Buffer(device->data), data_len); count++; - WLog_INFO(TAG, "registered device #%" PRIu32 ": %s (type=%" PRIu32 " id=%" PRIu32 ")", - count, device->name, device->type, device->id); + WLog_Print(rdpdr->log, WLOG_INFO, + "registered device #%" PRIu32 ": %s (type=%" PRIu32 " id=%" PRIu32 ")", + count, device->name, device->type, device->id); } } @@ -1257,6 +1429,19 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use return rdpdr_send(rdpdr, s); } +UINT rdpdr_try_send_device_list_announce_request(rdpdrPlugin* rdpdr) +{ + WINPR_ASSERT(rdpdr); + if (rdpdr->state != RDPDR_CHANNEL_STATE_USER_LOGGEDON) + { + WLog_Print(rdpdr->log, WLOG_DEBUG, + "hotplug event received, but channel [RDPDR] is not ready (state %s), ignoring.", + rdpdr_state_str(rdpdr->state)); + return CHANNEL_RC_OK; + } + return rdpdr_send_device_list_announce_request(rdpdr, TRUE); +} + static UINT dummy_irp_response(rdpdrPlugin* rdpdr, wStream* s) { @@ -1267,7 +1452,7 @@ static UINT dummy_irp_response(rdpdrPlugin* rdpdr, wStream* s) wStream* output = Stream_New(NULL, 256); // RDPDR_DEVICE_IO_RESPONSE_LENGTH if (!output) { - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } @@ -1300,11 +1485,11 @@ static UINT rdpdr_process_irp(rdpdrPlugin* rdpdr, wStream* s) { IRP* irp; UINT error = CHANNEL_RC_OK; - irp = irp_new(rdpdr->devman, s, &error); + irp = irp_new(rdpdr->devman, s, rdpdr->log, &error); if (!irp) { - WLog_ERR(TAG, "irp_new failed with %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "irp_new failed with %" PRIu32 "!", error); if (error == CHANNEL_RC_OK) { @@ -1316,8 +1501,11 @@ static UINT rdpdr_process_irp(rdpdrPlugin* rdpdr, wStream* s) IFCALLRET(irp->device->IRPRequest, error, irp->device, irp); - if (error) - WLog_ERR(TAG, "device->IRPRequest failed with error %" PRIu32 "", error); + if (error != CHANNEL_RC_OK) + { + WLog_Print(rdpdr->log, WLOG_ERROR, "device->IRPRequest failed with error %" PRIu32 "", + error); + } return error; } @@ -1359,6 +1547,8 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr) DEVICE* device; ULONG_PTR* pKeys = NULL; UINT error = CHANNEL_RC_OK; + + rdpdr->userLoggedOn = FALSE; /* reset possible received state */ pKeys = NULL; keyCount = ListDictionary_GetKeys(rdpdr->devman->devices, &pKeys); @@ -1369,7 +1559,7 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr) if (error != CHANNEL_RC_OK) { - WLog_ERR(TAG, "Init failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Init failed!"); free(pKeys); return error; } @@ -1379,6 +1569,97 @@ static UINT rdpdr_process_init(rdpdrPlugin* rdpdr) return CHANNEL_RC_OK; } +static BOOL state_match(enum RDPDR_CHANNEL_STATE state, size_t count, va_list ap) +{ + for (size_t x = 0; x < count; x++) + { + enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE); + if (state == cur) + return TRUE; + } + return FALSE; +} + +static const char* state_str(size_t count, va_list ap, char* buffer, size_t size) +{ + for (size_t x = 0; x < count; x++) + { + enum RDPDR_CHANNEL_STATE cur = va_arg(ap, enum RDPDR_CHANNEL_STATE); + const char* curstr = rdpdr_state_str(cur); + winpr_str_append(curstr, buffer, size, "|"); + } + return buffer; +} + +static BOOL rdpdr_state_check(rdpdrPlugin* rdpdr, UINT16 packetid, enum RDPDR_CHANNEL_STATE next, + size_t count, ...) +{ + va_list ap; + WINPR_ASSERT(rdpdr); + + va_start(ap, count); + BOOL rc = state_match(rdpdr->state, count, ap); + va_end(ap); + + if (!rc) + { + const char* strstate = rdpdr_state_str(rdpdr->state); + char buffer[256] = { 0 }; + + va_start(ap, count); + state_str(count, ap, buffer, sizeof(buffer)); + va_end(ap); + + WLog_Print(rdpdr->log, WLOG_ERROR, + "channel [RDPDR] received %s, expected states [%s] but have state %s, aborting.", + rdpdr_packetid_string(packetid), buffer, strstate); + + rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL); + return FALSE; + } + return rdpdr_state_advance(rdpdr, next); +} + +static BOOL rdpdr_check_channel_state(rdpdrPlugin* rdpdr, UINT16 packetid) +{ + WINPR_ASSERT(rdpdr); + + switch (packetid) + { + case PAKID_CORE_SERVER_ANNOUNCE: + /* windows servers sometimes send this message. + * it seems related to session login (e.g. first initialization for RDP/TLS style login, + * then reinitialize the channel after login successful + */ + rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_INITIAL); + return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_ANNOUNCE, 1, + RDPDR_CHANNEL_STATE_INITIAL); + case PAKID_CORE_SERVER_CAPABILITY: + return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_SERVER_CAPS, 6, + RDPDR_CHANNEL_STATE_NAME_REQUEST, + RDPDR_CHANNEL_STATE_SERVER_CAPS, RDPDR_CHANNEL_STATE_READY, + RDPDR_CHANNEL_STATE_CLIENT_CAPS, PAKID_CORE_CLIENTID_CONFIRM, + PAKID_CORE_USER_LOGGEDON); + case PAKID_CORE_CLIENTID_CONFIRM: + return rdpdr_state_check(rdpdr, packetid, RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, 3, + RDPDR_CHANNEL_STATE_CLIENT_CAPS, RDPDR_CHANNEL_STATE_READY, + RDPDR_CHANNEL_STATE_USER_LOGGEDON); + case PAKID_CORE_USER_LOGGEDON: + if (!rdpdr_check_extended_pdu_flag(rdpdr, RDPDR_USER_LOGGEDON_PDU)) + return FALSE; + + return rdpdr_state_check( + rdpdr, packetid, RDPDR_CHANNEL_STATE_USER_LOGGEDON, 4, + RDPDR_CHANNEL_STATE_NAME_REQUEST, RDPDR_CHANNEL_STATE_CLIENT_CAPS, + RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, RDPDR_CHANNEL_STATE_READY); + default: + { + enum RDPDR_CHANNEL_STATE state = RDPDR_CHANNEL_STATE_READY; + return rdpdr_state_check(rdpdr, packetid, state, 1, state); + } + } +} + /** * Function description * @@ -1402,6 +1683,9 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s) if (component == RDPDR_CTYP_CORE) { + if (!rdpdr_check_channel_state(rdpdr, packetId)) + return CHANNEL_RC_OK; + switch (packetId) { case PAKID_CORE_SERVER_ANNOUNCE: @@ -1410,19 +1694,20 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s) } else if ((error = rdpdr_send_client_announce_reply(rdpdr))) { - WLog_ERR(TAG, - "rdpdr_send_client_announce_reply failed with error %" PRIu32 "", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_send_client_announce_reply failed with error %" PRIu32 "", + error); } else if ((error = rdpdr_send_client_name_request(rdpdr))) { - WLog_ERR(TAG, - "rdpdr_send_client_name_request failed with error %" PRIu32 "", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_send_client_name_request failed with error %" PRIu32 "", + error); } else if ((error = rdpdr_process_init(rdpdr))) { - WLog_ERR(TAG, "rdpdr_process_init failed with error %" PRIu32 "", error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_process_init failed with error %" PRIu32 "", error); } break; @@ -1433,9 +1718,9 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s) } else if ((error = rdpdr_send_capability_response(rdpdr))) { - WLog_ERR(TAG, - "rdpdr_send_capability_response failed with error %" PRIu32 "", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_send_capability_response failed with error %" PRIu32 "", + error); } break; @@ -1446,22 +1731,29 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s) } else if ((error = rdpdr_send_device_list_announce_request(rdpdr, FALSE))) { - WLog_ERR( - TAG, + WLog_Print( + rdpdr->log, WLOG_ERROR, "rdpdr_send_device_list_announce_request failed with error %" PRIu32 "", error); } - + else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY)) + { + error = ERROR_INTERNAL_ERROR; + } break; case PAKID_CORE_USER_LOGGEDON: if ((error = rdpdr_send_device_list_announce_request(rdpdr, TRUE))) { - WLog_ERR( - TAG, + WLog_Print( + rdpdr->log, WLOG_ERROR, "rdpdr_send_device_list_announce_request failed with error %" PRIu32 "", error); } + else if (!rdpdr_state_advance(rdpdr, RDPDR_CHANNEL_STATE_READY)) + { + error = ERROR_INTERNAL_ERROR; + } break; @@ -1480,7 +1772,8 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s) case PAKID_CORE_DEVICE_IOREQUEST: if ((error = rdpdr_process_irp(rdpdr, s))) { - WLog_ERR(TAG, "rdpdr_process_irp failed with error %" PRIu32 "", error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_process_irp failed with error %" PRIu32 "", error); return error; } else @@ -1489,7 +1782,8 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s) break; default: - WLog_ERR(TAG, "RDPDR_CTYP_CORE unknown PacketId: 0x%04" PRIX16 "", packetId); + WLog_Print(rdpdr->log, WLOG_ERROR, + "RDPDR_CTYP_CORE unknown PacketId: 0x%04" PRIX16 "", packetId); error = ERROR_INVALID_DATA; break; } @@ -1500,9 +1794,10 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s) if (error != CHANNEL_RC_OK) { - WLog_ERR(TAG, - "Unknown message: Component: 0x%04" PRIX16 " PacketId: 0x%04" PRIX16 "", - component, packetId); + WLog_Print(rdpdr->log, WLOG_ERROR, + "Unknown message: Component: [0x%04" PRIX16 "] PacketId: [0x%04" PRIX16 + "]", + component, packetId); } } } @@ -1542,8 +1837,8 @@ UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s) if (status != CHANNEL_RC_OK) { Stream_Free(s, TRUE); - WLog_ERR(TAG, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]", - WTSErrorToString(status), status); + WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelWriteEx failed with %s [%08" PRIX32 "]", + WTSErrorToString(status), status); } return status; @@ -1580,7 +1875,7 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void* if (!rdpdr->data_in) { - WLog_ERR(TAG, "Stream_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } } @@ -1589,7 +1884,7 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void* if (!Stream_EnsureRemainingCapacity(data_in, dataLength)) { - WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!"); return ERROR_INVALID_DATA; } @@ -1599,7 +1894,8 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void* { if (Stream_Capacity(data_in) != Stream_GetPosition(data_in)) { - WLog_ERR(TAG, "rdpdr_virtual_channel_event_data_received: read error"); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_virtual_channel_event_data_received: read error"); return ERROR_INTERNAL_ERROR; } @@ -1609,7 +1905,7 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr, void* if (!MessageQueue_Post(rdpdr->queue, NULL, 0, (void*)data_in, NULL)) { - WLog_ERR(TAG, "MessageQueue_Post failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "MessageQueue_Post failed!"); return ERROR_INTERNAL_ERROR; } } @@ -1630,14 +1926,15 @@ static VOID VCAPITYPE rdpdr_virtual_channel_open_event_ex(LPVOID lpUserParam, DW case CHANNEL_EVENT_DATA_RECEIVED: if (!rdpdr || !pData || (rdpdr->OpenHandle != openHandle)) { - WLog_ERR(TAG, "error no match"); + WLog_Print(rdpdr->log, WLOG_ERROR, "error no match"); return; } if ((error = rdpdr_virtual_channel_event_data_received(rdpdr, pData, dataLength, totalLength, dataFlags))) - WLog_ERR(TAG, - "rdpdr_virtual_channel_event_data_received failed with error %" PRIu32 "!", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_virtual_channel_event_data_received failed with error %" PRIu32 + "!", + error); break; @@ -1675,7 +1972,8 @@ static DWORD WINAPI rdpdr_virtual_channel_client_thread(LPVOID arg) if ((error = rdpdr_process_connect(rdpdr))) { - WLog_ERR(TAG, "rdpdr_process_connect failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "rdpdr_process_connect failed with error %" PRIu32 "!", + error); if (rdpdr->rdpcontext) setChannelError(rdpdr->rdpcontext, error, @@ -1701,7 +1999,8 @@ static DWORD WINAPI rdpdr_virtual_channel_client_thread(LPVOID arg) if ((error = rdpdr_process_receive(rdpdr, data))) { - WLog_ERR(TAG, "rdpdr_process_receive failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_process_receive failed with error %" PRIu32 "!", error); if (rdpdr->rdpcontext) setChannelError(rdpdr->rdpcontext, error, @@ -1739,8 +2038,8 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pDa if (status != CHANNEL_RC_OK) { - WLog_ERR(TAG, "pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]", - WTSErrorToString(status), status); + WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]", + WTSErrorToString(status), status); return status; } @@ -1748,7 +2047,7 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pDa if (!rdpdr->queue) { - WLog_ERR(TAG, "MessageQueue_New failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "MessageQueue_New failed!"); return CHANNEL_RC_NO_MEMORY; } @@ -1757,7 +2056,7 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pDa if (!(rdpdr->thread = CreateThread(NULL, 0, rdpdr_virtual_channel_client_thread, (void*)rdpdr, 0, NULL))) { - WLog_ERR(TAG, "CreateThread failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "CreateThread failed!"); return ERROR_INTERNAL_ERROR; } @@ -1780,7 +2079,8 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr) (WaitForSingleObject(rdpdr->thread, INFINITE) == WAIT_FAILED)) { error = GetLastError(); - WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "!", + error); return error; } @@ -1791,7 +2091,8 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr) if ((error = drive_hotplug_thread_terminate(rdpdr))) { - WLog_ERR(TAG, "drive_hotplug_thread_terminate failed with error %" PRIu32 "!", error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "drive_hotplug_thread_terminate failed with error %" PRIu32 "!", error); return error; } @@ -1799,8 +2100,8 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr) if (CHANNEL_RC_OK != error) { - WLog_ERR(TAG, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", - WTSErrorToString(error), error); + WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", + WTSErrorToString(error), error); } rdpdr->OpenHandle = 0; @@ -1834,7 +2135,7 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP if (!rdpdr || (rdpdr->InitHandle != pInitHandle)) { - WLog_ERR(TAG, "error no match"); + WLog_Print(rdpdr->log, WLOG_ERROR, "error no match"); return; } @@ -1845,17 +2146,18 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP case CHANNEL_EVENT_CONNECTED: if ((error = rdpdr_virtual_channel_event_connected(rdpdr, pData, dataLength))) - WLog_ERR(TAG, - "rdpdr_virtual_channel_event_connected failed with error %" PRIu32 "!", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_virtual_channel_event_connected failed with error %" PRIu32 "!", + error); break; case CHANNEL_EVENT_DISCONNECTED: if ((error = rdpdr_virtual_channel_event_disconnected(rdpdr))) - WLog_ERR(TAG, - "rdpdr_virtual_channel_event_disconnected failed with error %" PRIu32 "!", - error); + WLog_Print(rdpdr->log, WLOG_ERROR, + "rdpdr_virtual_channel_event_disconnected failed with error %" PRIu32 + "!", + error); break; @@ -1866,7 +2168,7 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP case CHANNEL_EVENT_ATTACHED: case CHANNEL_EVENT_DETACHED: default: - WLog_ERR(TAG, "unknown event %" PRIu32 "!", event); + WLog_Print(rdpdr->log, WLOG_ERROR, "unknown event %" PRIu32 "!", event); break; } @@ -1876,6 +2178,7 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP } /* rdpdr is always built-in */ +#define TAG CHANNELS_TAG("rdpdr.client") #define VirtualChannelEntryEx rdpdr_VirtualChannelEntryEx BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle) @@ -1883,13 +2186,30 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p UINT rc; rdpdrPlugin* rdpdr; CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx; + + WINPR_ASSERT(pEntryPoints); + WINPR_ASSERT(pInitHandle); + rdpdr = (rdpdrPlugin*)calloc(1, sizeof(rdpdrPlugin)); if (!rdpdr) { - WLog_ERR(TAG, "calloc failed!"); + WLog_Print(rdpdr->log, WLOG_ERROR, "calloc failed!"); return FALSE; } + rdpdr->log = WLog_Get(TAG); + + rdpdr->clientExtendedPDU = + RDPDR_DEVICE_REMOVE_PDUS | RDPDR_CLIENT_DISPLAY_NAME_PDU | RDPDR_USER_LOGGEDON_PDU; + rdpdr->clientIOCode1 = + RDPDR_IRP_MJ_CREATE | RDPDR_IRP_MJ_CLEANUP | RDPDR_IRP_MJ_CLOSE | RDPDR_IRP_MJ_READ | + RDPDR_IRP_MJ_WRITE | RDPDR_IRP_MJ_FLUSH_BUFFERS | RDPDR_IRP_MJ_SHUTDOWN | + RDPDR_IRP_MJ_DEVICE_CONTROL | RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION | + RDPDR_IRP_MJ_SET_VOLUME_INFORMATION | RDPDR_IRP_MJ_QUERY_INFORMATION | + RDPDR_IRP_MJ_SET_INFORMATION | RDPDR_IRP_MJ_DIRECTORY_CONTROL | RDPDR_IRP_MJ_LOCK_CONTROL | + RDPDR_IRP_MJ_QUERY_SECURITY | RDPDR_IRP_MJ_SET_SECURITY; + + rdpdr->clientExtraFlags1 = ENABLE_ASYNCIO; rdpdr->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP; @@ -1911,8 +2231,8 @@ BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID p if (CHANNEL_RC_OK != rc) { - WLog_ERR(TAG, "pVirtualChannelInitEx failed with %s [%08" PRIX32 "]", WTSErrorToString(rc), - rc); + WLog_Print(rdpdr->log, WLOG_ERROR, "pVirtualChannelInitEx failed with %s [%08" PRIX32 "]", + WTSErrorToString(rc), rc); free(rdpdr); return FALSE; } diff --git a/channels/rdpdr/client/rdpdr_main.h b/channels/rdpdr/client/rdpdr_main.h index 4c372da63dad..37766f1d67ed 100644 --- a/channels/rdpdr/client/rdpdr_main.h +++ b/channels/rdpdr/client/rdpdr_main.h @@ -42,15 +42,33 @@ #include #endif -#define TAG CHANNELS_TAG("rdpdr.client") +#if !defined(Stream_CheckAndLogRequiredLengthWLog) +#define Stream_CheckAndLogRequiredLengthWLog(log, s, len) \ + Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \ + __FILE__, __LINE__) +#endif typedef struct rdpdr_plugin rdpdrPlugin; +enum RDPDR_CHANNEL_STATE +{ + RDPDR_CHANNEL_STATE_INITIAL = 0, + RDPDR_CHANNEL_STATE_ANNOUNCE, + RDPDR_CHANNEL_STATE_ANNOUNCE_REPLY, + RDPDR_CHANNEL_STATE_NAME_REQUEST, + RDPDR_CHANNEL_STATE_SERVER_CAPS, + RDPDR_CHANNEL_STATE_CLIENT_CAPS, + RDPDR_CHANNEL_STATE_CLIENTID_CONFIRM, + RDPDR_CHANNEL_STATE_READY, + RDPDR_CHANNEL_STATE_USER_LOGGEDON +}; + struct rdpdr_plugin { CHANNEL_DEF channelDef; CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints; + enum RDPDR_CHANNEL_STATE state; HANDLE thread; wStream* data_in; void* InitHandle; @@ -59,12 +77,33 @@ struct rdpdr_plugin DEVMAN* devman; - UINT16 versionMajor; - UINT16 versionMinor; - UINT16 clientID; + UINT32 serverOsType; + UINT32 serverOsVersion; + UINT16 serverVersionMajor; + UINT16 serverVersionMinor; + UINT32 serverExtendedPDU; + UINT32 serverIOCode1; + UINT32 serverIOCode2; + UINT32 serverExtraFlags1; + UINT32 serverExtraFlags2; + UINT32 serverSpecialTypeDeviceCap; + + UINT32 clientOsType; + UINT32 clientOsVersion; + UINT16 clientVersionMajor; + UINT16 clientVersionMinor; + UINT32 clientExtendedPDU; + UINT32 clientIOCode1; + UINT32 clientIOCode2; + UINT32 clientExtraFlags1; + UINT32 clientExtraFlags2; + UINT32 clientSpecialTypeDeviceCap; + + UINT32 clientID; char computerName[256]; UINT32 sequenceId; + BOOL userLoggedOn; /* hotplug support */ HANDLE hotplugThread; @@ -78,8 +117,10 @@ struct rdpdr_plugin HANDLE stopEvent; #endif rdpContext* rdpcontext; + wLog* log; }; +BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next); UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s); #endif /* FREERDP_CHANNEL_RDPDR_CLIENT_MAIN_H */ diff --git a/channels/rdpdr/server/rdpdr_main.c b/channels/rdpdr/server/rdpdr_main.c index 3e005da0fa95..2dcb2a0c731e 100644 --- a/channels/rdpdr/server/rdpdr_main.c +++ b/channels/rdpdr/server/rdpdr_main.c @@ -1952,7 +1952,7 @@ static UINT rdpdr_server_drive_query_directory_callback1(RdpdrServerContext* con irp->Callback = rdpdr_server_drive_query_directory_callback2; irp->DeviceId = deviceId; irp->FileId = fileId; - strcat(irp->PathName, "\\*.*"); + winpr_str_append("\\*.*", irp->PathName, ARRAYSIZE(irp->PathName), NULL); if (!rdpdr_server_enqueue_irp(context, irp)) { diff --git a/channels/rdpdr/server/rdpdr_main.h b/channels/rdpdr/server/rdpdr_main.h index f3f54cc0735e..c1ec5344551f 100644 --- a/channels/rdpdr/server/rdpdr_main.h +++ b/channels/rdpdr/server/rdpdr_main.h @@ -57,13 +57,6 @@ struct _RDPDR_HEADER }; typedef struct _RDPDR_HEADER RDPDR_HEADER; -#define RDPDR_VERSION_MAJOR 0x0001 - -#define RDPDR_VERSION_MINOR_RDP50 0x0002 -#define RDPDR_VERSION_MINOR_RDP51 0x0005 -#define RDPDR_VERSION_MINOR_RDP52 0x000A -#define RDPDR_VERSION_MINOR_RDP6X 0x000C - #define RDPDR_CAPABILITY_HEADER_LENGTH 8 struct _RDPDR_CAPABILITY_HEADER diff --git a/channels/rdpecam/CMakeLists.txt b/channels/rdpecam/CMakeLists.txt new file mode 100644 index 000000000000..63ed41069553 --- /dev/null +++ b/channels/rdpecam/CMakeLists.txt @@ -0,0 +1,22 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel("rdpecam") + +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/rdpecam/ChannelOptions.cmake b/channels/rdpecam/ChannelOptions.cmake new file mode 100644 index 000000000000..7528d115c80e --- /dev/null +++ b/channels/rdpecam/ChannelOptions.cmake @@ -0,0 +1,12 @@ + +set(OPTION_DEFAULT ON) +set(OPTION_CLIENT_DEFAULT OFF) +set(OPTION_SERVER_DEFAULT ON) + +define_channel_options(NAME "rdpecam" TYPE "dynamic" + DESCRIPTION "Video Capture Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPECAM]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_server_options(${OPTION_SERVER_DEFAULT}) + diff --git a/channels/rdpecam/server/CMakeLists.txt b/channels/rdpecam/server/CMakeLists.txt new file mode 100644 index 000000000000..2a65ba7fee46 --- /dev/null +++ b/channels/rdpecam/server/CMakeLists.txt @@ -0,0 +1,27 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_server("rdpecam") + +set(${MODULE_PREFIX}_SRCS + camera_device_enumerator_main.c + camera_device_main.c) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +target_link_libraries(${MODULE_NAME} freerdp) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/rdpecam/server/camera_device_enumerator_main.c b/channels/rdpecam/server/camera_device_enumerator_main.c new file mode 100644 index 000000000000..a17eeee93e49 --- /dev/null +++ b/channels/rdpecam/server/camera_device_enumerator_main.c @@ -0,0 +1,612 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define TAG CHANNELS_TAG("rdpecam-enumerator.server") + +typedef enum +{ + ENUMERATOR_INITIAL, + ENUMERATOR_OPENED, +} eEnumeratorChannelState; + +typedef struct +{ + CamDevEnumServerContext context; + + HANDLE stopEvent; + + HANDLE thread; + void* enumerator_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eEnumeratorChannelState state; + + wStream* buffer; +} enumerator_server; + +static UINT enumerator_server_initialize(CamDevEnumServerContext* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (enumerator->isOpened) + { + WLog_WARN(TAG, "Application error: Camera Device Enumerator channel already initialized, " + "calling in this state is not possible!"); + return ERROR_INVALID_STATE; + } + + enumerator->externalThread = externalThread; + + return error; +} + +static UINT enumerator_server_open_channel(enumerator_server* enumerator) +{ + CamDevEnumServerContext* context = &enumerator->context; + DWORD Error = ERROR_SUCCESS; + HANDLE hEvent; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + UINT32 channelId; + BOOL status = TRUE; + + WINPR_ASSERT(enumerator); + + if (WTSQuerySessionInformationA(enumerator->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + enumerator->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + hEvent = WTSVirtualChannelManagerGetEventHandle(enumerator->context.vcm); + + if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED) + { + Error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error); + return Error; + } + + enumerator->enumerator_channel = WTSVirtualChannelOpenEx( + enumerator->SessionId, RDPECAM_CONTROL_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); + if (!enumerator->enumerator_channel) + { + Error = GetLastError(); + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error); + return Error; + } + + channelId = WTSChannelGetIdByHandle(enumerator->enumerator_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + return Error; +} + +static UINT enumerator_server_handle_select_version_request(CamDevEnumServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SELECT_VERSION_REQUEST pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + IFCALLRET(context->SelectVersionRequest, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SelectVersionRequest failed with error %" PRIu32 "", error); + + return error; +} + +static UINT enumerator_server_recv_device_added_notification(CamDevEnumServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_DEVICE_ADDED_NOTIFICATION pdu; + UINT error = CHANNEL_RC_OK; + size_t remaining_length; + WCHAR* channel_name_start; + char* tmp; + size_t i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + /* + * RequiredLength 4: + * + * Nullterminator DeviceName (2), + * VirtualChannelName (>= 1), + * Nullterminator VirtualChannelName (1) + */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_NO_DATA; + + pdu.DeviceName = (WCHAR*)Stream_Pointer(s); + + remaining_length = Stream_GetRemainingLength(s); + channel_name_start = (WCHAR*)Stream_Pointer(s); + + /* Search for null terminator of DeviceName */ + for (i = 0; i < remaining_length; i += sizeof(WCHAR), ++channel_name_start) + { + if (*channel_name_start == L'\0') + break; + } + + if (*channel_name_start != L'\0') + { + WLog_ERR(TAG, "enumerator_server_recv_device_added_notification: Invalid DeviceName!"); + return ERROR_INVALID_DATA; + } + + pdu.VirtualChannelName = (char*)++channel_name_start; + ++i; + + if (i >= remaining_length || *pdu.VirtualChannelName == '\0') + { + WLog_ERR(TAG, + "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!"); + return ERROR_INVALID_DATA; + } + + tmp = pdu.VirtualChannelName; + for (; i < remaining_length; ++i, ++tmp) + { + if (*tmp == '\0') + break; + } + + if (*tmp != '\0') + { + WLog_ERR(TAG, + "enumerator_server_recv_device_added_notification: Invalid VirtualChannelName!"); + return ERROR_INVALID_DATA; + } + + IFCALLRET(context->DeviceAddedNotification, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->DeviceAddedNotification failed with error %" PRIu32 "", error); + + return error; +} + +static UINT enumerator_server_recv_device_removed_notification(CamDevEnumServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_DEVICE_REMOVED_NOTIFICATION pdu; + UINT error = CHANNEL_RC_OK; + size_t remaining_length; + char* tmp; + size_t i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) + return ERROR_NO_DATA; + + pdu.VirtualChannelName = (char*)Stream_Pointer(s); + + remaining_length = Stream_GetRemainingLength(s); + tmp = (char*)(Stream_Pointer(s) + 1); + + for (i = 1; i < remaining_length; ++i, ++tmp) + { + if (*tmp == '\0') + break; + } + + if (*tmp != '\0') + { + WLog_ERR(TAG, + "enumerator_server_recv_device_removed_notification: Invalid VirtualChannelName!"); + return ERROR_INVALID_DATA; + } + + IFCALLRET(context->DeviceRemovedNotification, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->DeviceRemovedNotification failed with error %" PRIu32 "", error); + + return error; +} + +static UINT enumerator_process_message(enumerator_server* enumerator) +{ + BOOL rc; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned; + CAM_SHARED_MSG_HEADER header = { 0 }; + wStream* s; + + WINPR_ASSERT(enumerator); + WINPR_ASSERT(enumerator->enumerator_channel); + + s = enumerator->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(enumerator->enumerator_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 1) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(enumerator->enumerator_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, header.Version); + Stream_Read_UINT8(s, header.MessageId); + + switch (header.MessageId) + { + case CAM_MSG_ID_SelectVersionRequest: + error = + enumerator_server_handle_select_version_request(&enumerator->context, s, &header); + break; + case CAM_MSG_ID_DeviceAddedNotification: + error = + enumerator_server_recv_device_added_notification(&enumerator->context, s, &header); + break; + case CAM_MSG_ID_DeviceRemovedNotification: + error = enumerator_server_recv_device_removed_notification(&enumerator->context, s, + &header); + break; + default: + WLog_ERR(TAG, "enumerator_process_message: unknown or invalid MessageId %" PRIu8 "", + header.MessageId); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +static UINT enumerator_server_context_poll_int(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(enumerator); + + switch (enumerator->state) + { + case ENUMERATOR_INITIAL: + error = enumerator_server_open_channel(enumerator); + if (error) + WLog_ERR(TAG, "enumerator_server_open_channel failed with error %" PRIu32 "!", + error); + else + enumerator->state = ENUMERATOR_OPENED; + break; + case ENUMERATOR_OPENED: + error = enumerator_process_message(enumerator); + break; + } + + return error; +} + +static HANDLE enumerator_server_get_channel_handle(enumerator_server* enumerator) +{ + void* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(enumerator); + + if (WTSVirtualChannelQuery(enumerator->enumerator_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI enumerator_server_thread_func(LPVOID arg) +{ + DWORD nCount; + HANDLE events[2] = { 0 }; + enumerator_server* enumerator = (enumerator_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status; + + WINPR_ASSERT(enumerator); + + nCount = 0; + events[nCount++] = enumerator->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (enumerator->state) + { + case ENUMERATOR_INITIAL: + error = enumerator_server_context_poll_int(&enumerator->context); + if (error == CHANNEL_RC_OK) + { + events[1] = enumerator_server_get_channel_handle(enumerator); + nCount = 2; + } + break; + case ENUMERATOR_OPENED: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + case WAIT_TIMEOUT: + error = enumerator_server_context_poll_int(&enumerator->context); + break; + + case WAIT_FAILED: + default: + error = ERROR_INTERNAL_ERROR; + break; + } + break; + } + } + + WTSVirtualChannelClose(enumerator->enumerator_channel); + enumerator->enumerator_channel = NULL; + + if (error && enumerator->context.rdpcontext) + setChannelError(enumerator->context.rdpcontext, error, + "enumerator_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +static UINT enumerator_server_open(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (!enumerator->externalThread && (enumerator->thread == NULL)) + { + enumerator->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!enumerator->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + enumerator->thread = + CreateThread(NULL, 0, enumerator_server_thread_func, enumerator, 0, NULL); + if (!enumerator->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(enumerator->stopEvent); + enumerator->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + enumerator->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +static UINT enumerator_server_close(CamDevEnumServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (!enumerator->externalThread && enumerator->thread) + { + SetEvent(enumerator->stopEvent); + + if (WaitForSingleObject(enumerator->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(enumerator->thread); + CloseHandle(enumerator->stopEvent); + enumerator->thread = NULL; + enumerator->stopEvent = NULL; + } + if (enumerator->externalThread) + { + if (enumerator->state != ENUMERATOR_INITIAL) + { + WTSVirtualChannelClose(enumerator->enumerator_channel); + enumerator->enumerator_channel = NULL; + enumerator->state = ENUMERATOR_INITIAL; + } + } + enumerator->isOpened = FALSE; + + return error; +} + +static UINT enumerator_server_context_poll(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + + if (!enumerator->externalThread) + return ERROR_INTERNAL_ERROR; + + return enumerator_server_context_poll_int(context); +} + +static BOOL enumerator_server_context_handle(CamDevEnumServerContext* context, HANDLE* handle) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + WINPR_ASSERT(enumerator); + WINPR_ASSERT(handle); + + if (!enumerator->externalThread) + return FALSE; + if (enumerator->state == ENUMERATOR_INITIAL) + return FALSE; + + *handle = enumerator_server_get_channel_handle(enumerator); + + return TRUE; +} + +static UINT enumerator_server_packet_send(CamDevEnumServerContext* context, wStream* s) +{ + enumerator_server* enumerator = (enumerator_server*)context; + UINT error = CHANNEL_RC_OK; + ULONG written; + + if (!WTSVirtualChannelWrite(enumerator->enumerator_channel, (PCHAR)Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written, + Stream_GetPosition(s)); + } + +out: + Stream_Free(s, TRUE); + return error; +} + +static UINT enumerator_send_select_version_response_pdu( + CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse) +{ + wStream* s; + + s = Stream_New(NULL, CAM_HEADER_SIZE); + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + Stream_Write_UINT8(s, selectVersionResponse->Header.Version); + Stream_Write_UINT8(s, selectVersionResponse->Header.MessageId); + + return enumerator_server_packet_send(context, s); +} + +CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm) +{ + enumerator_server* enumerator = (enumerator_server*)calloc(1, sizeof(enumerator_server)); + + if (!enumerator) + return NULL; + + enumerator->context.vcm = vcm; + enumerator->context.Initialize = enumerator_server_initialize; + enumerator->context.Open = enumerator_server_open; + enumerator->context.Close = enumerator_server_close; + enumerator->context.Poll = enumerator_server_context_poll; + enumerator->context.ChannelHandle = enumerator_server_context_handle; + + enumerator->context.SelectVersionResponse = enumerator_send_select_version_response_pdu; + + enumerator->buffer = Stream_New(NULL, 4096); + if (!enumerator->buffer) + goto fail; + + return &enumerator->context; +fail: + cam_dev_enum_server_context_free(&enumerator->context); + return NULL; +} + +void cam_dev_enum_server_context_free(CamDevEnumServerContext* context) +{ + enumerator_server* enumerator = (enumerator_server*)context; + + if (enumerator) + { + enumerator_server_close(context); + Stream_Free(enumerator->buffer, TRUE); + } + + free(enumerator); +} diff --git a/channels/rdpecam/server/camera_device_main.c b/channels/rdpecam/server/camera_device_main.c new file mode 100644 index 000000000000..941a288d3da9 --- /dev/null +++ b/channels/rdpecam/server/camera_device_main.c @@ -0,0 +1,969 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define TAG CHANNELS_TAG("rdpecam.server") + +typedef enum +{ + CAMERA_DEVICE_INITIAL, + CAMERA_DEVICE_OPENED, +} eCameraDeviceChannelState; + +typedef struct +{ + CameraDeviceServerContext context; + + HANDLE stopEvent; + + HANDLE thread; + void* device_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eCameraDeviceChannelState state; + + wStream* buffer; +} device_server; + +static UINT device_server_initialize(CameraDeviceServerContext* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (device->isOpened) + { + WLog_WARN(TAG, "Application error: Camera channel already initialized, " + "calling in this state is not possible!"); + return ERROR_INVALID_STATE; + } + + device->externalThread = externalThread; + + return error; +} + +static UINT device_server_open_channel(device_server* device) +{ + CameraDeviceServerContext* context = &device->context; + DWORD Error = ERROR_SUCCESS; + HANDLE hEvent; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + UINT32 channelId; + BOOL status = TRUE; + + WINPR_ASSERT(device); + + if (WTSQuerySessionInformationA(device->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + device->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + hEvent = WTSVirtualChannelManagerGetEventHandle(device->context.vcm); + + if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED) + { + Error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error); + return Error; + } + + device->device_channel = WTSVirtualChannelOpenEx(device->SessionId, context->virtualChannelName, + WTS_CHANNEL_OPTION_DYNAMIC); + if (!device->device_channel) + { + Error = GetLastError(); + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error); + return Error; + } + + channelId = WTSChannelGetIdByHandle(device->device_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + return Error; +} + +static UINT device_server_handle_success_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SUCCESS_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + IFCALLRET(context->SuccessResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SuccessResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_error_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_ERROR_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_NO_DATA; + + Stream_Read_UINT32(s, pdu.ErrorCode); + + IFCALLRET(context->ErrorResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->ErrorResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_stream_list_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_STREAM_LIST_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + BYTE i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 5)) + return ERROR_NO_DATA; + + pdu.N_Descriptions = MIN(Stream_GetRemainingLength(s) / 5, 255); + + for (i = 0; i < pdu.N_Descriptions; ++i) + { + CAM_STREAM_DESCRIPTION* StreamDescription = &pdu.StreamDescriptions[i]; + + Stream_Read_UINT16(s, StreamDescription->FrameSourceTypes); + Stream_Read_UINT8(s, StreamDescription->StreamCategory); + Stream_Read_UINT8(s, StreamDescription->Selected); + Stream_Read_UINT8(s, StreamDescription->CanBeShared); + } + + IFCALLRET(context->StreamListResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->StreamListResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_media_type_list_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_MEDIA_TYPE_LIST_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + BYTE i; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 26)) + return ERROR_NO_DATA; + + pdu.N_Descriptions = Stream_GetRemainingLength(s) / 26; + + pdu.MediaTypeDescriptions = calloc(pdu.N_Descriptions, sizeof(CAM_MEDIA_TYPE_DESCRIPTION)); + if (!pdu.MediaTypeDescriptions) + { + WLog_ERR(TAG, "Failed to allocate %zu CAM_MEDIA_TYPE_DESCRIPTION structs", + pdu.N_Descriptions); + return ERROR_NOT_ENOUGH_MEMORY; + } + + for (i = 0; i < pdu.N_Descriptions; ++i) + { + CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions = &pdu.MediaTypeDescriptions[i]; + + Stream_Read_UINT8(s, MediaTypeDescriptions->Format); + Stream_Read_UINT32(s, MediaTypeDescriptions->Width); + Stream_Read_UINT32(s, MediaTypeDescriptions->Height); + Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateNumerator); + Stream_Read_UINT32(s, MediaTypeDescriptions->FrameRateDenominator); + Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioNumerator); + Stream_Read_UINT32(s, MediaTypeDescriptions->PixelAspectRatioDenominator); + Stream_Read_UINT8(s, MediaTypeDescriptions->Flags); + } + + IFCALLRET(context->MediaTypeListResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->MediaTypeListResponse failed with error %" PRIu32 "", error); + + free(pdu.MediaTypeDescriptions); + + return error; +} + +static UINT device_server_recv_current_media_type_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_CURRENT_MEDIA_TYPE_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 26)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.MediaTypeDescription.Format); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.Width); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.Height); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateNumerator); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.FrameRateDenominator); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioNumerator); + Stream_Read_UINT32(s, pdu.MediaTypeDescription.PixelAspectRatioDenominator); + Stream_Read_UINT8(s, pdu.MediaTypeDescription.Flags); + + IFCALLRET(context->CurrentMediaTypeResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->CurrentMediaTypeResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_sample_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SAMPLE_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.StreamIndex); + + pdu.SampleSize = Stream_GetRemainingLength(s); + pdu.Sample = Stream_Pointer(s); + + IFCALLRET(context->SampleResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SampleResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_sample_error_response(CameraDeviceServerContext* context, wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_SAMPLE_ERROR_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 5)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.StreamIndex); + Stream_Read_UINT32(s, pdu.ErrorCode); + + IFCALLRET(context->SampleErrorResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->SampleErrorResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_server_recv_property_list_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_PROPERTY_LIST_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + pdu.N_Properties = Stream_GetRemainingLength(s) / 19; + + if (pdu.N_Properties > 0) + { + size_t i; + + pdu.Properties = calloc(pdu.N_Properties, sizeof(CAM_PROPERTY_DESCRIPTION)); + if (!pdu.Properties) + { + WLog_ERR(TAG, "Failed to allocate %zu CAM_PROPERTY_DESCRIPTION structs", + pdu.N_Properties); + return ERROR_NOT_ENOUGH_MEMORY; + } + + for (i = 0; i < pdu.N_Properties; ++i) + { + Stream_Read_UINT8(s, pdu.Properties[i].PropertySet); + Stream_Read_UINT8(s, pdu.Properties[i].PropertyId); + Stream_Read_UINT8(s, pdu.Properties[i].Capabilities); + Stream_Read_INT32(s, pdu.Properties[i].MinValue); + Stream_Read_INT32(s, pdu.Properties[i].MaxValue); + Stream_Read_INT32(s, pdu.Properties[i].Step); + Stream_Read_INT32(s, pdu.Properties[i].DefaultValue); + } + } + + IFCALLRET(context->PropertyListResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->PropertyListResponse failed with error %" PRIu32 "", error); + + free(pdu.Properties); + + return error; +} + +static UINT device_server_recv_property_value_response(CameraDeviceServerContext* context, + wStream* s, + const CAM_SHARED_MSG_HEADER* header) +{ + CAM_PROPERTY_VALUE_RESPONSE pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(header); + + pdu.Header = *header; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 5)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, pdu.PropertyValue.Mode); + Stream_Read_INT32(s, pdu.PropertyValue.Value); + + IFCALLRET(context->PropertyValueResponse, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->PropertyValueResponse failed with error %" PRIu32 "", error); + + return error; +} + +static UINT device_process_message(device_server* device) +{ + BOOL rc; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned; + CAM_SHARED_MSG_HEADER header = { 0 }; + wStream* s; + + WINPR_ASSERT(device); + WINPR_ASSERT(device->device_channel); + + s = device->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(device->device_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 1) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(device->device_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, CAM_HEADER_SIZE)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, header.Version); + Stream_Read_UINT8(s, header.MessageId); + + switch (header.MessageId) + { + case CAM_MSG_ID_SuccessResponse: + error = device_server_handle_success_response(&device->context, s, &header); + break; + case CAM_MSG_ID_ErrorResponse: + error = device_server_recv_error_response(&device->context, s, &header); + break; + case CAM_MSG_ID_StreamListResponse: + error = device_server_recv_stream_list_response(&device->context, s, &header); + break; + case CAM_MSG_ID_MediaTypeListResponse: + error = device_server_recv_media_type_list_response(&device->context, s, &header); + break; + case CAM_MSG_ID_CurrentMediaTypeResponse: + error = device_server_recv_current_media_type_response(&device->context, s, &header); + break; + case CAM_MSG_ID_SampleResponse: + error = device_server_recv_sample_response(&device->context, s, &header); + break; + case CAM_MSG_ID_SampleErrorResponse: + error = device_server_recv_sample_error_response(&device->context, s, &header); + break; + case CAM_MSG_ID_PropertyListResponse: + error = device_server_recv_property_list_response(&device->context, s, &header); + break; + case CAM_MSG_ID_PropertyValueResponse: + error = device_server_recv_property_value_response(&device->context, s, &header); + break; + default: + WLog_ERR(TAG, "device_process_message: unknown or invalid MessageId %" PRIu8 "", + header.MessageId); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +static UINT device_server_context_poll_int(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(device); + + switch (device->state) + { + case CAMERA_DEVICE_INITIAL: + error = device_server_open_channel(device); + if (error) + WLog_ERR(TAG, "device_server_open_channel failed with error %" PRIu32 "!", error); + else + device->state = CAMERA_DEVICE_OPENED; + break; + case CAMERA_DEVICE_OPENED: + error = device_process_message(device); + break; + } + + return error; +} + +static HANDLE device_server_get_channel_handle(device_server* device) +{ + void* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(device); + + if (WTSVirtualChannelQuery(device->device_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI device_server_thread_func(LPVOID arg) +{ + DWORD nCount; + HANDLE events[2] = { 0 }; + device_server* device = (device_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status; + + WINPR_ASSERT(device); + + nCount = 0; + events[nCount++] = device->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (device->state) + { + case CAMERA_DEVICE_INITIAL: + error = device_server_context_poll_int(&device->context); + if (error == CHANNEL_RC_OK) + { + events[1] = device_server_get_channel_handle(device); + nCount = 2; + } + break; + case CAMERA_DEVICE_OPENED: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + case WAIT_TIMEOUT: + error = device_server_context_poll_int(&device->context); + break; + + case WAIT_FAILED: + default: + error = ERROR_INTERNAL_ERROR; + break; + } + break; + } + } + + WTSVirtualChannelClose(device->device_channel); + device->device_channel = NULL; + + if (error && device->context.rdpcontext) + setChannelError(device->context.rdpcontext, error, + "device_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +static UINT device_server_open(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (!device->externalThread && (device->thread == NULL)) + { + device->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!device->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + device->thread = CreateThread(NULL, 0, device_server_thread_func, device, 0, NULL); + if (!device->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(device->stopEvent); + device->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + device->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +static UINT device_server_close(CameraDeviceServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (!device->externalThread && device->thread) + { + SetEvent(device->stopEvent); + + if (WaitForSingleObject(device->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(device->thread); + CloseHandle(device->stopEvent); + device->thread = NULL; + device->stopEvent = NULL; + } + if (device->externalThread) + { + if (device->state != CAMERA_DEVICE_INITIAL) + { + WTSVirtualChannelClose(device->device_channel); + device->device_channel = NULL; + device->state = CAMERA_DEVICE_INITIAL; + } + } + device->isOpened = FALSE; + + return error; +} + +static UINT device_server_context_poll(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + + if (!device->externalThread) + return ERROR_INTERNAL_ERROR; + + return device_server_context_poll_int(context); +} + +static BOOL device_server_context_handle(CameraDeviceServerContext* context, HANDLE* handle) +{ + device_server* device = (device_server*)context; + + WINPR_ASSERT(device); + WINPR_ASSERT(handle); + + if (!device->externalThread) + return FALSE; + if (device->state == CAMERA_DEVICE_INITIAL) + return FALSE; + + *handle = device_server_get_channel_handle(device); + + return TRUE; +} + +static wStream* device_server_packet_new(size_t size, BYTE version, BYTE messageId) +{ + wStream* s; + + /* Allocate what we need plus header bytes */ + s = Stream_New(NULL, size + CAM_HEADER_SIZE); + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return NULL; + } + + Stream_Write_UINT8(s, version); + Stream_Write_UINT8(s, messageId); + + return s; +} + +static UINT device_server_packet_send(CameraDeviceServerContext* context, wStream* s) +{ + device_server* device = (device_server*)context; + UINT error = CHANNEL_RC_OK; + ULONG written; + + WINPR_ASSERT(context); + WINPR_ASSERT(s); + + if (!WTSVirtualChannelWrite(device->device_channel, (PCHAR)Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written, + Stream_GetPosition(s)); + } + +out: + Stream_Free(s, TRUE); + return error; +} + +static UINT device_server_write_and_send_header(CameraDeviceServerContext* context, BYTE messageId) +{ + wStream* s; + + WINPR_ASSERT(context); + + s = device_server_packet_new(0, context->protocolVersion, messageId); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + return device_server_packet_send(context, s); +} + +static UINT +device_send_activate_device_request_pdu(CameraDeviceServerContext* context, + const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_ActivateDeviceRequest); +} + +static UINT device_send_deactivate_device_request_pdu( + CameraDeviceServerContext* context, + const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_DeactivateDeviceRequest); +} + +static UINT device_send_stream_list_request_pdu(CameraDeviceServerContext* context, + const CAM_STREAM_LIST_REQUEST* streamListRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_StreamListRequest); +} + +static UINT +device_send_media_type_list_request_pdu(CameraDeviceServerContext* context, + const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(mediaTypeListRequest); + + s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_MediaTypeListRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, mediaTypeListRequest->StreamIndex); + + return device_server_packet_send(context, s); +} + +static UINT device_send_current_media_type_request_pdu( + CameraDeviceServerContext* context, + const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(currentMediaTypeRequest); + + s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_CurrentMediaTypeRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, currentMediaTypeRequest->StreamIndex); + + return device_server_packet_send(context, s); +} + +static UINT +device_send_start_streams_request_pdu(CameraDeviceServerContext* context, + const CAM_START_STREAMS_REQUEST* startStreamsRequest) +{ + wStream* s; + size_t i; + + WINPR_ASSERT(context); + WINPR_ASSERT(startStreamsRequest); + + s = device_server_packet_new(startStreamsRequest->N_Infos * 27ul, context->protocolVersion, + CAM_MSG_ID_StartStreamsRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + for (i = 0; i < startStreamsRequest->N_Infos; ++i) + { + const CAM_START_STREAM_INFO* info = &startStreamsRequest->StartStreamsInfo[i]; + const CAM_MEDIA_TYPE_DESCRIPTION* description = &info->MediaTypeDescription; + + Stream_Write_UINT8(s, info->StreamIndex); + + Stream_Write_UINT8(s, description->Format); + Stream_Write_UINT32(s, description->Width); + Stream_Write_UINT32(s, description->Height); + Stream_Write_UINT32(s, description->FrameRateNumerator); + Stream_Write_UINT32(s, description->FrameRateDenominator); + Stream_Write_UINT32(s, description->PixelAspectRatioNumerator); + Stream_Write_UINT32(s, description->PixelAspectRatioDenominator); + Stream_Write_UINT8(s, description->Flags); + } + + return device_server_packet_send(context, s); +} + +static UINT device_send_stop_streams_request_pdu(CameraDeviceServerContext* context, + const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_StopStreamsRequest); +} + +static UINT device_send_sample_request_pdu(CameraDeviceServerContext* context, + const CAM_SAMPLE_REQUEST* sampleRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(sampleRequest); + + s = device_server_packet_new(1, context->protocolVersion, CAM_MSG_ID_SampleRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, sampleRequest->StreamIndex); + + return device_server_packet_send(context, s); +} + +static UINT +device_send_property_list_request_pdu(CameraDeviceServerContext* context, + const CAM_PROPERTY_LIST_REQUEST* propertyListRequest) +{ + WINPR_ASSERT(context); + + return device_server_write_and_send_header(context, CAM_MSG_ID_PropertyListRequest); +} + +static UINT +device_send_property_value_request_pdu(CameraDeviceServerContext* context, + const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(propertyValueRequest); + + s = device_server_packet_new(2, context->protocolVersion, CAM_MSG_ID_PropertyValueRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, propertyValueRequest->PropertySet); + Stream_Write_UINT8(s, propertyValueRequest->PropertyId); + + return device_server_packet_send(context, s); +} + +static UINT device_send_set_property_value_request_pdu( + CameraDeviceServerContext* context, + const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest) +{ + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(setPropertyValueRequest); + + s = device_server_packet_new(2 + 5, context->protocolVersion, + CAM_MSG_ID_SetPropertyValueRequest); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT8(s, setPropertyValueRequest->PropertySet); + Stream_Write_UINT8(s, setPropertyValueRequest->PropertyId); + + Stream_Write_UINT8(s, setPropertyValueRequest->PropertyValue.Mode); + Stream_Write_INT32(s, setPropertyValueRequest->PropertyValue.Value); + + return device_server_packet_send(context, s); +} + +CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm) +{ + device_server* device = (device_server*)calloc(1, sizeof(device_server)); + + if (!device) + return NULL; + + device->context.vcm = vcm; + device->context.Initialize = device_server_initialize; + device->context.Open = device_server_open; + device->context.Close = device_server_close; + device->context.Poll = device_server_context_poll; + device->context.ChannelHandle = device_server_context_handle; + + device->context.ActivateDeviceRequest = device_send_activate_device_request_pdu; + device->context.DeactivateDeviceRequest = device_send_deactivate_device_request_pdu; + + device->context.StreamListRequest = device_send_stream_list_request_pdu; + device->context.MediaTypeListRequest = device_send_media_type_list_request_pdu; + device->context.CurrentMediaTypeRequest = device_send_current_media_type_request_pdu; + + device->context.StartStreamsRequest = device_send_start_streams_request_pdu; + device->context.StopStreamsRequest = device_send_stop_streams_request_pdu; + device->context.SampleRequest = device_send_sample_request_pdu; + + device->context.PropertyListRequest = device_send_property_list_request_pdu; + device->context.PropertyValueRequest = device_send_property_value_request_pdu; + device->context.SetPropertyValueRequest = device_send_set_property_value_request_pdu; + + device->buffer = Stream_New(NULL, 4096); + if (!device->buffer) + goto fail; + + return &device->context; +fail: + camera_device_server_context_free(&device->context); + return NULL; +} + +void camera_device_server_context_free(CameraDeviceServerContext* context) +{ + device_server* device = (device_server*)context; + + if (device) + { + device_server_close(context); + Stream_Free(device->buffer, TRUE); + } + + free(context->virtualChannelName); + + free(device); +} diff --git a/channels/rdpei/client/rdpei_main.c b/channels/rdpei/client/rdpei_main.c index f20d58a1368b..1d95054a02de 100644 --- a/channels/rdpei/client/rdpei_main.c +++ b/channels/rdpei/client/rdpei_main.c @@ -1386,7 +1386,6 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) return CHANNEL_RC_OK; error_out: free(context); - free(rdpei->contactPoints); free(rdpei); return error; } diff --git a/channels/rdpei/server/rdpei_main.c b/channels/rdpei/server/rdpei_main.c index 80ab4b638a21..433b2cf10e42 100644 --- a/channels/rdpei/server/rdpei_main.c +++ b/channels/rdpei/server/rdpei_main.c @@ -99,6 +99,8 @@ UINT rdpei_server_init(RdpeiServerContext* context) void* buffer = NULL; DWORD bytesReturned; RdpeiServerPrivate* priv = context->priv; + UINT32 channelId; + BOOL status = TRUE; priv->channelHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, RDPEI_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); @@ -108,6 +110,15 @@ UINT rdpei_server_init(RdpeiServerContext* context) return CHANNEL_RC_INITIALIZATION_ERROR; } + channelId = WTSChannelGetIdByHandle(priv->channelHandle); + + IFCALLRET(context->onChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->onChannelIdAssigned failed!"); + goto out_close; + } + if (!WTSVirtualChannelQuery(priv->channelHandle, WTSVirtualEventHandle, &buffer, &bytesReturned) || (bytesReturned != sizeof(HANDLE))) @@ -612,6 +623,7 @@ UINT rdpei_server_send_sc_ready_ex(RdpeiServerContext* context, UINT32 version, { ULONG written; RdpeiServerPrivate* priv = context->priv; + UINT32 pduLen = 4; if (priv->automataState != STATE_INITIAL) { @@ -621,14 +633,17 @@ UINT rdpei_server_send_sc_ready_ex(RdpeiServerContext* context, UINT32 version, Stream_SetPosition(priv->outputStream, 0); - if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4)) + if (version >= RDPINPUT_PROTOCOL_V300) + pduLen += 4; + + if (!Stream_EnsureCapacity(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen)) { WLog_ERR(TAG, "Stream_EnsureCapacity failed!"); return CHANNEL_RC_NO_MEMORY; } Stream_Write_UINT16(priv->outputStream, EVENTID_SC_READY); - Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + 4); + Stream_Write_UINT32(priv->outputStream, RDPINPUT_HEADER_LENGTH + pduLen); Stream_Write_UINT32(priv->outputStream, version); if (version >= RDPINPUT_PROTOCOL_V300) Stream_Write_UINT32(priv->outputStream, features); diff --git a/channels/rdpgfx/client/rdpgfx_main.c b/channels/rdpgfx/client/rdpgfx_main.c index 67b3a7db4d8a..52e0e2a9e553 100644 --- a/channels/rdpgfx/client/rdpgfx_main.c +++ b/channels/rdpgfx/client/rdpgfx_main.c @@ -163,11 +163,12 @@ static UINT rdpgfx_send_caps_advertise_pdu(RdpgfxClientContext* context, static BOOL rdpgfx_is_capability_filtered(RDPGFX_PLUGIN* gfx, UINT32 caps) { const UINT32 filter = gfx->capsFilter; - const UINT32 capList[] = { - RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10, - RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, - RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106 - }; + const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, + RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101, + RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, + RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, + RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR, + RDPGFX_CAPVERSION_107 }; UINT32 x; for (x = 0; x < ARRAYSIZE(capList); x++) @@ -190,7 +191,7 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback) RdpgfxClientContext* context; RDPGFX_CAPSET* capsSet; RDPGFX_CAPSET capsSets[RDPGFX_NUMBER_CAPSETS] = { 0 }; - RDPGFX_CAPS_ADVERTISE_PDU pdu; + RDPGFX_CAPS_ADVERTISE_PDU pdu = { 0 }; if (!callback) return ERROR_BAD_ARGUMENTS; @@ -323,6 +324,25 @@ static UINT rdpgfx_send_supported_caps(RDPGFX_CHANNEL_CALLBACK* callback) capsSet->length = 0x4; capsSet->flags = caps10Flags; } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_106_ERR)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_106_ERR; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; + } + + if (!rdpgfx_is_capability_filtered(gfx, RDPGFX_CAPVERSION_107)) + { + capsSet = &capsSets[pdu.capsSetCount++]; + capsSet->version = RDPGFX_CAPVERSION_107; + capsSet->length = 0x4; + capsSet->flags = caps10Flags; +#if !defined(CAIRO_FOUND) && !defined(SWSCALE_FOUND) + capsSet->flags |= RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE; +#endif + } } return IFCALLRESULT(ERROR_BAD_CONFIGURATION, context->CapsAdvertise, context, &pdu); @@ -895,6 +915,8 @@ static UINT rdpgfx_recv_end_frame_pdu(RDPGFX_CHANNEL_CALLBACK* callback, wStream case RDPGFX_CAPVERSION_104: case RDPGFX_CAPVERSION_105: case RDPGFX_CAPVERSION_106: + case RDPGFX_CAPVERSION_106_ERR: + case RDPGFX_CAPVERSION_107: if (gfx->SendQoeAck) { RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU qoe; @@ -1043,7 +1065,9 @@ static UINT rdpgfx_recv_wire_to_surface_2_pdu(RDPGFX_CHANNEL_CALLBACK* callback, Stream_Read_UINT8(s, pdu.pixelFormat); /* pixelFormat (1 byte) */ Stream_Read_UINT32(s, pdu.bitmapDataLength); /* bitmapDataLength (4 bytes) */ pdu.bitmapData = Stream_Pointer(s); - Stream_Seek(s, pdu.bitmapDataLength); + if (!Stream_SafeSeek(s, pdu.bitmapDataLength)) + return ERROR_INVALID_DATA; + DEBUG_RDPGFX(gfx->log, "RecvWireToSurface2Pdu: surfaceId: %" PRIu16 " codecId: %s (0x%04" PRIX16 ") " "codecContextId: %" PRIu32 " pixelFormat: 0x%02" PRIX8 diff --git a/channels/rdpgfx/rdpgfx_common.c b/channels/rdpgfx/rdpgfx_common.c index 79bbc9e787d9..03fc97c1226b 100644 --- a/channels/rdpgfx/rdpgfx_common.c +++ b/channels/rdpgfx/rdpgfx_common.c @@ -1,4 +1,4 @@ -/** +/** * FreeRDP: A Remote Desktop Protocol Implementation * Graphics Pipeline Extension * @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -110,6 +111,9 @@ const char* rdpgfx_get_codec_id_string(UINT16 codecId) */ UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header) { + WINPR_ASSERT(s); + WINPR_ASSERT(header); + if (Stream_GetRemainingLength(s) < 8) { WLog_ERR(TAG, "calloc failed!"); @@ -119,6 +123,13 @@ UINT rdpgfx_read_header(wStream* s, RDPGFX_HEADER* header) Stream_Read_UINT16(s, header->cmdId); /* cmdId (2 bytes) */ Stream_Read_UINT16(s, header->flags); /* flags (2 bytes) */ Stream_Read_UINT32(s, header->pduLength); /* pduLength (4 bytes) */ + + if ((header->pduLength < 8) || (Stream_GetRemainingLength(s) < (header->pduLength - 8))) + { + WLog_ERR(TAG, "header->pduLength %u less than 8!", header->pduLength); + return ERROR_INVALID_DATA; + } + return CHANNEL_RC_OK; } diff --git a/channels/rdpgfx/server/rdpgfx_main.c b/channels/rdpgfx/server/rdpgfx_main.c index 306cc1c44841..283bb880d6de 100644 --- a/channels/rdpgfx/server/rdpgfx_main.c +++ b/channels/rdpgfx/server/rdpgfx_main.c @@ -1428,6 +1428,8 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context) PULONG pSessionId = NULL; DWORD BytesReturned = 0; priv->SessionId = WTS_CURRENT_SESSION; + UINT32 channelId; + BOOL status = TRUE; if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, (LPSTR*)&pSessionId, &BytesReturned) == FALSE) @@ -1447,6 +1449,15 @@ static BOOL rdpgfx_server_open(RdpgfxServerContext* context) return FALSE; } + channelId = WTSChannelGetIdByHandle(priv->rdpgfx_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + goto out_close; + } + /* Query for channel event handle */ if (!WTSVirtualChannelQuery(priv->rdpgfx_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) || diff --git a/channels/rdpsnd/client/alsa/rdpsnd_alsa.c b/channels/rdpsnd/client/alsa/rdpsnd_alsa.c index 95e7a79769fa..a903596aef95 100644 --- a/channels/rdpsnd/client/alsa/rdpsnd_alsa.c +++ b/channels/rdpsnd/client/alsa/rdpsnd_alsa.c @@ -213,10 +213,6 @@ static BOOL rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, const AUDIO_FORMA break; - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - break; - default: return FALSE; } diff --git a/channels/rdpsnd/client/mac/rdpsnd_mac.m b/channels/rdpsnd/client/mac/rdpsnd_mac.m index 8a01d9b73b3b..94ef150495c7 100644 --- a/channels/rdpsnd/client/mac/rdpsnd_mac.m +++ b/channels/rdpsnd/client/mac/rdpsnd_mac.m @@ -139,86 +139,99 @@ static void rdpsnd_mac_release(rdpsndMacPlugin *mac) static BOOL rdpsnd_mac_open(rdpsndDevicePlugin *device, const AUDIO_FORMAT *format, UINT32 latency) { - AudioDeviceID outputDeviceID; - UInt32 propertySize; - OSStatus err; - NSError *error; - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + @autoreleasepool + { + AudioDeviceID outputDeviceID; + UInt32 propertySize; + OSStatus err; + NSError *error; + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; + AudioObjectPropertyAddress propertyAddress = { + kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyScopeGlobal, +#if defined(MAC_OS_VERSION_12_0) + kAudioObjectPropertyElementMain +#else + kAudioObjectPropertyElementMaster +#endif + }; - if (mac->isOpen) - return TRUE; + if (mac->isOpen) + return TRUE; - if (!rdpsnd_mac_set_format(device, format, latency)) - return FALSE; + if (!rdpsnd_mac_set_format(device, format, latency)) + return FALSE; - propertySize = sizeof(outputDeviceID); - err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, - &propertySize, &outputDeviceID); - if (err) - { - WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err)); - return FALSE; - } + propertySize = sizeof(outputDeviceID); + err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, + &propertySize, &outputDeviceID); + if (err) + { + WLog_ERR(TAG, "AudioHardwareGetProperty: %s", FormatError(err)); + return FALSE; + } - mac->engine = [[AVAudioEngine alloc] init]; - if (!mac->engine) - return FALSE; + mac->engine = [[AVAudioEngine alloc] init]; + if (!mac->engine) + return FALSE; - err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit, - kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, - &outputDeviceID, sizeof(outputDeviceID)); - if (err) - { - rdpsnd_mac_release(mac); - WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err)); - return FALSE; - } + err = AudioUnitSetProperty(mac->engine.outputNode.audioUnit, + kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, + 0, &outputDeviceID, sizeof(outputDeviceID)); + if (err) + { + rdpsnd_mac_release(mac); + WLog_ERR(TAG, "AudioUnitSetProperty: %s", FormatError(err)); + return FALSE; + } - mac->player = [[AVAudioPlayerNode alloc] init]; - if (!mac->player) - { - rdpsnd_mac_release(mac); - WLog_ERR(TAG, "AVAudioPlayerNode::init() failed"); - return FALSE; - } + mac->player = [[AVAudioPlayerNode alloc] init]; + if (!mac->player) + { + rdpsnd_mac_release(mac); + WLog_ERR(TAG, "AVAudioPlayerNode::init() failed"); + return FALSE; + } - [mac->engine attachNode:mac->player]; + [mac->engine attachNode:mac->player]; - [mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil]; + [mac->engine connect:mac->player to:mac->engine.mainMixerNode format:nil]; - [mac->engine prepare]; + [mac->engine prepare]; - if (![mac->engine startAndReturnError:&error]) - { - device->Close(device); - WLog_ERR(TAG, "Failed to start audio player %s", [error.localizedDescription UTF8String]); - return FALSE; - } + if (![mac->engine startAndReturnError:&error]) + { + device->Close(device); + WLog_ERR(TAG, "Failed to start audio player %s", + [error.localizedDescription UTF8String]); + return FALSE; + } - mac->isOpen = TRUE; - return TRUE; + mac->isOpen = TRUE; + return TRUE; + } } static void rdpsnd_mac_close(rdpsndDevicePlugin *device) { - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - - if (mac->isPlaying) + @autoreleasepool { - [mac->player stop]; - mac->isPlaying = FALSE; - } + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - if (mac->isOpen) - { - [mac->engine stop]; - mac->isOpen = FALSE; - } + if (mac->isPlaying) + { + [mac->player stop]; + mac->isPlaying = FALSE; + } + + if (mac->isOpen) + { + [mac->engine stop]; + mac->isOpen = FALSE; + } - rdpsnd_mac_release(mac); + rdpsnd_mac_release(mac); + } } static void rdpsnd_mac_free(rdpsndDevicePlugin *device) @@ -246,108 +259,118 @@ static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin *device, const AUDIO_ static BOOL rdpsnd_mac_set_volume(rdpsndDevicePlugin *device, UINT32 value) { - Float32 fVolume; - UINT16 volumeLeft; - UINT16 volumeRight; - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; + @autoreleasepool + { + Float32 fVolume; + UINT16 volumeLeft; + UINT16 volumeRight; + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - if (!mac->player) - return FALSE; + if (!mac->player) + return FALSE; - volumeLeft = (value & 0xFFFF); - volumeRight = ((value >> 16) & 0xFFFF); - fVolume = ((float)volumeLeft) / 65535.0f; + volumeLeft = (value & 0xFFFF); + volumeRight = ((value >> 16) & 0xFFFF); + fVolume = ((float)volumeLeft) / 65535.0f; - mac->player.volume = fVolume; + mac->player.volume = fVolume; - return TRUE; + return TRUE; + } } static void rdpsnd_mac_start(rdpsndDevicePlugin *device) { - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - - if (!mac->isPlaying) + @autoreleasepool { - if (!mac->engine.isRunning) - { - NSError *error; + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - if (![mac->engine startAndReturnError:&error]) + if (!mac->isPlaying) + { + if (!mac->engine.isRunning) { - device->Close(device); - WLog_ERR(TAG, "Failed to start audio player %s", - [error.localizedDescription UTF8String]); - return; + NSError *error; + + if (![mac->engine startAndReturnError:&error]) + { + device->Close(device); + WLog_ERR(TAG, "Failed to start audio player %s", + [error.localizedDescription UTF8String]); + return; + } } - } - [mac->player play]; + [mac->player play]; - mac->isPlaying = TRUE; - mac->diff = 100; /* Initial latency, corrected after first sample is played. */ + mac->isPlaying = TRUE; + mac->diff = 100; /* Initial latency, corrected after first sample is played. */ + } } } static UINT rdpsnd_mac_play(rdpsndDevicePlugin *device, const BYTE *data, size_t size) { - rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; - AVAudioPCMBuffer *buffer; - AVAudioFormat *format; - float *const *db; - size_t pos, step, x; - AVAudioFrameCount count; - UINT64 start = GetTickCount64(); - - if (!mac->isOpen) - return 0; - - step = 2 * mac->format.nChannels; - - count = size / step; - format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 - sampleRate:mac->format.nSamplesPerSec - channels:mac->format.nChannels - interleaved:NO]; - if (!format) - { - WLog_WARN(TAG, "AVAudioFormat::init() failed"); - return 0; - } - buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count]; - if (!buffer) + @autoreleasepool { + rdpsndMacPlugin *mac = (rdpsndMacPlugin *)device; + AVAudioPCMBuffer *buffer; + AVAudioFormat *format; + float *const *db; + size_t pos, step, x; + AVAudioFrameCount count; + UINT64 start = GetTickCount64(); + + if (!mac->isOpen) + return 0; + + step = 2 * mac->format.nChannels; + + count = size / step; + format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32 + sampleRate:mac->format.nSamplesPerSec + channels:mac->format.nChannels + interleaved:NO]; + if (!format) + { + WLog_WARN(TAG, "AVAudioFormat::init() failed"); + return 0; + } + buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:format frameCapacity:count]; [format release]; - WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed"); - return 0; - } + if (!buffer) + { + WLog_WARN(TAG, "AVAudioPCMBuffer::init() failed"); + return 0; + } - buffer.frameLength = buffer.frameCapacity; - db = buffer.floatChannelData; + buffer.frameLength = buffer.frameCapacity; + db = buffer.floatChannelData; - for (pos = 0; pos < count; pos++) - { - const BYTE *d = &data[pos * step]; - for (x = 0; x < mac->format.nChannels; x++) + for (pos = 0; pos < count; pos++) { - const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f; - db[x][pos] = val; - d += sizeof(int16_t); + const BYTE *d = &data[pos * step]; + for (x = 0; x < mac->format.nChannels; x++) + { + const float val = (int16_t)((uint16_t)d[0] | ((uint16_t)d[1] << 8)) / 32768.0f; + db[x][pos] = val; + d += sizeof(int16_t); + } } - } - rdpsnd_mac_start(device); + rdpsnd_mac_start(device); - [mac->player scheduleBuffer:buffer - completionHandler:^{ - UINT64 stop = GetTickCount64(); - if (start > stop) - mac->diff = 0; - else - mac->diff = stop - start; - }]; + [mac->player scheduleBuffer:buffer + completionHandler:^{ + UINT64 stop = GetTickCount64(); + if (start > stop) + mac->diff = 0; + else + mac->diff = stop - start; + }]; + [buffer release]; - return mac->diff > UINT_MAX ? UINT_MAX : mac->diff; + return mac->diff > UINT_MAX ? UINT_MAX : mac->diff; + } } #ifdef BUILTIN_CHANNELS diff --git a/channels/rdpsnd/client/oss/rdpsnd_oss.c b/channels/rdpsnd/client/oss/rdpsnd_oss.c index 7a7937dad4e9..f8e15d2cb226 100644 --- a/channels/rdpsnd/client/oss/rdpsnd_oss.c +++ b/channels/rdpsnd/client/oss/rdpsnd_oss.c @@ -117,10 +117,6 @@ static BOOL rdpsnd_oss_format_supported(rdpsndDevicePlugin* device, const AUDIO_ break; - case WAVE_FORMAT_MULAW: - case WAVE_FORMAT_ALAW: - break; - default: return FALSE; } diff --git a/channels/rdpsnd/client/proxy/CMakeLists.txt b/channels/rdpsnd/client/proxy/CMakeLists.txt index 2d40750911b4..874d46a652a2 100644 --- a/channels/rdpsnd/client/proxy/CMakeLists.txt +++ b/channels/rdpsnd/client/proxy/CMakeLists.txt @@ -25,6 +25,9 @@ include_directories(..) add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "") +if (NOT BUILTIN_CHANNELS) + list(APPEND ${MODULE_PREFIX}_LIBS freerdp-client) +endif() list(APPEND ${MODULE_PREFIX}_LIBS freerdp) list(APPEND ${MODULE_PREFIX}_LIBS winpr) diff --git a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c index 7f3a6daf7897..2fd4d7d9e7ef 100644 --- a/channels/rdpsnd/client/pulse/rdpsnd_pulse.c +++ b/channels/rdpsnd/client/pulse/rdpsnd_pulse.c @@ -23,11 +23,15 @@ #include "config.h" #endif +#include + #include #include #include +#include #include +#include #include #include @@ -51,8 +55,39 @@ struct rdpsnd_pulse_plugin pa_stream* stream; UINT32 latency; UINT32 volume; + time_t reconnect_delay_seconds; + time_t reconnect_time; }; +static BOOL rdpsnd_check_pulse(rdpsndPulsePlugin* pulse, BOOL haveStream) +{ + BOOL rc = TRUE; + WINPR_ASSERT(pulse); + + if (!pulse->context) + { + WLog_WARN(TAG, "pulse->context=%p", pulse->context); + rc = FALSE; + } + + if (haveStream) + { + if (!pulse->stream) + { + WLog_WARN(TAG, "pulse->stream=%p", pulse->stream); + rc = FALSE; + } + } + + if (!pulse->mainloop) + { + WLog_WARN(TAG, "pulse->mainloop=%p", pulse->mainloop); + rc = FALSE; + } + + return rc; +} + static BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format); static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int eol, @@ -65,7 +100,8 @@ static void rdpsnd_pulse_get_sink_info(pa_context* c, const pa_sink_info* i, int ; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; - if (!pulse || !c || !i) + WINPR_ASSERT(c); + if (!rdpsnd_check_pulse(pulse, FALSE) || !i) return; for (x = 0; x < i->volume.channels; x++) @@ -97,6 +133,10 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd { pa_context_state_t state; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; + + WINPR_ASSERT(context); + WINPR_ASSERT(pulse); + state = pa_context_get_state(context); switch (state) @@ -106,6 +146,14 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd break; case PA_CONTEXT_FAILED: + // Destroy context now, create new one for next connection attempt + pa_context_unref(pulse->context); + pulse->context = NULL; + if (pulse->reconnect_delay_seconds >= 0) + pulse->reconnect_time = time(NULL) + pulse->reconnect_delay_seconds; + pa_threaded_mainloop_signal(pulse->mainloop, 0); + break; + case PA_CONTEXT_TERMINATED: pa_threaded_mainloop_signal(pulse->mainloop, 0); break; @@ -117,21 +165,17 @@ static void rdpsnd_pulse_context_state_callback(pa_context* context, void* userd static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device) { + BOOL rc; pa_operation* o; pa_context_state_t state; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; - if (!pulse->context) - return FALSE; - - if (pa_context_connect(pulse->context, NULL, 0, NULL)) - { + if (!rdpsnd_check_pulse(pulse, FALSE)) return FALSE; - } pa_threaded_mainloop_lock(pulse->mainloop); - if (pa_threaded_mainloop_start(pulse->mainloop) < 0) + if (pa_context_connect(pulse->context, NULL, 0, NULL) < 0) { pa_threaded_mainloop_unlock(pulse->mainloop); return FALSE; @@ -157,27 +201,35 @@ static BOOL rdpsnd_pulse_connect(rdpsndDevicePlugin* device) if (o) pa_operation_unref(o); - pa_threaded_mainloop_unlock(pulse->mainloop); - if (state == PA_CONTEXT_READY) { - return TRUE; + rc = TRUE; } else { pa_context_disconnect(pulse->context); - return FALSE; + rc = FALSE; } + + pa_threaded_mainloop_unlock(pulse->mainloop); + return rc; } static void rdpsnd_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata) { rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; + + if (!rdpsnd_check_pulse(pulse, TRUE)) + return; + pa_threaded_mainloop_signal(pulse->mainloop, 0); } static void rdpsnd_pulse_wait_for_operation(rdpsndPulsePlugin* pulse, pa_operation* operation) { + if (!rdpsnd_check_pulse(pulse, TRUE)) + return; + if (!operation) return; @@ -193,6 +245,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata { pa_stream_state_t state; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; + + WINPR_ASSERT(stream); + if (!rdpsnd_check_pulse(pulse, TRUE)) + return; + state = pa_stream_get_state(stream); switch (state) @@ -203,6 +260,8 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata case PA_STREAM_FAILED: case PA_STREAM_TERMINATED: + // Stream object is about to be destroyed, clean up our pointer + pulse->stream = NULL; pa_threaded_mainloop_signal(pulse->mainloop, 0); break; @@ -214,6 +273,11 @@ static void rdpsnd_pulse_stream_state_callback(pa_stream* stream, void* userdata static void rdpsnd_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata) { rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)userdata; + + WINPR_ASSERT(stream); + if (!rdpsnd_check_pulse(pulse, TRUE)) + return; + pa_threaded_mainloop_signal(pulse->mainloop, 0); } @@ -221,15 +285,20 @@ static void rdpsnd_pulse_close(rdpsndDevicePlugin* device) { rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; - if (!pulse->context || !pulse->stream) + WINPR_ASSERT(pulse); + + if (!rdpsnd_check_pulse(pulse, FALSE)) return; pa_threaded_mainloop_lock(pulse->mainloop); - rdpsnd_pulse_wait_for_operation( - pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse)); - pa_stream_disconnect(pulse->stream); - pa_stream_unref(pulse->stream); - pulse->stream = NULL; + if (pulse->stream) + { + rdpsnd_pulse_wait_for_operation( + pulse, pa_stream_drain(pulse->stream, rdpsnd_pulse_stream_success_callback, pulse)); + pa_stream_disconnect(pulse->stream); + pa_stream_unref(pulse->stream); + pulse->stream = NULL; + } pa_threaded_mainloop_unlock(pulse->mainloop); } @@ -237,7 +306,9 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F { pa_sample_spec sample_spec = { 0 }; - if (!pulse->context) + WINPR_ASSERT(format); + + if (!rdpsnd_check_pulse(pulse, FALSE)) return FALSE; if (!rdpsnd_pulse_format_supported(&pulse->device, format)) @@ -281,30 +352,52 @@ static BOOL rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, const AUDIO_F return TRUE; } -static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, - UINT32 latency) +static BOOL rdpsnd_pulse_context_connect(rdpsndDevicePlugin* device) { - pa_stream_state_t state; - pa_stream_flags_t flags; - pa_buffer_attr buffer_attr = { 0 }; - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX]; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; - if (!pulse->context || pulse->stream) - return TRUE; + pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp"); - if (!rdpsnd_pulse_set_format_spec(pulse, format)) + if (!pulse->context) return FALSE; - pulse->latency = latency; + pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse); + + if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse)) + return FALSE; + + return TRUE; +} + +static BOOL rdpsnd_pulse_open_stream(rdpsndDevicePlugin* device) +{ + pa_stream_state_t state; + pa_stream_flags_t flags; + pa_buffer_attr buffer_attr = { 0 }; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = { 0 }; + rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; if (pa_sample_spec_valid(&pulse->sample_spec) == 0) { pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec); - return TRUE; + return FALSE; } pa_threaded_mainloop_lock(pulse->mainloop); + if (!pulse->context) + { + pa_threaded_mainloop_unlock(pulse->mainloop); + if (pulse->reconnect_delay_seconds >= 0 && time(NULL) - pulse->reconnect_time >= 0) + rdpsnd_pulse_context_connect(device); + pa_threaded_mainloop_lock(pulse->mainloop); + } + + if (!rdpsnd_check_pulse(pulse, FALSE)) + { + pa_threaded_mainloop_unlock(pulse->mainloop); + return FALSE; + } + pulse->stream = pa_stream_new(pulse->context, "freerdp", &pulse->sample_spec, NULL); if (!pulse->stream) @@ -320,19 +413,22 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo if (pulse->latency > 0) { - buffer_attr.maxlength = pa_usec_to_bytes(pulse->latency * 2 * 1000, &pulse->sample_spec); + buffer_attr.maxlength = UINT32_MAX; buffer_attr.tlength = pa_usec_to_bytes(pulse->latency * 1000, &pulse->sample_spec); - buffer_attr.prebuf = (UINT32)-1; - buffer_attr.minreq = (UINT32)-1; - buffer_attr.fragsize = (UINT32)-1; + buffer_attr.prebuf = UINT32_MAX; + buffer_attr.minreq = UINT32_MAX; + buffer_attr.fragsize = UINT32_MAX; flags |= PA_STREAM_ADJUST_LATENCY; } if (pa_stream_connect_playback(pulse->stream, pulse->device_name, pulse->latency > 0 ? &buffer_attr : NULL, flags, NULL, NULL) < 0) { + WLog_ERR(TAG, "error connecting playback stream"); + pa_stream_unref(pulse->stream); + pulse->stream = NULL; pa_threaded_mainloop_unlock(pulse->mainloop); - return TRUE; + return FALSE; } for (;;) @@ -359,6 +455,24 @@ static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* fo return FALSE; } +static BOOL rdpsnd_pulse_open(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format, + UINT32 latency) +{ + rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; + + WINPR_ASSERT(format); + + if (!rdpsnd_check_pulse(pulse, FALSE)) + return TRUE; + + if (!rdpsnd_pulse_set_format_spec(pulse, format)) + return FALSE; + + pulse->latency = latency; + + return rdpsnd_pulse_open_stream(device); +} + static void rdpsnd_pulse_free(rdpsndDevicePlugin* device) { rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; @@ -369,9 +483,7 @@ static void rdpsnd_pulse_free(rdpsndDevicePlugin* device) rdpsnd_pulse_close(device); if (pulse->mainloop) - { pa_threaded_mainloop_stop(pulse->mainloop); - } if (pulse->context) { @@ -394,6 +506,7 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_ AUDIO_FORMAT* defaultFormat) { rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; + if (!pulse || !defaultFormat) return FALSE; @@ -415,6 +528,9 @@ static BOOL rdpsnd_pulse_default_format(rdpsndDevicePlugin* device, const AUDIO_ BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMAT* format) { + WINPR_ASSERT(device); + WINPR_ASSERT(format); + switch (format->wFormatTag) { case WAVE_FORMAT_PCM: @@ -427,15 +543,7 @@ BOOL rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, const AUDIO_FORMA break; - case WAVE_FORMAT_ALAW: - case WAVE_FORMAT_MULAW: - if (format->cbSize == 0 && (format->nSamplesPerSec <= PA_RATE_MAX) && - (format->wBitsPerSample == 8) && - (format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX)) - { - return TRUE; - } - + default: break; } @@ -447,39 +555,51 @@ static UINT32 rdpsnd_pulse_get_volume(rdpsndDevicePlugin* device) pa_operation* o; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; - if (!pulse) - return 0; - - if (!pulse->context || !pulse->mainloop) + if (!rdpsnd_check_pulse(pulse, FALSE)) return 0; pa_threaded_mainloop_lock(pulse->mainloop); o = pa_context_get_sink_info_by_index(pulse->context, 0, rdpsnd_pulse_get_sink_info, pulse); - pa_operation_unref(o); + if (o) + pa_operation_unref(o); pa_threaded_mainloop_unlock(pulse->mainloop); return pulse->volume; } +static void rdpsnd_set_volume_success_cb(pa_context* c, int success, void* userdata) +{ + rdpsndPulsePlugin* pulse = userdata; + + if (!rdpsnd_check_pulse(pulse, TRUE)) + return; + WINPR_ASSERT(c); + + WLog_INFO(TAG, "%s: %d", __FUNCTION__, success); +} + static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value) { - pa_cvolume cv; + pa_cvolume cv = { 0 }; pa_volume_t left; pa_volume_t right; pa_operation* operation; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; - if (!pulse->context || !pulse->stream) + if (!rdpsnd_check_pulse(pulse, TRUE)) + { + WLog_WARN(TAG, "%s called before pulse backend was initialized"); return FALSE; + } left = (pa_volume_t)(value & 0xFFFF); right = (pa_volume_t)((value >> 16) & 0xFFFF); pa_cvolume_init(&cv); cv.channels = 2; - cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF; - cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / 0xFFFF; + cv.values[0] = PA_VOLUME_MUTED + (left * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM; + cv.values[1] = PA_VOLUME_MUTED + (right * (PA_VOLUME_NORM - PA_VOLUME_MUTED)) / PA_VOLUME_NORM; pa_threaded_mainloop_lock(pulse->mainloop); operation = pa_context_set_sink_input_volume(pulse->context, pa_stream_get_index(pulse->stream), - &cv, NULL, NULL); + &cv, rdpsnd_set_volume_success_cb, pulse); if (operation) pa_operation_unref(operation); @@ -491,28 +611,38 @@ static BOOL rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, UINT32 value) static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size_t size) { size_t length; + void* pa_data; int status; pa_usec_t latency; int negative; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; - if (!pulse->stream || !data) + if (!data) return 0; pa_threaded_mainloop_lock(pulse->mainloop); + if (!rdpsnd_check_pulse(pulse, TRUE)) + { + pa_threaded_mainloop_unlock(pulse->mainloop); + // Discard this playback request and just attempt to reconnect the stream + WLog_DBG(TAG, "reconnecting playback stream"); + rdpsnd_pulse_open_stream(device); + return 0; + } + while (size > 0) { - while ((length = pa_stream_writable_size(pulse->stream)) == 0) - pa_threaded_mainloop_wait(pulse->mainloop); + length = size; + + status = pa_stream_begin_write(pulse->stream, &pa_data, &length); - if (length == (size_t)-1) + if (status < 0) break; - if (length > size) - length = size; + memcpy(pa_data, data, length); - status = pa_stream_write(pulse->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE); + status = pa_stream_write(pulse->stream, pa_data, length, NULL, 0LL, PA_SEEK_RELATIVE); if (status < 0) { @@ -530,22 +660,24 @@ static UINT rdpsnd_pulse_play(rdpsndDevicePlugin* device, const BYTE* data, size return latency / 1000; } -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV* args) { int status; DWORD flags; COMMAND_LINE_ARGUMENT_A* arg; rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; - COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, - "", NULL, NULL, -1, NULL, "device" }, - { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } }; + COMMAND_LINE_ARGUMENT_A rdpsnd_pulse_args[] = { + { "dev", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "device" }, + { "reconnect_delay_seconds", COMMAND_LINE_VALUE_REQUIRED, "", NULL, + NULL, -1, NULL, "reconnect_delay_seconds" }, + { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } + }; flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD; + + WINPR_ASSERT(pulse); + WINPR_ASSERT(args); + status = CommandLineParseArgumentsA(args->argc, args->argv, rdpsnd_pulse_args, flags, pulse, NULL, NULL); @@ -566,6 +698,15 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV if (!pulse->device_name) return ERROR_OUTOFMEMORY; } + CommandLineSwitchCase(arg, "reconnect_delay_seconds") + { + unsigned long val = strtoul(arg->Value, NULL, 0); + + if ((errno != 0) || (val > INT32_MAX)) + return ERROR_INVALID_DATA; + + pulse->reconnect_delay_seconds = val; + } CommandLineSwitchEnd(arg) } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL); @@ -578,16 +719,14 @@ static UINT rdpsnd_pulse_parse_addin_args(rdpsndDevicePlugin* device, ADDIN_ARGV #define freerdp_rdpsnd_client_subsystem_entry FREERDP_API freerdp_rdpsnd_client_subsystem_entry #endif -/** - * Function description - * - * @return 0 on success, otherwise a Win32 error code - */ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints) { ADDIN_ARGV* args; rdpsndPulsePlugin* pulse; UINT ret; + + WINPR_ASSERT(pEntryPoints); + pulse = (rdpsndPulsePlugin*)calloc(1, sizeof(rdpsndPulsePlugin)); if (!pulse) @@ -613,6 +752,8 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p goto error; } } + pulse->reconnect_delay_seconds = 5; + pulse->reconnect_time = time(NULL); ret = CHANNEL_RC_NO_MEMORY; pulse->mainloop = pa_threaded_mainloop_new(); @@ -620,15 +761,17 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p if (!pulse->mainloop) goto error; - pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp"); + pa_threaded_mainloop_lock(pulse->mainloop); - if (!pulse->context) - goto error; + if (pa_threaded_mainloop_start(pulse->mainloop) < 0) + { + pa_threaded_mainloop_unlock(pulse->mainloop); + return FALSE; + } - pa_context_set_state_callback(pulse->context, rdpsnd_pulse_context_state_callback, pulse); - ret = ERROR_INVALID_OPERATION; + pa_threaded_mainloop_unlock(pulse->mainloop); - if (!rdpsnd_pulse_connect((rdpsndDevicePlugin*)pulse)) + if (!rdpsnd_pulse_context_connect((rdpsndDevicePlugin*)pulse)) goto error; pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)pulse); diff --git a/channels/rdpsnd/client/rdpsnd_main.c b/channels/rdpsnd/client/rdpsnd_main.c index c6d193e2c927..9af5ead32f2c 100644 --- a/channels/rdpsnd/client/rdpsnd_main.c +++ b/channels/rdpsnd/client/rdpsnd_main.c @@ -126,6 +126,10 @@ struct rdpsnd_plugin HANDLE thread; wMessageQueue* queue; BOOL initialized; + + UINT16 wVersion; + UINT32 volume; + BOOL applyVolume; }; static const char* rdpsnd_is_dyn_str(BOOL dynamic) @@ -182,7 +186,7 @@ static void rdpsnd_select_supported_audio_formats(rdpsndPlugin* rdpsnd) rdpsnd->ClientFormats = audio_formats_new(rdpsnd->NumberOfServerFormats); - if (!rdpsnd->ClientFormats) + if (!rdpsnd->ClientFormats || !rdpsnd->device) return; for (index = 0; index < rdpsnd->NumberOfServerFormats; index++) @@ -213,6 +217,10 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd) UINT16 length; UINT32 dwVolume; UINT16 wNumberOfFormats; + + if (!rdpsnd->device || (!rdpsnd->dynamic && (rdpsnd->OpenHandle == 0))) + return CHANNEL_RC_INITIALIZATION_ERROR; + dwVolume = IFCALLRESULT(0, rdpsnd->device->GetVolume, rdpsnd->device); wNumberOfFormats = rdpsnd->NumberOfClientFormats; length = 4 + 20; @@ -264,9 +272,9 @@ static UINT rdpsnd_send_client_audio_formats(rdpsndPlugin* rdpsnd) static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* s) { UINT16 index; - UINT16 wVersion; UINT16 wNumberOfFormats; UINT ret = ERROR_BAD_LENGTH; + audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats); rdpsnd->NumberOfServerFormats = 0; rdpsnd->ServerFormats = NULL; @@ -281,7 +289,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* Stream_Seek_UINT16(s); /* wDGramPort */ Stream_Read_UINT16(s, wNumberOfFormats); Stream_Read_UINT8(s, rdpsnd->cBlockNo); /* cLastBlockConfirmed */ - Stream_Read_UINT16(s, wVersion); /* wVersion */ + Stream_Read_UINT16(s, rdpsnd->wVersion); /* wVersion */ Stream_Seek_UINT8(s); /* bPad */ rdpsnd->NumberOfServerFormats = wNumberOfFormats; @@ -308,7 +316,7 @@ static UINT rdpsnd_recv_server_audio_formats_pdu(rdpsndPlugin* rdpsnd, wStream* if (ret == CHANNEL_RC_OK) { - if (wVersion >= CHANNEL_VERSION_WIN_7) + if (rdpsnd->wVersion >= CHANNEL_VERSION_WIN_7) ret = rdpsnd_send_quality_mode_pdu(rdpsnd); } @@ -369,6 +377,20 @@ static UINT rdpsnd_recv_training_pdu(rdpsndPlugin* rdpsnd, wStream* s) return rdpsnd_send_training_confirm_pdu(rdpsnd, wTimeStamp, wPackSize); } +static BOOL rdpsnd_apply_volume(rdpsndPlugin* rdpsnd) +{ + WINPR_ASSERT(rdpsnd); + + if (rdpsnd->isOpen && rdpsnd->applyVolume && rdpsnd->device) + { + BOOL rc = IFCALLRESULT(TRUE, rdpsnd->device->SetVolume, rdpsnd->device, rdpsnd->volume); + if (!rc) + return FALSE; + rdpsnd->applyVolume = FALSE; + } + return TRUE; +} + static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo, const AUDIO_FORMAT* format) { @@ -417,7 +439,7 @@ static BOOL rdpsnd_ensure_device_is_open(rdpsndPlugin* rdpsnd, UINT32 wFormatNo, rdpsnd->totalPlaySize = 0; } - return TRUE; + return rdpsnd_apply_volume(rdpsnd); } /** @@ -578,6 +600,7 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) UINT64 end; UINT64 diffMS, ts; UINT latency = 0; + UINT error; if (Stream_GetRemainingLength(s) < size) return ERROR_BAD_LENGTH; @@ -585,6 +608,15 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) if (rdpsnd->wCurrentFormatNo >= rdpsnd->NumberOfClientFormats) return ERROR_INTERNAL_ERROR; + /* + * Send the first WaveConfirm PDU. The server side uses this to determine the + * network latency. + * See also [MS-RDPEA] 2.2.3.8 Wave Confirm PDU + */ + error = rdpsnd_send_wave_confirm_pdu(rdpsnd, rdpsnd->wTimeStamp, rdpsnd->cBlockNo); + if (error) + return error; + data = Stream_Pointer(s); format = &rdpsnd->ClientFormats[rdpsnd->wCurrentFormatNo]; WLog_Print(rdpsnd->log, WLOG_DEBUG, @@ -617,10 +649,11 @@ static UINT rdpsnd_treat_wave(rdpsndPlugin* rdpsnd, wStream* s, size_t size) diffMS = end - rdpsnd->wArrivalTime + latency; ts = (rdpsnd->wTimeStamp + diffMS) % UINT16_MAX; - /* Don't send wave confirm PDU if on dynamic channel */ - if (rdpsnd->dynamic) - return CHANNEL_RC_OK; - + /* + * Send the second WaveConfirm PDU. With the first WaveConfirm PDU, + * the server side uses this second WaveConfirm PDU to determine the actual + * render latency. + */ return rdpsnd_send_wave_confirm_pdu(rdpsnd, (UINT16)ts, rdpsnd->cBlockNo); } @@ -695,7 +728,7 @@ static void rdpsnd_recv_close_pdu(rdpsndPlugin* rdpsnd) */ static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s) { - BOOL rc; + BOOL rc = TRUE; UINT32 dwVolume; if (Stream_GetRemainingLength(s) < 4) @@ -704,7 +737,10 @@ static UINT rdpsnd_recv_volume_pdu(rdpsndPlugin* rdpsnd, wStream* s) Stream_Read_UINT32(s, dwVolume); WLog_Print(rdpsnd->log, WLOG_DEBUG, "%s Volume: 0x%08" PRIX32 "", rdpsnd_is_dyn_str(rdpsnd->dynamic), dwVolume); - rc = IFCALLRESULT(FALSE, rdpsnd->device->SetVolume, rdpsnd->device, dwVolume); + + rdpsnd->volume = dwVolume; + rdpsnd->applyVolume = TRUE; + rc = rdpsnd_apply_volume(rdpsnd); if (!rc) { @@ -1182,38 +1218,72 @@ static UINT rdpsnd_virtual_channel_event_connected(rdpsndPlugin* rdpsnd, LPVOID UINT32 dataLength) { UINT32 status; + DWORD opened = 0; WINPR_UNUSED(pData); WINPR_UNUSED(dataLength); status = rdpsnd->channelEntryPoints.pVirtualChannelOpenEx( - rdpsnd->InitHandle, &rdpsnd->OpenHandle, rdpsnd->channelDef.name, - rdpsnd_virtual_channel_open_event_ex); + rdpsnd->InitHandle, &opened, rdpsnd->channelDef.name, rdpsnd_virtual_channel_open_event_ex); if (status != CHANNEL_RC_OK) { WLog_ERR(TAG, "%s pVirtualChannelOpenEx failed with %s [%08" PRIX32 "]", rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(status), status); - return status; - } - - rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE); - - if (!rdpsnd->dsp_context) goto fail; + } - rdpsnd->pool = StreamPool_New(TRUE, 4096); - - if (!rdpsnd->pool) + if (rdpsnd_process_connect(rdpsnd) != CHANNEL_RC_OK) goto fail; - return rdpsnd_process_connect(rdpsnd); + rdpsnd->OpenHandle = opened; + return CHANNEL_RC_OK; fail: - freerdp_dsp_context_free(rdpsnd->dsp_context); - StreamPool_Free(rdpsnd->pool); + if (opened != 0) + rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened); return CHANNEL_RC_NO_MEMORY; } +static void rdpsnd_terminate_thread(rdpsndPlugin* rdpsnd) +{ + if (!rdpsnd) + return; + + if (rdpsnd->queue) + MessageQueue_PostQuit(rdpsnd->queue, 0); + + if (rdpsnd->thread) + { + WaitForSingleObject(rdpsnd->thread, INFINITE); + CloseHandle(rdpsnd->thread); + } + + MessageQueue_Free(rdpsnd->queue); + rdpsnd->thread = NULL; + rdpsnd->queue = NULL; +} + +static void cleanup_internals(rdpsndPlugin* rdpsnd) +{ + if (!rdpsnd) + return; + + rdpsnd_terminate_thread(rdpsnd); + + if (rdpsnd->pool) + StreamPool_Return(rdpsnd->pool, rdpsnd->data_in); + + audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats); + audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats); + + rdpsnd->NumberOfClientFormats = 0; + rdpsnd->ClientFormats = NULL; + rdpsnd->NumberOfServerFormats = 0; + rdpsnd->ServerFormats = NULL; + + rdpsnd->data_in = NULL; +} + /** * Function description * @@ -1223,33 +1293,25 @@ static UINT rdpsnd_virtual_channel_event_disconnected(rdpsndPlugin* rdpsnd) { UINT error; - if (rdpsnd->OpenHandle == 0) - return CHANNEL_RC_OK; + if (rdpsnd->OpenHandle != 0) + { + DWORD opened = rdpsnd->OpenHandle; + rdpsnd->OpenHandle = 0; - if (rdpsnd->device) - IFCALL(rdpsnd->device->Close, rdpsnd->device); + if (rdpsnd->device) + IFCALL(rdpsnd->device->Close, rdpsnd->device); - error = - rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, rdpsnd->OpenHandle); + error = rdpsnd->channelEntryPoints.pVirtualChannelCloseEx(rdpsnd->InitHandle, opened); - if (CHANNEL_RC_OK != error) - { - WLog_ERR(TAG, "%s pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", - rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(error), error); - return error; + if (CHANNEL_RC_OK != error) + { + WLog_ERR(TAG, "%s pVirtualChannelCloseEx failed with %s [%08" PRIX32 "]", + rdpsnd_is_dyn_str(rdpsnd->dynamic), WTSErrorToString(error), error); + return error; + } } - rdpsnd->OpenHandle = 0; - freerdp_dsp_context_free(rdpsnd->dsp_context); - StreamPool_Return(rdpsnd->pool, rdpsnd->data_in); - StreamPool_Free(rdpsnd->pool); - - audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats); - rdpsnd->NumberOfClientFormats = 0; - rdpsnd->ClientFormats = NULL; - audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats); - rdpsnd->NumberOfServerFormats = 0; - rdpsnd->ServerFormats = NULL; + cleanup_internals(rdpsnd); if (rdpsnd->device) { @@ -1266,6 +1328,36 @@ static void _queue_free(void* obj) Stream_Release(s); } +static void free_internals(rdpsndPlugin* rdpsnd) +{ + if (!rdpsnd) + return; + + freerdp_dsp_context_free(rdpsnd->dsp_context); + StreamPool_Free(rdpsnd->pool); + rdpsnd->pool = NULL; + rdpsnd->dsp_context = NULL; +} + +static BOOL allocate_internals(rdpsndPlugin* rdpsnd) +{ + if (!rdpsnd->pool) + { + rdpsnd->pool = StreamPool_New(TRUE, 4096); + if (!rdpsnd->pool) + return FALSE; + } + + if (!rdpsnd->dsp_context) + { + rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE); + if (!rdpsnd->dsp_context) + return FALSE; + } + + return TRUE; +} + static DWORD WINAPI play_thread(LPVOID arg) { UINT error = CHANNEL_RC_OK; @@ -1311,24 +1403,23 @@ static UINT rdpsnd_virtual_channel_event_initialized(rdpsndPlugin* rdpsnd) if (!rdpsnd->queue) return CHANNEL_RC_NO_MEMORY; + if (!allocate_internals(rdpsnd)) + return CHANNEL_RC_NO_MEMORY; + rdpsnd->thread = CreateThread(NULL, 0, play_thread, rdpsnd, 0, NULL); if (!rdpsnd->thread) return CHANNEL_RC_INITIALIZATION_ERROR; + return CHANNEL_RC_OK; } -static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd) +void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd) { if (rdpsnd) { - MessageQueue_PostQuit(rdpsnd->queue, 0); - if (rdpsnd->thread) - { - WaitForSingleObject(rdpsnd->thread, INFINITE); - CloseHandle(rdpsnd->thread); - } - MessageQueue_Free(rdpsnd->queue); + rdpsnd_terminate_thread(rdpsnd); + free_internals(rdpsnd); audio_formats_free(rdpsnd->fixed_format, 1); free(rdpsnd->subsystem); free(rdpsnd->device_name); @@ -1370,6 +1461,7 @@ static VOID VCAPITYPE rdpsnd_virtual_channel_init_event_ex(LPVOID lpUserParam, L case CHANNEL_EVENT_TERMINATED: rdpsnd_virtual_channel_event_terminated(plugin); + plugin = NULL; break; case CHANNEL_EVENT_ATTACHED: @@ -1401,6 +1493,27 @@ rdpContext* freerdp_rdpsnd_get_context(rdpsndPlugin* plugin) return plugin->rdpcontext; } +static rdpsndPlugin* allocatePlugin(void) +{ + rdpsndPlugin* rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin)); + if (!rdpsnd) + goto fail; + + rdpsnd->fixed_format = audio_format_new(); + if (!rdpsnd->fixed_format) + goto fail; + rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client"); + if (!rdpsnd->log) + goto fail; + + rdpsnd->attached = TRUE; + return rdpsnd; + +fail: + if (rdpsnd) + audio_format_free(rdpsnd->fixed_format); + return NULL; +} /* rdpsnd is always built-in */ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, PVOID pInitHandle) { @@ -1411,12 +1524,11 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, if (!pEntryPoints) return FALSE; - rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin)); + rdpsnd = allocatePlugin(); if (!rdpsnd) return FALSE; - rdpsnd->attached = TRUE; rdpsnd->channelDef.options = CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP; sprintf_s(rdpsnd->channelDef.name, ARRAYSIZE(rdpsnd->channelDef.name), "rdpsnd"); pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints; @@ -1427,15 +1539,6 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, rdpsnd->rdpcontext = pEntryPointsEx->context; } - rdpsnd->fixed_format = audio_format_new(); - - if (!rdpsnd->fixed_format) - { - free(rdpsnd); - return FALSE; - } - - rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client"); CopyMemory(&(rdpsnd->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)); rdpsnd->InitHandle = pInitHandle; @@ -1447,7 +1550,7 @@ BOOL VCAPITYPE rdpsnd_VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints, { WLog_ERR(TAG, "%s pVirtualChannelInitEx failed with %s [%08" PRIX32 "]", rdpsnd_is_dyn_str(FALSE), WTSErrorToString(rc), rc); - free(rdpsnd); + rdpsnd_virtual_channel_event_terminated(rdpsnd); return FALSE; } @@ -1459,19 +1562,10 @@ static UINT rdpsnd_on_open(IWTSVirtualChannelCallback* pChannelCallback) RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback; rdpsndPlugin* rdpsnd = (rdpsndPlugin*)callback->plugin; - rdpsnd->dsp_context = freerdp_dsp_context_new(FALSE); - if (!rdpsnd->dsp_context) - goto fail; - - rdpsnd->pool = StreamPool_New(TRUE, 4096); - if (!rdpsnd->pool) - goto fail; + if (!allocate_internals(rdpsnd)) + return ERROR_OUTOFMEMORY; return rdpsnd_process_connect(rdpsnd); -fail: - freerdp_dsp_context_free(rdpsnd->dsp_context); - StreamPool_Free(rdpsnd->pool); - return CHANNEL_RC_NO_MEMORY; } static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data) @@ -1479,7 +1573,9 @@ static UINT rdpsnd_on_data_received(IWTSVirtualChannelCallback* pChannelCallback RDPSND_CHANNEL_CALLBACK* callback = (RDPSND_CHANNEL_CALLBACK*)pChannelCallback; rdpsndPlugin* plugin; wStream* copy; - size_t len = Stream_GetRemainingLength(data); + size_t len; + + len = Stream_GetRemainingLength(data); if (!callback || !callback->plugin) return ERROR_INVALID_PARAMETER; @@ -1508,22 +1604,16 @@ static UINT rdpsnd_on_close(IWTSVirtualChannelCallback* pChannelCallback) if (rdpsnd->device) IFCALL(rdpsnd->device->Close, rdpsnd->device); - freerdp_dsp_context_free(rdpsnd->dsp_context); - StreamPool_Return(rdpsnd->pool, rdpsnd->data_in); - StreamPool_Free(rdpsnd->pool); - audio_formats_free(rdpsnd->ClientFormats, rdpsnd->NumberOfClientFormats); - rdpsnd->NumberOfClientFormats = 0; - rdpsnd->ClientFormats = NULL; - audio_formats_free(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats); - rdpsnd->NumberOfServerFormats = 0; - rdpsnd->ServerFormats = NULL; + cleanup_internals(rdpsnd); + if (rdpsnd->device) { IFCALL(rdpsnd->device->Free, rdpsnd->device); rdpsnd->device = NULL; } + free_internals(rdpsnd); free(pChannelCallback); return CHANNEL_RC_OK; } @@ -1622,7 +1712,7 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) if (!rdpsnd) { - rdpsnd = (rdpsndPlugin*)calloc(1, sizeof(rdpsndPlugin)); + rdpsnd = allocatePlugin(); if (!rdpsnd) { WLog_ERR(TAG, "%s calloc failed!", rdpsnd_is_dyn_str(TRUE)); @@ -1633,14 +1723,10 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) rdpsnd->iface.Connected = NULL; rdpsnd->iface.Disconnected = NULL; rdpsnd->iface.Terminated = rdpsnd_plugin_terminated; - rdpsnd->attached = TRUE; rdpsnd->dynamic = TRUE; - rdpsnd->fixed_format = audio_format_new(); - if (!rdpsnd->fixed_format) - goto fail; - rdpsnd->log = WLog_Get("com.freerdp.channels.rdpsnd.client"); - rdpsnd->channelEntryPoints.pExtendedData = pEntryPoints->GetPluginData(pEntryPoints); + /* user data pointer is not const, cast to avoid warning. */ + rdpsnd->channelEntryPoints.pExtendedData = (void*)pEntryPoints->GetPluginData(pEntryPoints); error = pEntryPoints->RegisterPlugin(pEntryPoints, "rdpsnd", &rdpsnd->iface); } @@ -1650,8 +1736,5 @@ UINT rdpsnd_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) return CHANNEL_RC_BAD_CHANNEL; } - return error; -fail: - rdpsnd_plugin_terminated(&rdpsnd->iface); return error; } diff --git a/channels/rdpsnd/server/rdpsnd_main.c b/channels/rdpsnd/server/rdpsnd_main.c index be0ca149f33b..a26c3cff51b4 100644 --- a/channels/rdpsnd/server/rdpsnd_main.c +++ b/channels/rdpsnd/server/rdpsnd_main.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -36,17 +37,33 @@ #include "rdpsnd_common.h" #include "rdpsnd_main.h" +static wStream* rdpsnd_server_get_buffer(RdpsndServerContext* context) +{ + wStream* s; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + s = context->priv->rdpsnd_pdu; + Stream_SetPosition(s, 0); + return s; +} + /** - * Function description + * Send Server Audio Formats and Version PDU (2.2.2.1) * * @return 0 on success, otherwise a Win32 error code */ -static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) +static UINT rdpsnd_server_send_formats(RdpsndServerContext* context) { + wStream* s = rdpsnd_server_get_buffer(context); size_t pos; UINT16 i; BOOL status = FALSE; ULONG written; + + if (!Stream_EnsureRemainingCapacity(s, 24)) + return ERROR_OUTOFMEMORY; + Stream_Write_UINT8(s, SNDC_FORMATS); Stream_Write_UINT8(s, 0); Stream_Seek_UINT16(s); @@ -61,12 +78,9 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) for (i = 0; i < context->num_server_formats; i++) { - AUDIO_FORMAT format = context->server_formats[i]; - // TODO: Eliminate this!!! - format.nAvgBytesPerSec = - format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8; + const AUDIO_FORMAT* format = &context->server_formats[i]; - if (!audio_format_write(s, &format)) + if (!audio_format_write(s, format)) goto fail; } @@ -74,6 +88,8 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) Stream_SetPosition(s, 2); Stream_Write_UINT16(s, pos - 4); Stream_SetPosition(s, pos); + + WINPR_ASSERT(context->priv); status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), Stream_GetPosition(s), &written); Stream_SetPosition(s, 0); @@ -82,7 +98,7 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s) } /** - * Function description + * Read Wave Confirm PDU (2.2.3.8) and handle callback * * @return 0 on success, otherwise a Win32 error code */ @@ -92,11 +108,10 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream BYTE confirmBlockNum; UINT error = CHANNEL_RC_OK; - if (Stream_GetRemainingLength(s) < 4) - { - WLog_ERR(TAG, "not enough data in stream!"); + WINPR_ASSERT(context); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) return ERROR_INVALID_DATA; - } Stream_Read_UINT16(s, timestamp); Stream_Read_UINT8(s, confirmBlockNum); @@ -110,13 +125,39 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream } /** - * Function description + * Read Training Confirm PDU (2.2.3.2) and handle callback + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpsnd_server_recv_trainingconfirm(RdpsndServerContext* context, wStream* s) +{ + UINT16 timestamp; + UINT16 packsize; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, timestamp); + Stream_Read_UINT16(s, packsize); + + IFCALLRET(context->TrainingConfirm, error, context, timestamp, packsize); + if (error) + WLog_ERR(TAG, "context->TrainingConfirm failed with error %" PRIu32 "", error); + + return error; +} + +/** + * Read Quality Mode PDU (2.2.2.3) * * @return 0 on success, otherwise a Win32 error code */ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s) { - UINT16 quality; + WINPR_ASSERT(context); if (Stream_GetRemainingLength(s) < 4) { @@ -124,34 +165,34 @@ static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStrea return ERROR_INVALID_DATA; } - Stream_Read_UINT16(s, quality); - Stream_Seek_UINT16(s); // reserved - WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", quality); + Stream_Read_UINT16(s, context->qualityMode); /* wQualityMode */ + Stream_Seek_UINT16(s); /* Reserved */ + + WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", context->qualityMode); + return CHANNEL_RC_OK; } /** - * Function description + * Read Client Audio Formats and Version PDU (2.2.2.2) * * @return 0 on success, otherwise a Win32 error code */ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) { UINT16 i, num_known_format = 0; - UINT32 flags, vol, pitch; UINT16 udpPort; BYTE lastblock; UINT error = CHANNEL_RC_OK; - if (Stream_GetRemainingLength(s) < 20) - { - WLog_ERR(TAG, "not enough data in stream!"); + WINPR_ASSERT(context); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 20)) return ERROR_INVALID_DATA; - } - Stream_Read_UINT32(s, flags); /* dwFlags */ - Stream_Read_UINT32(s, vol); /* dwVolume */ - Stream_Read_UINT32(s, pitch); /* dwPitch */ + Stream_Read_UINT32(s, context->capsFlags); /* dwFlags */ + Stream_Read_UINT32(s, context->initialVolume); /* dwVolume */ + Stream_Read_UINT32(s, context->initialPitch); /* dwPitch */ Stream_Read_UINT16(s, udpPort); /* wDGramPort */ Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */ Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */ @@ -159,11 +200,8 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) Stream_Seek_UINT8(s); /* bPad */ /* this check is only a guess as cbSize can influence the size of a format record */ - if (Stream_GetRemainingLength(s) < context->num_client_formats * 18) - { - WLog_ERR(TAG, "not enough data in stream!"); + if (!Stream_CheckAndLogRequiredLength(TAG, s, 18ull * context->num_client_formats)) return ERROR_INVALID_DATA; - } if (!context->num_client_formats) { @@ -181,24 +219,26 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) for (i = 0; i < context->num_client_formats; i++) { - if (Stream_GetRemainingLength(s) < 18) + AUDIO_FORMAT* format = &context->client_formats[i]; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 18)) { WLog_ERR(TAG, "not enough data in stream!"); error = ERROR_INVALID_DATA; goto out_free; } - Stream_Read_UINT16(s, context->client_formats[i].wFormatTag); - Stream_Read_UINT16(s, context->client_formats[i].nChannels); - Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec); - Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec); - Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign); - Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample); - Stream_Read_UINT16(s, context->client_formats[i].cbSize); + Stream_Read_UINT16(s, format->wFormatTag); + Stream_Read_UINT16(s, format->nChannels); + Stream_Read_UINT32(s, format->nSamplesPerSec); + Stream_Read_UINT32(s, format->nAvgBytesPerSec); + Stream_Read_UINT16(s, format->nBlockAlign); + Stream_Read_UINT16(s, format->wBitsPerSample); + Stream_Read_UINT16(s, format->cbSize); - if (context->client_formats[i].cbSize > 0) + if (format->cbSize > 0) { - if (!Stream_SafeSeek(s, context->client_formats[i].cbSize)) + if (!Stream_SafeSeek(s, format->cbSize)) { WLog_ERR(TAG, "Stream_SafeSeek failed!"); error = ERROR_INTERNAL_ERROR; @@ -206,7 +246,7 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) } } - if (context->client_formats[i].wFormatTag != 0) + if (format->wFormatTag != 0) { // lets call this a known format // TODO: actually look through our own list of known formats @@ -228,15 +268,19 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) static DWORD WINAPI rdpsnd_server_thread(LPVOID arg) { - DWORD nCount, status; - HANDLE events[8]; - RdpsndServerContext* context; + DWORD nCount = 0, status; + HANDLE events[2] = { 0 }; + RdpsndServerContext* context = (RdpsndServerContext*)arg; UINT error = CHANNEL_RC_OK; - context = (RdpsndServerContext*)arg; - nCount = 0; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + events[nCount++] = context->priv->channelEvent; events[nCount++] = context->priv->StopEvent; + WINPR_ASSERT(nCount <= ARRAYSIZE(events)); + while (TRUE) { status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); @@ -281,6 +325,9 @@ static DWORD WINAPI rdpsnd_server_thread(LPVOID arg) */ static UINT rdpsnd_server_initialize(RdpsndServerContext* context, BOOL ownThread) { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + context->priv->ownThread = ownThread; return context->Start(context); } @@ -297,6 +344,9 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context, UINT16 cli AUDIO_FORMAT* format; UINT error = CHANNEL_RC_OK; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + if ((client_format_index >= context->num_client_formats) || (!context->src_format)) { WLog_ERR(TAG, "index %d is not correct.", client_format_index); @@ -371,6 +421,51 @@ static UINT rdpsnd_server_select_format(RdpsndServerContext* context, UINT16 cli return error; } +/** + * Send Training PDU (2.2.3.1) + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpsnd_server_training(RdpsndServerContext* context, UINT16 timestamp, UINT16 packsize, + BYTE* data) +{ + size_t end = 0; + ULONG written; + BOOL status; + wStream* s = rdpsnd_server_get_buffer(context); + + if (!Stream_EnsureRemainingCapacity(s, 8)) + return ERROR_INTERNAL_ERROR; + + Stream_Write_UINT8(s, SNDC_TRAINING); + Stream_Write_UINT8(s, 0); + Stream_Seek_UINT16(s); + Stream_Write_UINT16(s, timestamp); + Stream_Write_UINT16(s, packsize); + + if (packsize > 0) + { + if (!Stream_EnsureRemainingCapacity(s, packsize)) + { + Stream_SetPosition(s, 0); + return ERROR_INTERNAL_ERROR; + } + + Stream_Write(s, data, packsize); + } + + end = Stream_GetPosition(s); + Stream_SetPosition(s, 2); + Stream_Write_UINT16(s, end - 4); + + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end, + &written); + + Stream_SetPosition(s, 0); + + return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; +} + static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment) { size_t size; @@ -404,15 +499,21 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi const BYTE* src; AUDIO_FORMAT* format; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + wStream* s = rdpsnd_server_get_buffer(context); - if (context->selected_client_format >= context->num_client_formats) + if (context->selected_client_format > context->num_client_formats) return ERROR_INTERNAL_ERROR; + WINPR_ASSERT(context->client_formats); + format = &context->client_formats[context->selected_client_format]; /* WaveInfo PDU */ Stream_SetPosition(s, 0); + + if (!Stream_EnsureRemainingCapacity(s, 16)) + return ERROR_OUTOFMEMORY; + Stream_Write_UINT8(s, SNDC_WAVE); /* msgType */ Stream_Write_UINT8(s, 0); /* bPad */ Stream_Write_UINT16(s, 0); /* BodySize */ @@ -422,7 +523,7 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi Stream_Seek(s, 3); /* bPad */ start = Stream_GetPosition(s); src = context->priv->out_buffer; - length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame * 1ULL; + length = 1ull * context->priv->out_pending_frames * context->priv->src_bytes_per_frame; if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s)) return ERROR_INTERNAL_ERROR; @@ -436,7 +537,6 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi Stream_SetPosition(s, 2); Stream_Write_UINT16(s, end - start + 8); Stream_SetPosition(s, end); - context->block_no = (context->block_no + 1) % 256; if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), start + 4, &written)) @@ -464,6 +564,8 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi error = ERROR_INTERNAL_ERROR; } + context->block_no = (context->block_no + 1) % 256; + out: Stream_SetPosition(s, 0); context->priv->out_pending_frames = 0; @@ -476,59 +578,79 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi * * @return 0 on success, otherwise a Win32 error code */ -static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wTimestamp) +static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 formatNo, + const BYTE* data, size_t size, BOOL encoded, + UINT16 timestamp, UINT32 audioTimeStamp) { - size_t length; size_t end = 0; - const BYTE* src; - AUDIO_FORMAT* format; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + BOOL status; + wStream* s = rdpsnd_server_get_buffer(context); - if (context->selected_client_format >= context->num_client_formats) - return ERROR_INTERNAL_ERROR; + if (!Stream_EnsureRemainingCapacity(s, 16)) + { + error = ERROR_INTERNAL_ERROR; + goto out; + } - format = &context->client_formats[context->selected_client_format]; - /* WaveInfo PDU */ - Stream_SetPosition(s, 0); - Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */ - Stream_Write_UINT8(s, 0); /* bPad */ - Stream_Write_UINT16(s, 0); /* BodySize */ - Stream_Write_UINT16(s, wTimestamp); /* wTimeStamp */ - Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */ - Stream_Write_UINT8(s, context->block_no); /* cBlockNo */ - Stream_Seek(s, 3); /* bPad */ - Stream_Write_UINT32(s, wTimestamp); /* dwAudioTimeStamp */ - src = context->priv->out_buffer; - length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame; + /* Wave2 PDU */ + Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT16(s, 0); /* BodySize */ + Stream_Write_UINT16(s, timestamp); /* wTimeStamp */ + Stream_Write_UINT16(s, formatNo); /* wFormatNo */ + Stream_Write_UINT8(s, context->block_no); /* cBlockNo */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT8(s, 0); /* bPad */ + Stream_Write_UINT32(s, audioTimeStamp); /* dwAudioTimeStamp */ + + if (encoded) + { + if (!Stream_EnsureRemainingCapacity(s, size)) + { + error = ERROR_INTERNAL_ERROR; + goto out; + } - if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s)) - error = ERROR_INTERNAL_ERROR; + Stream_Write(s, data, size); + } else { - BOOL rc; - - /* Set stream size */ - if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign)) - return ERROR_INTERNAL_ERROR; + AUDIO_FORMAT* format; - end = Stream_GetPosition(s); - Stream_SetPosition(s, 2); - Stream_Write_UINT16(s, end - 4); - context->block_no = (context->block_no + 1) % 256; - rc = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end, - &written); + if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, data, size, s)) + { + error = ERROR_INTERNAL_ERROR; + goto out; + } - if (!rc || (end != written)) + format = &context->client_formats[formatNo]; + if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign)) { - WLog_ERR(TAG, - "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32, - end, written); error = ERROR_INTERNAL_ERROR; + goto out; } } + end = Stream_GetPosition(s); + Stream_SetPosition(s, 2); + Stream_Write_UINT16(s, end - 4); + + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end, + &written); + + if (!status || (end != written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32, + end, written); + error = ERROR_INTERNAL_ERROR; + } + + context->block_no = (context->block_no + 1) % 256; + +out: Stream_SetPosition(s, 0); context->priv->out_pending_frames = 0; return error; @@ -537,8 +659,21 @@ static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wT /* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */ static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp) { + const BYTE* src; + size_t length; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + if (context->selected_client_format >= context->num_client_formats) + return ERROR_INTERNAL_ERROR; + + src = context->priv->out_buffer; + length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame; + if (context->clientVersion >= CHANNEL_VERSION_WIN_8) - return rdpsnd_server_send_wave2_pdu(context, wTimestamp); + return rdpsnd_server_send_wave2_pdu(context, context->selected_client_format, src, length, + FALSE, wTimestamp, wTimestamp); else return rdpsnd_server_send_wave_pdu(context, wTimestamp); } @@ -551,9 +686,11 @@ static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wT static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* buf, int nframes, UINT16 wTimestamp) { - int cframes; - int cframesize; UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + EnterCriticalSection(&context->priv->lock); if (context->selected_client_format >= context->num_client_formats) @@ -566,12 +703,13 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* while (nframes > 0) { - cframes = MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames); - cframesize = cframes * context->priv->src_bytes_per_frame; + const size_t cframes = + MIN(nframes, context->priv->out_frames - context->priv->out_pending_frames); + size_t cframesize = cframes * context->priv->src_bytes_per_frame; CopyMemory(context->priv->out_buffer + (context->priv->out_pending_frames * context->priv->src_bytes_per_frame), buf, cframesize); - buf = (BYTE*)buf + cframesize; + buf = (const BYTE*)buf + cframesize; nframes -= cframes; context->priv->out_pending_frames += cframes; @@ -590,6 +728,33 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* return error; } +/** + * Send encoded audio samples using a Wave2 PDU. + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT rdpsnd_server_send_samples2(RdpsndServerContext* context, UINT16 formatNo, + const void* buf, size_t size, UINT16 timestamp, + UINT32 audioTimeStamp) +{ + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + if (context->clientVersion < CHANNEL_VERSION_WIN_8) + return ERROR_INTERNAL_ERROR; + + EnterCriticalSection(&context->priv->lock); + + error = + rdpsnd_server_send_wave2_pdu(context, formatNo, buf, size, TRUE, timestamp, audioTimeStamp); + + LeaveCriticalSection(&context->priv->lock); + + return error; +} + /** * Function description * @@ -597,21 +762,23 @@ static UINT rdpsnd_server_send_samples(RdpsndServerContext* context, const void* */ static UINT rdpsnd_server_set_volume(RdpsndServerContext* context, int left, int right) { - size_t pos; + size_t len; BOOL status; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; + wStream* s = rdpsnd_server_get_buffer(context); + + if (!Stream_EnsureRemainingCapacity(s, 8)) + return ERROR_NOT_ENOUGH_MEMORY; + Stream_Write_UINT8(s, SNDC_SETVOLUME); Stream_Write_UINT8(s, 0); - Stream_Seek_UINT16(s); + Stream_Write_UINT16(s, 4); /* Payload length */ Stream_Write_UINT16(s, left); Stream_Write_UINT16(s, right); - pos = Stream_GetPosition(s); - Stream_SetPosition(s, 2); - Stream_Write_UINT16(s, pos - 4); - Stream_SetPosition(s, pos); + len = Stream_GetPosition(s); + status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), - Stream_GetPosition(s), &written); + (ULONG)len, &written); Stream_SetPosition(s, 0); return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; } @@ -626,8 +793,9 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context) size_t pos; BOOL status; ULONG written; - wStream* s = context->priv->rdpsnd_pdu; UINT error = CHANNEL_RC_OK; + wStream* s = rdpsnd_server_get_buffer(context); + EnterCriticalSection(&context->priv->lock); if (context->priv->out_pending_frames > 0) @@ -649,6 +817,10 @@ static UINT rdpsnd_server_close(RdpsndServerContext* context) return error; context->selected_client_format = 0xFFFF; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return ERROR_OUTOFMEMORY; + Stream_Write_UINT8(s, SNDC_CLOSE); Stream_Write_UINT8(s, 0); Stream_Seek_UINT16(s); @@ -671,14 +843,58 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context) { void* buffer = NULL; DWORD bytesReturned; - RdpsndServerPrivate* priv = context->priv; + RdpsndServerPrivate* priv; UINT error = ERROR_INTERNAL_ERROR; - priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpsnd"); + PULONG pSessionId = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); - if (!priv->ChannelHandle) + priv = context->priv; + priv->SessionId = WTS_CURRENT_SESSION; + + if (context->use_dynamic_virtual_channel) { - WLog_ERR(TAG, "WTSVirtualChannelOpen failed!"); - return ERROR_INTERNAL_ERROR; + UINT32 channelId; + BOOL status = TRUE; + + if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &bytesReturned)) + { + priv->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + priv->ChannelHandle = (HANDLE)WTSVirtualChannelOpenEx( + priv->SessionId, "AUDIO_PLAYBACK_DVC", WTS_CHANNEL_OPTION_DYNAMIC); + if (!priv->ChannelHandle) + { + WLog_ERR(TAG, "Open audio dynamic virtual channel (AUDIO_PLAYBACK_DVC) failed!"); + return ERROR_INTERNAL_ERROR; + } + + channelId = WTSChannelGetIdByHandle(priv->ChannelHandle); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + goto out_close; + } + } + else + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + } + else + { + priv->ChannelHandle = + WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "rdpsnd"); + if (!priv->ChannelHandle) + { + WLog_ERR(TAG, "Open audio static virtual channel (rdpsnd) failed!"); + return ERROR_INTERNAL_ERROR; + } } if (!WTSVirtualChannelQuery(priv->ChannelHandle, WTSVirtualEventHandle, &buffer, @@ -713,7 +929,7 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context) goto out_pdu; } - if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu))) + if ((error = rdpsnd_server_send_formats(context))) { WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error); goto out_lock; @@ -763,6 +979,12 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context) { UINT error = CHANNEL_RC_OK; + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + if (!context->priv->StopEvent) + return error; + if (context->priv->ownThread) { if (context->priv->StopEvent) @@ -778,36 +1000,46 @@ static UINT rdpsnd_server_stop(RdpsndServerContext* context) CloseHandle(context->priv->Thread); CloseHandle(context->priv->StopEvent); + context->priv->Thread = NULL; + context->priv->StopEvent = NULL; } } DeleteCriticalSection(&context->priv->lock); if (context->priv->rdpsnd_pdu) + { Stream_Free(context->priv->rdpsnd_pdu, TRUE); + context->priv->rdpsnd_pdu = NULL; + } + + if (context->priv->ChannelHandle) + { + WTSVirtualChannelClose(context->priv->ChannelHandle); + context->priv->ChannelHandle = NULL; + } return error; } RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) { - RdpsndServerContext* context; RdpsndServerPrivate* priv; - context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext)); + RdpsndServerContext* context = (RdpsndServerContext*)calloc(1, sizeof(RdpsndServerContext)); if (!context) - { - WLog_ERR(TAG, "calloc failed!"); - return NULL; - } + goto fail; context->vcm = vcm; context->Start = rdpsnd_server_start; context->Stop = rdpsnd_server_stop; context->selected_client_format = 0xFFFF; context->Initialize = rdpsnd_server_initialize; + context->SendFormats = rdpsnd_server_send_formats; context->SelectFormat = rdpsnd_server_select_format; + context->Training = rdpsnd_server_training; context->SendSamples = rdpsnd_server_send_samples; + context->SendSamples2 = rdpsnd_server_send_samples2; context->SetVolume = rdpsnd_server_set_volume; context->Close = rdpsnd_server_close; context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate)); @@ -815,7 +1047,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) if (!priv) { WLog_ERR(TAG, "calloc failed!"); - goto out_free; + goto fail; } priv->dsp_context = freerdp_dsp_context_new(TRUE); @@ -823,7 +1055,7 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) if (!priv->dsp_context) { WLog_ERR(TAG, "freerdp_dsp_context_new failed!"); - goto out_free_priv; + goto fail; } priv->input_stream = Stream_New(NULL, 4); @@ -831,24 +1063,23 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm) if (!priv->input_stream) { WLog_ERR(TAG, "Stream_New failed!"); - goto out_free_dsp; + goto fail; } priv->expectedBytes = 4; priv->waitingHeader = TRUE; priv->ownThread = TRUE; return context; -out_free_dsp: - freerdp_dsp_context_free(priv->dsp_context); -out_free_priv: - free(context->priv); -out_free: - free(context); +fail: + rdpsnd_server_context_free(context); return NULL; } void rdpsnd_server_context_reset(RdpsndServerContext* context) { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + context->priv->expectedBytes = 4; context->priv->waitingHeader = TRUE; Stream_SetPosition(context->priv->input_stream, 0); @@ -856,16 +1087,21 @@ void rdpsnd_server_context_reset(RdpsndServerContext* context) void rdpsnd_server_context_free(RdpsndServerContext* context) { - if (context->priv->ChannelHandle) - WTSVirtualChannelClose(context->priv->ChannelHandle); + if (!context) + return; + + if (context->priv) + { + rdpsnd_server_stop(context); - free(context->priv->out_buffer); + free(context->priv->out_buffer); - if (context->priv->dsp_context) - freerdp_dsp_context_free(context->priv->dsp_context); + if (context->priv->dsp_context) + freerdp_dsp_context_free(context->priv->dsp_context); - if (context->priv->input_stream) - Stream_Free(context->priv->input_stream, TRUE); + if (context->priv->input_stream) + Stream_Free(context->priv->input_stream, TRUE); + } free(context->server_formats); free(context->client_formats); @@ -875,6 +1111,9 @@ void rdpsnd_server_context_free(RdpsndServerContext* context) HANDLE rdpsnd_server_get_event_handle(RdpsndServerContext* context) { + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + return context->priv->channelEvent; } @@ -896,8 +1135,14 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context) { DWORD bytesReturned; UINT ret = CHANNEL_RC_OK; - RdpsndServerPrivate* priv = context->priv; - wStream* s = priv->input_stream; + RdpsndServerPrivate* priv; + wStream* s; + + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + + priv = context->priv; + s = priv->input_stream; if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s), priv->expectedBytes, &bytesReturned)) @@ -952,6 +1197,10 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context) ret = rdpsnd_server_recv_waveconfirm(context, s); break; + case SNDC_TRAINING: + ret = rdpsnd_server_recv_trainingconfirm(context, s); + break; + case SNDC_FORMATS: ret = rdpsnd_server_recv_formats(context, s); @@ -962,8 +1211,6 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context) case SNDC_QUALITYMODE: ret = rdpsnd_server_recv_quality_mode(context, s); - Stream_SetPosition(s, - 0); /* in case the Activated callback tries to treat some messages */ if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7)) IFCALL(context->Activated, context); diff --git a/channels/rdpsnd/server/rdpsnd_main.h b/channels/rdpsnd/server/rdpsnd_main.h index f058abbc71ba..9849273ea4d0 100644 --- a/channels/rdpsnd/server/rdpsnd_main.h +++ b/channels/rdpsnd/server/rdpsnd_main.h @@ -39,6 +39,7 @@ struct _rdpsnd_server_private HANDLE StopEvent; HANDLE channelEvent; void* ChannelHandle; + DWORD SessionId; BOOL waitingHeader; DWORD expectedBytes; diff --git a/channels/server/channels.c b/channels/server/channels.c index 40c55eeccb13..68641a6fc892 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -50,8 +50,21 @@ #include #include #include +#include #include #include +#include + +#if defined(CHANNEL_RDPECAM_SERVER) +#include +#include +#endif + +#if defined(CHANNEL_AINPUT_SERVER) +#include +#endif + +extern void freerdp_channels_dummy(void); void freerdp_channels_dummy(void) { @@ -65,8 +78,16 @@ void freerdp_channels_dummy(void) RemdeskServerContext* remdesk; EncomspServerContext* encomsp; RailServerContext* rail; + TelemetryServerContext* telemetry; RdpgfxServerContext* rdpgfx; DispServerContext* disp; +#if defined (CHANNEL_RDPECAM_SERVER) + CamDevEnumServerContext* camera_enumerator; + CameraDeviceServerContext* camera_device; +#endif +#ifdef WITH_CHANNEL_GFXREDIR + GfxRedirServerContext* gfxredir; +#endif // WITH_CHANNEL_GFXREDIR audin = audin_server_context_new(NULL); audin_server_context_free(audin); rdpsnd = rdpsnd_server_context_new(NULL); @@ -87,10 +108,30 @@ void freerdp_channels_dummy(void) encomsp_server_context_free(encomsp); rail = rail_server_context_new(NULL); rail_server_context_free(rail); + telemetry = telemetry_server_context_new(NULL); + telemetry_server_context_free(telemetry); rdpgfx = rdpgfx_server_context_new(NULL); rdpgfx_server_context_free(rdpgfx); disp = disp_server_context_new(NULL); disp_server_context_free(disp); + +#if defined (CHANNEL_RDPECAM_SERVER) + camera_enumerator = cam_dev_enum_server_context_new(NULL); + cam_dev_enum_server_context_free(camera_enumerator); + camera_device = camera_device_server_context_new(NULL); + camera_device_server_context_free(camera_device); +#endif + +#if defined(CHANNEL_AINPUT_SERVER) + { + ainput_server_context* ainput = ainput_server_context_new(NULL); + ainput_server_context_free(ainput); + } +#endif +#ifdef WITH_CHANNEL_GFXREDIR + gfxredir = gfxredir_server_context_new(NULL); + gfxredir_server_context_free(gfxredir); +#endif // WITH_CHANNEL_GFXREDIR } /** diff --git a/channels/telemetry/CMakeLists.txt b/channels/telemetry/CMakeLists.txt new file mode 100644 index 000000000000..d2b4f24e3c57 --- /dev/null +++ b/channels/telemetry/CMakeLists.txt @@ -0,0 +1,22 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel("telemetry") + +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/telemetry/ChannelOptions.cmake b/channels/telemetry/ChannelOptions.cmake new file mode 100644 index 000000000000..1b9e3917cbad --- /dev/null +++ b/channels/telemetry/ChannelOptions.cmake @@ -0,0 +1,12 @@ + +set(OPTION_DEFAULT OFF) +set(OPTION_CLIENT_DEFAULT OFF) +set(OPTION_SERVER_DEFAULT ON) + +define_channel_options(NAME "telemetry" TYPE "dynamic" + DESCRIPTION "Telemetry Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPET]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_server_options(${OPTION_SERVER_DEFAULT}) + diff --git a/channels/telemetry/server/CMakeLists.txt b/channels/telemetry/server/CMakeLists.txt new file mode 100644 index 000000000000..75be8ac9ec8b --- /dev/null +++ b/channels/telemetry/server/CMakeLists.txt @@ -0,0 +1,26 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2022 Pascal Nowack +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_server("telemetry") + +set(${MODULE_PREFIX}_SRCS + telemetry_main.c) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +target_link_libraries(${MODULE_NAME} freerdp) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/telemetry/server/telemetry_main.c b/channels/telemetry/server/telemetry_main.c new file mode 100644 index 000000000000..717360311897 --- /dev/null +++ b/channels/telemetry/server/telemetry_main.c @@ -0,0 +1,443 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Telemetry Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#define TAG CHANNELS_TAG("telemetry.server") + +typedef enum +{ + TELEMETRY_INITIAL, + TELEMETRY_OPENED, +} eTelemetryChannelState; + +typedef struct +{ + TelemetryServerContext context; + + HANDLE stopEvent; + + HANDLE thread; + void* telemetry_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eTelemetryChannelState state; + + wStream* buffer; +} telemetry_server; + +static UINT telemetry_server_initialize(TelemetryServerContext* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (telemetry->isOpened) + { + WLog_WARN(TAG, "Application error: TELEMETRY channel already initialized, " + "calling in this state is not possible!"); + return ERROR_INVALID_STATE; + } + + telemetry->externalThread = externalThread; + + return error; +} + +static UINT telemetry_server_open_channel(telemetry_server* telemetry) +{ + TelemetryServerContext* context = &telemetry->context; + DWORD Error = ERROR_SUCCESS; + HANDLE hEvent; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + UINT32 channelId; + BOOL status = TRUE; + + WINPR_ASSERT(telemetry); + + if (WTSQuerySessionInformationA(telemetry->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + telemetry->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + hEvent = WTSVirtualChannelManagerGetEventHandle(telemetry->context.vcm); + + if (WaitForSingleObject(hEvent, 1000) == WAIT_FAILED) + { + Error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "!", Error); + return Error; + } + + telemetry->telemetry_channel = WTSVirtualChannelOpenEx( + telemetry->SessionId, TELEMETRY_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); + if (!telemetry->telemetry_channel) + { + Error = GetLastError(); + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error); + return Error; + } + + channelId = WTSChannelGetIdByHandle(telemetry->telemetry_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + return Error; +} + +static UINT telemetry_server_recv_rdp_telemetry_pdu(TelemetryServerContext* context, wStream* s) +{ + TELEMETRY_RDP_TELEMETRY_PDU pdu; + UINT error = CHANNEL_RC_OK; + + if (Stream_GetRemainingLength(s) < 16) + { + WLog_ERR(TAG, "telemetry_server_recv_rdp_telemetry_pdu: Not enough data!"); + return ERROR_NO_DATA; + } + + Stream_Read_UINT32(s, pdu.PromptForCredentialsMillis); + Stream_Read_UINT32(s, pdu.PromptForCredentialsDoneMillis); + Stream_Read_UINT32(s, pdu.GraphicsChannelOpenedMillis); + Stream_Read_UINT32(s, pdu.FirstGraphicsReceivedMillis); + + IFCALLRET(context->RdpTelemetry, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->RdpTelemetry failed with error %" PRIu32 "", error); + + return error; +} + +static UINT telemetry_process_message(telemetry_server* telemetry) +{ + BOOL rc; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned; + BYTE MessageId; + BYTE Length; + wStream* s; + + WINPR_ASSERT(telemetry); + WINPR_ASSERT(telemetry->telemetry_channel); + + s = telemetry->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(telemetry->telemetry_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 1) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(telemetry->telemetry_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, MessageId); + Stream_Read_UINT8(s, Length); + + switch (MessageId) + { + case 0x01: + error = telemetry_server_recv_rdp_telemetry_pdu(&telemetry->context, s); + break; + default: + WLog_ERR(TAG, "telemetry_process_message: unknown MessageId %" PRIu8 "", MessageId); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +static UINT telemetry_server_context_poll_int(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(telemetry); + + switch (telemetry->state) + { + case TELEMETRY_INITIAL: + error = telemetry_server_open_channel(telemetry); + if (error) + WLog_ERR(TAG, "telemetry_server_open_channel failed with error %" PRIu32 "!", + error); + else + telemetry->state = TELEMETRY_OPENED; + break; + case TELEMETRY_OPENED: + error = telemetry_process_message(telemetry); + break; + } + + return error; +} + +static HANDLE telemetry_server_get_channel_handle(telemetry_server* telemetry) +{ + void* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(telemetry); + + if (WTSVirtualChannelQuery(telemetry->telemetry_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI telemetry_server_thread_func(LPVOID arg) +{ + DWORD nCount; + HANDLE events[2] = { 0 }; + telemetry_server* telemetry = (telemetry_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status; + + WINPR_ASSERT(telemetry); + + nCount = 0; + events[nCount++] = telemetry->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (telemetry->state) + { + case TELEMETRY_INITIAL: + error = telemetry_server_context_poll_int(&telemetry->context); + if (error == CHANNEL_RC_OK) + { + events[1] = telemetry_server_get_channel_handle(telemetry); + nCount = 2; + } + break; + case TELEMETRY_OPENED: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + case WAIT_TIMEOUT: + error = telemetry_server_context_poll_int(&telemetry->context); + break; + + case WAIT_FAILED: + default: + error = ERROR_INTERNAL_ERROR; + break; + } + break; + } + } + + WTSVirtualChannelClose(telemetry->telemetry_channel); + telemetry->telemetry_channel = NULL; + + if (error && telemetry->context.rdpcontext) + setChannelError(telemetry->context.rdpcontext, error, + "telemetry_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +static UINT telemetry_server_open(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (!telemetry->externalThread && (telemetry->thread == NULL)) + { + telemetry->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!telemetry->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + telemetry->thread = CreateThread(NULL, 0, telemetry_server_thread_func, telemetry, 0, NULL); + if (!telemetry->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(telemetry->stopEvent); + telemetry->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + telemetry->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +static UINT telemetry_server_close(TelemetryServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (!telemetry->externalThread && telemetry->thread) + { + SetEvent(telemetry->stopEvent); + + if (WaitForSingleObject(telemetry->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(telemetry->thread); + CloseHandle(telemetry->stopEvent); + telemetry->thread = NULL; + telemetry->stopEvent = NULL; + } + if (telemetry->externalThread) + { + if (telemetry->state != TELEMETRY_INITIAL) + { + WTSVirtualChannelClose(telemetry->telemetry_channel); + telemetry->telemetry_channel = NULL; + telemetry->state = TELEMETRY_INITIAL; + } + } + telemetry->isOpened = FALSE; + + return error; +} + +static UINT telemetry_server_context_poll(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + + if (!telemetry->externalThread) + return ERROR_INTERNAL_ERROR; + + return telemetry_server_context_poll_int(context); +} + +static BOOL telemetry_server_context_handle(TelemetryServerContext* context, HANDLE* handle) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + WINPR_ASSERT(telemetry); + WINPR_ASSERT(handle); + + if (!telemetry->externalThread) + return FALSE; + if (telemetry->state == TELEMETRY_INITIAL) + return FALSE; + + *handle = telemetry_server_get_channel_handle(telemetry); + + return TRUE; +} + +TelemetryServerContext* telemetry_server_context_new(HANDLE vcm) +{ + telemetry_server* telemetry = (telemetry_server*)calloc(1, sizeof(telemetry_server)); + + if (!telemetry) + return NULL; + + telemetry->context.vcm = vcm; + telemetry->context.Initialize = telemetry_server_initialize; + telemetry->context.Open = telemetry_server_open; + telemetry->context.Close = telemetry_server_close; + telemetry->context.Poll = telemetry_server_context_poll; + telemetry->context.ChannelHandle = telemetry_server_context_handle; + + telemetry->buffer = Stream_New(NULL, 4096); + if (!telemetry->buffer) + goto fail; + + return &telemetry->context; +fail: + telemetry_server_context_free(&telemetry->context); + return NULL; +} + +void telemetry_server_context_free(TelemetryServerContext* context) +{ + telemetry_server* telemetry = (telemetry_server*)context; + + if (telemetry) + { + telemetry_server_close(context); + Stream_Free(telemetry->buffer, TRUE); + } + + free(telemetry); +} diff --git a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c index 0444ad07bed3..215337464386 100644 --- a/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c +++ b/channels/tsmf/client/ffmpeg/tsmf_ffmpeg.c @@ -195,9 +195,12 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP if (media_type->SubType == TSMF_SUB_TYPE_AVC1 && media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO) { + size_t required = 6; /* The extradata format that FFmpeg uses is following CodecPrivate in Matroska. See http://haali.su/mkv/codecs.pdf */ p = mdecoder->codec_context->extradata; + if (mdecoder->codec_context->extradata_size < required) + return FALSE; *p++ = 1; /* Reserved? */ *p++ = media_type->ExtraData[8]; /* Profile */ *p++ = 0; /* Profile */ @@ -206,23 +209,36 @@ static BOOL tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYP *p++ = 0xe0 | 0x01; /* Reserved | #sps */ s = media_type->ExtraData + 20; size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1))); + required += size + 2; + if (mdecoder->codec_context->extradata_size < required) + return FALSE; memcpy(p, s, size + 2); s += size + 2; p += size + 2; + required++; + if (mdecoder->codec_context->extradata_size < required) + return FALSE; *p++ = 1; /* #pps */ size = ((UINT32)(*s)) * 256 + ((UINT32)(*(s + 1))); + required += size + 2; + if (mdecoder->codec_context->extradata_size < required) + return FALSE; memcpy(p, s, size + 2); } else { memcpy(mdecoder->codec_context->extradata, media_type->ExtraData, media_type->ExtraDataSize); + if (mdecoder->codec_context->extradata_size < media_type->ExtraDataSize + 8) + return FALSE; memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8); } } +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100) if (mdecoder->codec->capabilities & AV_CODEC_CAP_TRUNCATED) mdecoder->codec_context->flags |= AV_CODEC_FLAG_TRUNCATED; +#endif return TRUE; } @@ -245,6 +261,9 @@ static BOOL tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* medi { TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*)decoder; + WINPR_ASSERT(mdecoder); + WINPR_ASSERT(media_type); + switch (media_type->MajorType) { case TSMF_MAJOR_TYPE_VIDEO: @@ -297,6 +316,9 @@ static BOOL tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* medi http://msdn.microsoft.com/en-us/library/dd757806.aspx */ if (media_type->ExtraData) { + if (media_type->ExtraDataSize < 12) + return FALSE; + media_type->ExtraData += 12; media_type->ExtraDataSize -= 12; } @@ -615,7 +637,9 @@ static void tsmf_ffmpeg_free(ITSMFDecoder* decoder) static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT; static BOOL CALLBACK InitializeAvCodecs(PINIT_ONCE once, PVOID param, PVOID* context) { +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100) avcodec_register_all(); +#endif return TRUE; } diff --git a/channels/tsmf/client/gstreamer/tsmf_gstreamer.c b/channels/tsmf/client/gstreamer/tsmf_gstreamer.c index 61ea6a82c58c..51cbc869f0c2 100644 --- a/channels/tsmf/client/gstreamer/tsmf_gstreamer.c +++ b/channels/tsmf/client/gstreamer/tsmf_gstreamer.c @@ -420,6 +420,8 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m http://msdn.microsoft.com/en-us/library/dd757806.aspx */ if (media_type->ExtraData) { + if (media_type->ExtraDataSize < 12) + return FALSE; media_type->ExtraData += 12; media_type->ExtraDataSize -= 12; } diff --git a/channels/tsmf/client/tsmf_codec.c b/channels/tsmf/client/tsmf_codec.c index 66723962d0fd..a212affea3ba 100644 --- a/channels/tsmf/client/tsmf_codec.c +++ b/channels/tsmf/client/tsmf_codec.c @@ -386,7 +386,12 @@ static BOOL tsmf_read_format_type(TS_AM_MEDIA_TYPE* mediatype, wStream* s, UINT3 if (cbFormat > 176) { - mediatype->ExtraDataSize = cbFormat - 176; + const size_t nsize = cbFormat - 176; + if (mediatype->ExtraDataSize < nsize) + return FALSE; + if (!Stream_CheckAndLogRequiredLength(TAG, s, nsize)) + return FALSE; + mediatype->ExtraDataSize = nsize; mediatype->ExtraData = Stream_Pointer(s); } break; diff --git a/channels/tsmf/client/tsmf_ifman.c b/channels/tsmf/client/tsmf_ifman.c index a71a03fda8a2..cfee41c8bda9 100644 --- a/channels/tsmf/client/tsmf_ifman.c +++ b/channels/tsmf/client/tsmf_ifman.c @@ -69,16 +69,20 @@ UINT tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman) */ UINT tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman) { - UINT32 i; - UINT32 v; - UINT32 pos; - UINT32 CapabilityType; - UINT32 cbCapabilityLength; - UINT32 numHostCapabilities; - + UINT32 i = 0; + UINT32 v = 0; + UINT32 pos = 0; + UINT32 CapabilityType = 0; + UINT32 cbCapabilityLength = 0; + UINT32 numHostCapabilities = 0; + + WINPR_ASSERT(ifman); if (!Stream_EnsureRemainingCapacity(ifman->output, ifman->input_size + 4)) return ERROR_OUTOFMEMORY; + if (Stream_GetRemainingLength(ifman->input) < ifman->input_size) + return ERROR_INVALID_DATA; + pos = Stream_GetPosition(ifman->output); Stream_Copy(ifman->input, ifman->output, ifman->input_size); Stream_SetPosition(ifman->output, pos); diff --git a/channels/urbdrc/client/data_transfer.c b/channels/urbdrc/client/data_transfer.c index 9a44e6e09116..ed6ccf32d5a5 100644 --- a/channels/urbdrc/client/data_transfer.c +++ b/channels/urbdrc/client/data_transfer.c @@ -97,7 +97,13 @@ static wStream* urb_create_iocompletion(UINT32 InterfaceField, UINT32 MessageId, UINT32 OutputBufferSize) { const UINT32 InterfaceId = (STREAM_ID_PROXY << 30) | (InterfaceField & 0x3FFFFFFF); - wStream* out = Stream_New(NULL, OutputBufferSize + 28); + +#if UINT32_MAX >= SIZE_MAX + if (OutputBufferSize > UINT32_MAX - 28ull) + return NULL; +#endif + + wStream* out = Stream_New(NULL, OutputBufferSize + 28ull); if (!out) return NULL; @@ -241,6 +247,10 @@ static UINT urbdrc_process_io_control(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* c Stream_Read_UINT32(s, OutputBufferSize); Stream_Read_UINT32(s, RequestId); + + if (OutputBufferSize > UINT32_MAX - 4) + return ERROR_INVALID_DATA; + InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev)); out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize + 4); @@ -673,7 +683,11 @@ static UINT urb_control_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callba buffer = Stream_Pointer(out); if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; Stream_Copy(s, out, OutputBufferSize); + } /** process TS_URB_CONTROL_TRANSFER */ if (!pdev->control_transfer(pdev, RequestId, EndpointAddress, TransferFlags, bmRequestType, @@ -720,6 +734,15 @@ static UINT urb_bulk_or_interrupt_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */ Stream_Read_UINT32(s, OutputBufferSize); EndpointAddress = (PipeHandle & 0x000000ff); + + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + { + return ERROR_INVALID_DATA; + } + } + /** process TS_URB_BULK_OR_INTERRUPT_TRANSFER */ return pdev->bulk_or_interrupt_transfer( pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, noAck, @@ -804,6 +827,13 @@ static UINT urb_isoch_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback packetDescriptorData = Stream_Pointer(s); Stream_Seek(s, NumberOfPackets * 12); Stream_Read_UINT32(s, OutputBufferSize); + + if (transferDir == USBD_TRANSFER_DIRECTION_OUT) + { + if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize)) + return ERROR_INVALID_DATA; + } + return pdev->isoch_transfer( pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame, ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize, @@ -1749,6 +1779,13 @@ static UINT urbdrc_process_transfer_request(IUDEVICE* pdev, URBDRC_CHANNEL_CALLB break; } + if (error) + { + WLog_Print(urbdrc->log, WLOG_WARN, + "USB transfer request URB Function %08" PRIx32 " failed with %08" PRIx32, + URB_Function, error); + } + return error; } diff --git a/channels/urbdrc/client/libusb/libusb_udevice.c b/channels/urbdrc/client/libusb/libusb_udevice.c index aa69890aed8d..5cc6726c8736 100644 --- a/channels/urbdrc/client/libusb/libusb_udevice.c +++ b/channels/urbdrc/client/libusb/libusb_udevice.c @@ -237,7 +237,7 @@ static void async_transfer_user_data_free(ASYNC_TRANSFER_USER_DATA* user_data) } } -static void func_iso_callback(struct libusb_transfer* transfer) +static void LIBUSB_CALL func_iso_callback(struct libusb_transfer* transfer) { ASYNC_TRANSFER_USER_DATA* user_data = (ASYNC_TRANSFER_USER_DATA*)transfer->user_data; const UINT32 streamID = stream_id_from_buffer(transfer); @@ -336,7 +336,7 @@ static const LIBUSB_ENDPOINT_DESCEIPTOR* func_get_ep_desc(LIBUSB_CONFIG_DESCRIPT return NULL; } -static void func_bulk_transfer_cb(struct libusb_transfer* transfer) +static void LIBUSB_CALL func_bulk_transfer_cb(struct libusb_transfer* transfer) { ASYNC_TRANSFER_USER_DATA* user_data; uint32_t streamID; @@ -490,17 +490,19 @@ static LIBUSB_DEVICE* udev_get_libusb_dev(libusb_context* context, uint8_t bus_n for (i = 0; i < total_device; i++) { - uint8_t cbus = libusb_get_bus_number(libusb_list[i]); - uint8_t caddr = libusb_get_device_address(libusb_list[i]); - - if ((bus_number == cbus) && (dev_number == caddr)) + LIBUSB_DEVICE* dev = libusb_list[i]; + if ((bus_number == libusb_get_bus_number(dev)) && + (dev_number == libusb_get_device_address(dev))) { - device = libusb_list[i]; - break; + device = dev; + } + else + { + libusb_unref_device(dev); } } - libusb_free_device_list(libusb_list, 1); + libusb_free_device_list(libusb_list, 0); return device; } @@ -522,24 +524,23 @@ static LIBUSB_DEVICE_DESCRIPTOR* udev_new_descript(URBDRC_PLUGIN* urbdrc, LIBUSB return descriptor; } - static int libusb_udev_select_interface(IUDEVICE* idev, BYTE InterfaceNumber, BYTE AlternateSetting) { int error = 0, diff = 0; UDEVICE* pdev = (UDEVICE*)idev; - URBDRC_PLUGIN* urbdrc; - MSUSB_CONFIG_DESCRIPTOR* MsConfig; - MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces; if (!pdev || !pdev->urbdrc) return -1; - urbdrc = pdev->urbdrc; - MsConfig = pdev->MsConfig; + URBDRC_PLUGIN* urbdrc = pdev->urbdrc; + MSUSB_CONFIG_DESCRIPTOR* MsConfig = pdev->MsConfig; if (MsConfig) { - MsInterfaces = MsConfig->MsInterfaces; + if (InterfaceNumber >= MsConfig->NumInterfaces) + return -2; + + MSUSB_INTERFACE_DESCRIPTOR** MsInterfaces = MsConfig->MsInterfaces; if (MsInterfaces) { WLog_Print(urbdrc->log, WLOG_INFO, @@ -1012,6 +1013,9 @@ static BOOL libusb_udev_detach_kernel_driver(IUDEVICE* idev) if (!pdev || !pdev->LibusbConfig || !pdev->libusb_handle || !pdev->urbdrc) return FALSE; +#ifdef _WIN32 + return TRUE; +#else urbdrc = pdev->urbdrc; if ((pdev->status & URBDRC_DEVICE_DETACH_KERNEL) == 0) @@ -1032,6 +1036,7 @@ static BOOL libusb_udev_detach_kernel_driver(IUDEVICE* idev) } return TRUE; +#endif } static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev) @@ -1048,12 +1053,14 @@ static BOOL libusb_udev_attach_kernel_driver(IUDEVICE* idev) log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_release_interface", err); +#ifndef _WIN32 if (err != LIBUSB_ERROR_NO_DEVICE) { err = libusb_attach_kernel_driver(pdev->libusb_handle, i); log_libusb_result(pdev->urbdrc->log, WLOG_DEBUG, "libusb_attach_kernel_driver if=%d", err, i); } +#endif } return TRUE; @@ -1109,6 +1116,7 @@ static void libusb_udev_mark_channel_closed(IUDEVICE* idev) const uint8_t devNr = idev->get_dev_number(idev); pdev->status |= URBDRC_DEVICE_CHANNEL_CLOSED; + pdev->iface.cancel_all_transfer_request(&pdev->iface); urbdrc->udevman->unregister_udevice(urbdrc->udevman, busNr, devNr); } } @@ -1214,12 +1222,18 @@ static int libusb_udev_isoch_transfer(IUDEVICE* idev, URBDRC_CHANNEL_CALLBACK* c if (!Buffer) Stream_Seek(user_data->data, (NumberOfPackets * 12)); - iso_packet_size = BufferSize / NumberOfPackets; - iso_transfer = libusb_alloc_transfer(NumberOfPackets); + if (NumberOfPackets > 0) + { + iso_packet_size = BufferSize / NumberOfPackets; + iso_transfer = libusb_alloc_transfer((int)NumberOfPackets); + } if (iso_transfer == NULL) { - WLog_Print(urbdrc->log, WLOG_ERROR, "Error: libusb_alloc_transfer."); + WLog_Print(urbdrc->log, WLOG_ERROR, + "Error: libusb_alloc_transfer [NumberOfPackets=%" PRIu32 ", BufferSize=%" PRIu32 + " ]", + NumberOfPackets, BufferSize); async_transfer_user_data_free(user_data); return -1; } @@ -1474,6 +1488,7 @@ static void udev_free(IUDEVICE* idev) ArrayList_Free(udev->request_queue); /* free the config descriptor that send from windows */ msusb_msconfig_free(udev->MsConfig); + libusb_unref_device(udev->libusb_dev); libusb_close(udev->libusb_handle); libusb_close(udev->hub_handle); free(udev->devDescriptor); @@ -1522,8 +1537,8 @@ static void udev_load_interface(UDEVICE* pdev) pdev->iface.free = udev_free; } -static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev, - UINT16 bus_number, UINT16 dev_number) +static int udev_get_device_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev, + UINT16 bus_number, UINT16 dev_number) { int error; ssize_t i, total_device; @@ -1535,21 +1550,19 @@ static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVI for (i = 0; i < total_device; i++) { - LIBUSB_DEVICE_HANDLE* handle; - uint8_t cbus = libusb_get_bus_number(libusb_list[i]); - uint8_t caddr = libusb_get_device_address(libusb_list[i]); + LIBUSB_DEVICE* dev = libusb_list[i]; - if ((bus_number != cbus) || (dev_number != caddr)) + if ((bus_number != libusb_get_bus_number(dev)) || + (dev_number != libusb_get_device_address(dev))) continue; - error = libusb_open(libusb_list[i], &handle); + error = libusb_open(dev, &pdev->libusb_handle); if (log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error)) break; /* get port number */ - error = libusb_get_port_numbers(libusb_list[i], port_numbers, sizeof(port_numbers)); - libusb_close(handle); + error = libusb_get_port_numbers(dev, port_numbers, sizeof(port_numbers)); if (error < 1) { @@ -1567,29 +1580,40 @@ static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVI WLog_Print(urbdrc->log, WLOG_DEBUG, " DevPath: %s", pdev->path); break; } + libusb_free_device_list(libusb_list, 1); + + if (error < 0) + return -1; + return 0; +} + +static int udev_get_hub_handle(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UDEVICE* pdev, + UINT16 bus_number, UINT16 dev_number) +{ + int error; + ssize_t i, total_device; + LIBUSB_DEVICE** libusb_list; + LIBUSB_DEVICE_HANDLE* handle; + total_device = libusb_get_device_list(ctx, &libusb_list); /* Look for device hub. */ - if (error == 0) - { - error = -1; + error = -1; - for (i = 0; i < total_device; i++) - { - LIBUSB_DEVICE_HANDLE* handle; - uint8_t cbus = libusb_get_bus_number(libusb_list[i]); - uint8_t caddr = libusb_get_device_address(libusb_list[i]); + for (i = 0; i < total_device; i++) + { + LIBUSB_DEVICE* dev = libusb_list[i]; - if ((bus_number != cbus) || (1 != caddr)) /* Root hub allways first on bus. */ - continue; + if ((bus_number != libusb_get_bus_number(dev)) || + (1 != libusb_get_device_address(dev))) /* Root hub allways first on bus. */ + continue; - WLog_Print(urbdrc->log, WLOG_DEBUG, " Open hub: %" PRIu16 "", bus_number); - error = libusb_open(libusb_list[i], &handle); + WLog_Print(urbdrc->log, WLOG_DEBUG, " Open hub: %" PRIu16 "", bus_number); + error = libusb_open(dev, &handle); - if (!log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error)) - pdev->hub_handle = handle; + if (!log_libusb_result(urbdrc->log, WLOG_ERROR, "libusb_open", error)) + pdev->hub_handle = handle; - break; - } + break; } libusb_free_device_list(libusb_list, 1); @@ -1640,30 +1664,26 @@ static IUDEVICE* udev_init(URBDRC_PLUGIN* urbdrc, libusb_context* context, LIBUS if (urbdrc->listener_callback) udev_set_channelManager(&pdev->iface, urbdrc->listener_callback->channel_mgr); - /* Get HUB handle */ - status = udev_get_hub_handle(urbdrc, context, pdev, bus_number, dev_number); - - if (status < 0) - pdev->hub_handle = NULL; - + /* Get DEVICE handle */ + status = udev_get_device_handle(urbdrc, context, pdev, bus_number, dev_number); + if (status != LIBUSB_SUCCESS) { struct libusb_device_descriptor desc; - const uint8_t bus = libusb_get_bus_number(pdev->libusb_dev); const uint8_t port = libusb_get_port_number(pdev->libusb_dev); - const uint8_t addr = libusb_get_device_address(pdev->libusb_dev); libusb_get_device_descriptor(pdev->libusb_dev, &desc); - status = libusb_open(pdev->libusb_dev, &pdev->libusb_handle); - - if (status != LIBUSB_SUCCESS) - { - log_libusb_result(urbdrc->log, WLOG_ERROR, - "libusb_open [b=0x%02X,p=0x%02X,a=0x%02X,VID=0x%04X,PID=0x%04X]", - status, bus, port, addr, desc.idVendor, desc.idProduct); - goto fail; - } + log_libusb_result(urbdrc->log, WLOG_ERROR, + "libusb_open [b=0x%02X,p=0x%02X,a=0x%02X,VID=0x%04X,PID=0x%04X]", status, + bus_number, port, dev_number, desc.idVendor, desc.idProduct); + goto fail; } + /* Get HUB handle */ + status = udev_get_hub_handle(urbdrc, context, pdev, bus_number, dev_number); + + if (status < 0) + pdev->hub_handle = NULL; + pdev->devDescriptor = udev_new_descript(urbdrc, pdev->libusb_dev); if (!pdev->devDescriptor) @@ -1734,8 +1754,6 @@ size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendo { LIBUSB_DEVICE** libusb_list; UDEVICE** array; - UINT16 bus_number; - UINT16 dev_number; ssize_t i, total_device; size_t num = 0; @@ -1752,23 +1770,27 @@ size_t udev_new_by_id(URBDRC_PLUGIN* urbdrc, libusb_context* ctx, UINT16 idVendo for (i = 0; i < total_device; i++) { - LIBUSB_DEVICE_DESCRIPTOR* descriptor = udev_new_descript(urbdrc, libusb_list[i]); + LIBUSB_DEVICE* dev = libusb_list[i]; + LIBUSB_DEVICE_DESCRIPTOR* descriptor = udev_new_descript(urbdrc, dev); if ((descriptor->idVendor == idVendor) && (descriptor->idProduct == idProduct)) { - bus_number = libusb_get_bus_number(libusb_list[i]); - dev_number = libusb_get_device_address(libusb_list[i]); - array[num] = (PUDEVICE)udev_init(urbdrc, ctx, libusb_list[i], bus_number, dev_number); + array[num] = (PUDEVICE)udev_init(urbdrc, ctx, dev, libusb_get_bus_number(dev), + libusb_get_device_address(dev)); if (array[num] != NULL) num++; } + else + { + libusb_unref_device(dev); + } free(descriptor); } fail: - libusb_free_device_list(libusb_list, 1); + libusb_free_device_list(libusb_list, 0); *devArray = (IUDEVICE**)array; return num; } diff --git a/channels/urbdrc/client/libusb/libusb_udevman.c b/channels/urbdrc/client/libusb/libusb_udevman.c index 1638b8c13f2f..224ec304fbc9 100644 --- a/channels/urbdrc/client/libusb/libusb_udevman.c +++ b/channels/urbdrc/client/libusb/libusb_udevman.c @@ -195,6 +195,12 @@ static size_t udevman_register_udevice(IUDEVMAN* idevman, BYTE bus_number, BYTE /* register all device that match pid vid */ num = udev_new_by_id(urbdrc, udevman->context, idVendor, idProduct, &devArray); + if (num == 0) + { + WLog_Print(urbdrc->log, WLOG_WARN, + "Could not find or redirect any usb devices by id %04x:%04x", idVendor, idProduct); + } + for (i = 0; i < num; i++) { UINT32 id; @@ -505,12 +511,7 @@ static BOOL filter_by_class(uint8_t bDeviceClass, uint8_t bDeviceSubClass) static BOOL append(char* dst, size_t length, const char* src) { - size_t slen = strlen(src); - size_t dlen = strnlen(dst, length); - if (dlen + slen >= length) - return FALSE; - strcat(dst, src); - return TRUE; + return winpr_str_append(src, dst, length, NULL); } static BOOL device_is_filtered(struct libusb_device* dev, @@ -580,8 +581,8 @@ static BOOL device_is_filtered(struct libusb_device* dev, return filtered; } -static int hotplug_callback(struct libusb_context* ctx, struct libusb_device* dev, - libusb_hotplug_event event, void* user_data) +static int LIBUSB_CALL hotplug_callback(struct libusb_context* ctx, struct libusb_device* dev, + libusb_hotplug_event event, void* user_data) { VID_PID_PAIR pair; struct libusb_device_descriptor desc; @@ -834,7 +835,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman) { int rc = LIBUSB_SUCCESS; struct timeval tv = { 0, 500 }; - if (libusb_try_lock_events(udevman->context)) + if (libusb_try_lock_events(udevman->context) == 0) { if (libusb_event_handling_ok(udevman->context)) { @@ -859,7 +860,7 @@ static BOOL poll_libusb_events(UDEVMAN* udevman) return rc > 0; } -static DWORD poll_thread(LPVOID lpThreadParameter) +static DWORD WINAPI poll_thread(LPVOID lpThreadParameter) { libusb_hotplug_callback_handle handle; UDEVMAN* udevman = (UDEVMAN*)lpThreadParameter; @@ -921,7 +922,7 @@ UINT freerdp_urbdrc_client_subsystem_entry(PFREERDP_URBDRC_SERVICE_ENTRY_POINTS udevman->next_device_id = BASE_USBDEVICE_NUM; udevman->iface.plugin = pEntryPoints->plugin; rc = libusb_init(&udevman->context); - + if (rc != LIBUSB_SUCCESS) goto fail; diff --git a/channels/video/client/CMakeLists.txt b/channels/video/client/CMakeLists.txt index cbbe483db95d..476b31677fb5 100644 --- a/channels/video/client/CMakeLists.txt +++ b/channels/video/client/CMakeLists.txt @@ -25,8 +25,10 @@ include_directories(..) add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry") - - +if (NOT BUILTIN_CHANNELS) + set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client) + set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} rdpgfx-client) +endif() set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr) target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) diff --git a/channels/video/client/video_main.c b/channels/video/client/video_main.c index a21e7cdf2b86..a8031fc86852 100644 --- a/channels/video/client/video_main.c +++ b/channels/video/client/video_main.c @@ -930,6 +930,8 @@ static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCall Stream_Read_UINT16(s, data.PacketsInSample); Stream_Read_UINT32(s, data.SampleNumber); Stream_Read_UINT32(s, data.cbSample); + if (!Stream_CheckAndLogRequiredLength(TAG, s, data.cbSample)) + return ERROR_INVALID_DATA; data.pSample = Stream_Pointer(s); /* diff --git a/ci/cmake-preloads/config-ios.txt b/ci/cmake-preloads/config-ios.txt index 886c36a6da9d..08145bb2cb97 100644 --- a/ci/cmake-preloads/config-ios.txt +++ b/ci/cmake-preloads/config-ios.txt @@ -1,7 +1,8 @@ message("PRELOADING iOS cache") -set (CMAKE_TOOLCHAIN_FILE "cmake/iOSToolchain.cmake" CACHE PATH "ToolChain file") +set (CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/cmake/ios.toolchain.cmake" CACHE PATH "cmake toolchain file") set (FREERDP_IOS_EXTERNAL_SSL_PATH $ENV{FREERDP_IOS_EXTERNAL_SSL_PATH} CACHE PATH "android ssl") set (CMAKE_BUILD_TYPE "Debug" CACHE STRING "build type") -set (IOS_PLATFORM "SIMULATOR" CACHE STRING "iso platfrorm to build") +set (CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "iOS platform to build") +set (CMAKE_OSX_DEPLOYMENT_TARGET "10.0" CACHE STRING "iOS minimum target") set (WITH_SANITIZE_ADDRESS ON CACHE BOOL "build with address sanitizer") set (WITH_CLIENT OFF CACHE BOOL "disable iOS client") diff --git a/ci/cmake-preloads/config-linux-all.txt b/ci/cmake-preloads/config-linux-all.txt index 4ba743e7247a..03fa536892d9 100644 --- a/ci/cmake-preloads/config-linux-all.txt +++ b/ci/cmake-preloads/config-linux-all.txt @@ -8,7 +8,7 @@ set (WITH_CHANNELS ON CACHE BOOL "channels") set (BUILTIN_CHANNELS ON CACHE BOOL "static channels") set (WITH_CUPS ON CACHE BOOL "cups") set (WITH_WAYLAND ON CACHE BOOL "wayland") -set (WITH_GSSAPI ON CACHE BOOL "Kerberos support") +set (WITH_GSSAPI OFF CACHE BOOL "Kerberos support") set (WITH_PCSC ON CACHE BOOL "PCSC") set (WITH_JPEG ON CACHE BOOL "jpeg") set (WITH_GSTREAMER_0_10 ON CACHE BOOL "gstreamer") diff --git a/ci/cmake-preloads/config-qa-static.cmake b/ci/cmake-preloads/config-qa-static.cmake new file mode 100644 index 000000000000..c08c1b51de35 --- /dev/null +++ b/ci/cmake-preloads/config-qa-static.cmake @@ -0,0 +1,15 @@ +message("PRELOADING cache") +set (CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "preload") +set (WITH_SERVER ON CACHE BOOL "qa default") +set (WITH_SAMPLE ON CACHE BOOL "qa default") +set (WITH_VERBOSE_WINPR_ASSERT OFF CACHE BOOL "qa default") +set (BUILD_SHARED_LIBS OFF CACHE BOOL "qa default") + +set (BUILD_WITH_CLANG_TIDY OFF CACHE BOOL "qa default") + +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ClangDetectTool.cmake) +clang_detect_tool(CLANG_EXE clang REQUIRED) +clang_detect_tool(CLANG_XX_EXE clang++ REQUIRED) + +set (CMAKE_C_COMPILER "${CLANG_EXE}" CACHE STRING "qa default") +set (CMAKE_CXX_COMPILER "${CLANG_XX_EXE}" CACHE STRING "qa default") diff --git a/ci/cmake-preloads/config-qa.cmake b/ci/cmake-preloads/config-qa.cmake new file mode 100644 index 000000000000..4b3953bec5b0 --- /dev/null +++ b/ci/cmake-preloads/config-qa.cmake @@ -0,0 +1,25 @@ +set (BUILD_TESTING ON CACHE BOOL "qa default") +set (CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "preload") +set (WITH_SAMPLE ON CACHE BOOL "qa default") +set (WITH_SERVER ON CACHE BOOL "qa default") +set (WITH_SHADOW ON CACHE BOOL "qa default") +set (WITH_PROXY ON CACHE BOOL "qa default") +set (WITH_PULSE ON CACHE BOOL "qa default") +set (WITH_CUPS ON CACHE BOOL "qa default") +set (WITH_PCSC ON CACHE BOOL "qa default") +set (WITH_SOXR ON CACHE BOOL "qa default") +set (WITH_SWSCALE ON CACHE BOOL "qa default") +set (WITH_DSP_FFMPEG ON CACHE BOOL "qa default") +set (WITH_FFMPEG ON CACHE BOOL "qa default") +set (WITH_SANITIZE_ADDRESS ON CACHE BOOL "qa default") +set (CMAKE_C_FLAGS "-Weverything -Wno-exit-time-destructors -Wno-cast-align -Wno-documentation -Wno-documentation-unknown-command -Wno-padded -Wno-covered-switch-default -Wno-declaration-after-statement" CACHE STRING "qa default") +set (CMAKE_CXX_FLAGS "-Weverything -Wno-exit-time-destructors -Wno-cast-align -Wno-documentation -Wno-documentation-unknown-command -Wno-padded -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-covered-switch-default -Wno-declaration-after-statement" CACHE STRING "qa default") + +set (BUILD_WITH_CLANG_TIDY ON CACHE BOOL "qa default") + +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/ClangDetectTool.cmake) +clang_detect_tool(CLANG_EXE clang REQUIRED) +clang_detect_tool(CLANG_XX_EXE clang++ REQUIRED) + +set (CMAKE_C_COMPILER "${CLANG_EXE}" CACHE STRING "qa default") +set (CMAKE_CXX_COMPILER "${CLANG_XX_EXE}" CACHE STRING "qa default") diff --git a/client/Android/CMakeLists.txt b/client/Android/CMakeLists.txt index 7fcdb6354a92..b3473a98af7e 100644 --- a/client/Android/CMakeLists.txt +++ b/client/Android/CMakeLists.txt @@ -48,7 +48,7 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp freerdp-client) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} dl) set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} jnigraphics) -set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${FREERDP_API_VERSION}) +#set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${FREERDP_API_VERSION}) target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries EXPORT AndroidTargets) diff --git a/client/Android/Studio/aFreeRDP/build.gradle b/client/Android/Studio/aFreeRDP/build.gradle index a625e6bb9510..96f7184d8ce4 100644 --- a/client/Android/Studio/aFreeRDP/build.gradle +++ b/client/Android/Studio/aFreeRDP/build.gradle @@ -1,5 +1,16 @@ -apply plugin: 'com.android.application' +plugins { + id 'com.gladed.androidgitversion' version '0.4.4' +} + +androidGitVersion { + abis = ['armeabi':1, 'armeabi-v7a':2, 'arm64-v8a':3, 'mips':5, 'mips64':6, 'x86':8, 'x86_64':9 ] + prefix '' +} + +println 'Version Name: ' + androidGitVersion.name() +println 'Version Code: ' + androidGitVersion.code() +apply plugin: 'com.android.application' android { compileSdkVersion = rootProject.ext.compileApi buildToolsVersion = rootProject.ext.toolsVersion @@ -9,8 +20,8 @@ android { minSdkVersion rootProject.ext.minApi targetSdkVersion rootProject.ext.targetApi vectorDrawables.useSupportLibrary = true - versionCode = rootProject.ext.versionCode - versionName = rootProject.ext.versionName + versionName = androidGitVersion.name() + versionCode = androidGitVersion.code() } signingConfigs { diff --git a/client/Android/Studio/aFreeRDP/src/main/AndroidManifest.xml b/client/Android/Studio/aFreeRDP/src/main/AndroidManifest.xml index 7d7558d52c63..8c46bc9fe6d3 100644 --- a/client/Android/Studio/aFreeRDP/src/main/AndroidManifest.xml +++ b/client/Android/Studio/aFreeRDP/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ - def stdout = new ByteArrayOutputStream() - exec { - commandLine 'git', 'describe', '--tags' - standardOutput = stdout - } - return stdout.toString().trim() -} - ext { - versionName = properties.get('VERSION_NAME', getVersionName()) - versionCode = properties.get('VERSION_CODE', 22) - compileApi = properties.get('COMPILE_API', 30) - targetApi = properties.get('TARGET_API', 30) - minApi = properties.get('MIN_API', 21) - toolsVersion = properties.get('TOOLS_VERSION', '30.0.3') + compileApi = properties.get('COMPILE_API', 31) + targetApi = properties.get('TARGET_API', 31) + minApi = properties.get('MIN_API', 23) + toolsVersion = properties.get('TOOLS_VERSION', '31.0.0') println '----------------- Project configuration -------------------' - println 'VERSION_NAME: ' + versionName - println 'VERSION_CODE: ' + versionCode println 'RELEASE_STORE_FILE: ' + RELEASE_STORE_FILE println 'RELEASE_KEY_ALIAS: ' + RELEASE_KEY_ALIAS println 'compile API: ' + compileApi @@ -49,17 +36,17 @@ ext { buildscript { repositories { - jcenter() + mavenCentral() google() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' + classpath 'com.android.tools.build:gradle:7.1.2' } } allprojects { repositories { - jcenter() + mavenCentral() google() } } diff --git a/client/Android/Studio/freeRDPCore/build.gradle b/client/Android/Studio/freeRDPCore/build.gradle index 713ad3722c96..e2224c21c75f 100644 --- a/client/Android/Studio/freeRDPCore/build.gradle +++ b/client/Android/Studio/freeRDPCore/build.gradle @@ -8,8 +8,6 @@ android { minSdkVersion rootProject.ext.minApi targetSdkVersion rootProject.ext.targetApi vectorDrawables.useSupportLibrary = true - versionCode = rootProject.ext.versionCode - versionName = rootProject.ext.versionName } buildTypes { diff --git a/client/Android/Studio/freeRDPCore/src/main/AndroidManifest.xml b/client/Android/Studio/freeRDPCore/src/main/AndroidManifest.xml index ef59325d2f3a..34613339f535 100644 --- a/client/Android/Studio/freeRDPCore/src/main/AndroidManifest.xml +++ b/client/Android/Studio/freeRDPCore/src/main/AndroidManifest.xml @@ -22,6 +22,7 @@ @@ -49,6 +50,7 @@ @@ -61,11 +63,13 @@ diff --git a/client/Android/Studio/freeRDPCore/src/main/java/com/freerdp/freerdpcore/services/LibFreeRDP.java b/client/Android/Studio/freeRDPCore/src/main/java/com/freerdp/freerdpcore/services/LibFreeRDP.java index 66f8cff6aa81..0f4a92188a6e 100644 --- a/client/Android/Studio/freeRDPCore/src/main/java/com/freerdp/freerdpcore/services/LibFreeRDP.java +++ b/client/Android/Studio/freeRDPCore/src/main/java/com/freerdp/freerdpcore/services/LibFreeRDP.java @@ -23,27 +23,23 @@ import com.freerdp.freerdpcore.presentation.ApplicationSettingsActivity; import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class LibFreeRDP { private static final String TAG = "LibFreeRDP"; private static EventListener listener; - private static boolean mHasH264 = true; + private static boolean mHasH264 = false; private static final LongSparseArray mInstanceState = new LongSparseArray<>(); - static + private static boolean tryLoad(String[] libraries) { - final String h264 = "openh264"; - final String[] libraries = { h264, - "freerdp-openssl", - "ssl", - "crypto", - "jpeg", - "winpr2", - "freerdp2", - "freerdp-client2", - "freerdp-android2" }; + boolean success = false; final String LD_PATH = System.getProperty("java.library.path"); for (String lib : libraries) @@ -52,15 +48,54 @@ public class LibFreeRDP { Log.v(TAG, "Trying to load library " + lib + " from LD_PATH: " + LD_PATH); System.loadLibrary(lib); + success = true; } catch (UnsatisfiedLinkError e) { Log.e(TAG, "Failed to load library " + lib + ": " + e.toString()); - if (lib.equals(h264)) + success = false; + break; + } + } + + return success; + } + + private static boolean tryLoad(String library) { - mHasH264 = false; + return tryLoad(new String[] { library }); } - } + static + { + try + { + System.loadLibrary("freerdp-android"); + String version = freerdp_get_jni_version(); + Pattern pattern = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+).*"); + Matcher matcher = pattern.matcher(version); + if (!matcher.matches() || (matcher.groupCount() < 3)) + throw new RuntimeException("APK broken: native library version " + version + + " does not meet requirements!"); + int major = Integer.parseInt(Objects.requireNonNull(matcher.group(1))); + int minor = Integer.parseInt(Objects.requireNonNull(matcher.group(2))); + int patch = Integer.parseInt(Objects.requireNonNull(matcher.group(3))); + + if (major > 2) + mHasH264 = freerdp_has_h264(); + else if (minor > 5) + mHasH264 = freerdp_has_h264(); + else if ((minor == 5) && (patch >= 1)) + mHasH264 = freerdp_has_h264(); + else + throw new RuntimeException("APK broken: native library version " + version + + " does not meet requirements!"); + Log.i(TAG, "Successfully loaded native library. H264 is " + + (mHasH264 ? "supported" : "not available")); + } + catch (UnsatisfiedLinkError e) + { + Log.e(TAG, "Failed to load library: " + e.toString()); + throw e; } } @@ -69,6 +104,8 @@ public static boolean hasH264Support() return mHasH264; } + private static native boolean freerdp_has_h264(); + private static native String freerdp_get_jni_version(); private static native String freerdp_get_version(); diff --git a/client/Android/Studio/freeRDPCore/src/main/res/values-ja/strings.xml b/client/Android/Studio/freeRDPCore/src/main/res/values-ja/strings.xml index 39561c693fff..d4cd51f05cee 100644 --- a/client/Android/Studio/freeRDPCore/src/main/res/values-ja/strings.xml +++ b/client/Android/Studio/freeRDPCore/src/main/res/values-ja/strings.xml @@ -115,7 +115,6 @@ 遠隔実行するプログラム 作業フォルダ Async channel - Async transport Async input Async update コンソールモード diff --git a/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings.xml b/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings.xml index d5016f7be7ba..9666f8dd0336 100644 --- a/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings.xml +++ b/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings.xml @@ -28,14 +28,14 @@ android:title="@string/settings_width" android:singleLine="true" freerdp:bounds_default="640" - freerdp:bounds_max="2048" + freerdp:bounds_max="65536" freerdp:bounds_min="640" /> diff --git a/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings_3g.xml b/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings_3g.xml index c9db098fc5f0..79a9f0f7db3f 100644 --- a/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings_3g.xml +++ b/client/Android/Studio/freeRDPCore/src/main/res/xml/screen_settings_3g.xml @@ -28,14 +28,14 @@ android:singleLine="true" android:title="@string/settings_width" freerdp:bounds_default="640" - freerdp:bounds_max="2048" + freerdp:bounds_max="65536" freerdp:bounds_min="640" /> diff --git a/client/Android/Studio/gradle/wrapper/gradle-wrapper.properties b/client/Android/Studio/gradle/wrapper/gradle-wrapper.properties index cbba7b087961..0416d39546d0 100644 --- a/client/Android/Studio/gradle/wrapper/gradle-wrapper.properties +++ b/client/Android/Studio/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Mar 15 09:39:30 CET 2021 +#Fri May 13 11:48:24 CEST 2022 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/client/Android/android_freerdp.c b/client/Android/android_freerdp.c index 4546f41c5592..406707dc7b09 100644 --- a/client/Android/android_freerdp.c +++ b/client/Android/android_freerdp.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,7 @@ #define TAG CLIENT_TAG("android") /* Defines the JNI version supported by this library. */ -#define FREERDP_JNI_VERSION "2.4.0" +#define FREERDP_JNI_VERSION "2.11.8" static void android_OnChannelConnectedEventHandler(void* context, ChannelConnectedEventArgs* e) { @@ -288,7 +289,7 @@ static BOOL android_Pointer_SetDefault(rdpContext* context) static BOOL android_register_pointer(rdpGraphics* graphics) { - rdpPointer pointer; + rdpPointer pointer = { 0 }; if (!graphics) return FALSE; @@ -522,7 +523,7 @@ static int android_freerdp_run(freerdp* instance) count += tmp; status = WaitForMultipleObjects(count, handles, FALSE, INFINITE); - if ((status == WAIT_FAILED)) + if (status == WAIT_FAILED) { WLog_ERR(TAG, "WaitForMultipleObjects failed with %" PRIu32 " [%08lX]", status, GetLastError()); @@ -996,7 +997,7 @@ static jboolean JNICALL jni_freerdp_send_clipboard_data(JNIEnv* env, jclass cls, ANDROID_EVENT* event; freerdp* inst = (freerdp*)instance; const jbyte* data = jdata != NULL ? (*env)->GetStringUTFChars(env, jdata, NULL) : NULL; - const size_t data_length = data ? (*env)->GetStringUTFLength(env, data) : 0; + const size_t data_length = data ? (*env)->GetStringUTFLength(env, jdata) : 0; jboolean ret = JNI_FALSE; event = (ANDROID_EVENT*)android_event_clipboard_new((void*)data, data_length); @@ -1024,6 +1025,15 @@ static jstring JNICALL jni_freerdp_get_jni_version(JNIEnv* env, jclass cls) return (*env)->NewStringUTF(env, FREERDP_JNI_VERSION); } +static jboolean JNICALL jni_freerdp_has_h264(JNIEnv* env, jclass cls) +{ + H264_CONTEXT* ctx = h264_context_new(FALSE); + if (!ctx) + return JNI_FALSE; + h264_context_free(ctx); + return JNI_TRUE; +} + static jstring JNICALL jni_freerdp_get_version(JNIEnv* env, jclass cls) { return (*env)->NewStringUTF(env, freerdp_get_version_string()); @@ -1062,7 +1072,8 @@ static JNINativeMethod methods[] = { { "freerdp_send_cursor_event", "(JIII)Z", &jni_freerdp_send_cursor_event }, { "freerdp_send_key_event", "(JIZ)Z", &jni_freerdp_send_key_event }, { "freerdp_send_unicodekey_event", "(JIZ)Z", &jni_freerdp_send_unicodekey_event }, - { "freerdp_send_clipboard_data", "(JLjava/lang/String;)Z", &jni_freerdp_send_clipboard_data } + { "freerdp_send_clipboard_data", "(JLjava/lang/String;)Z", &jni_freerdp_send_clipboard_data }, + { "freerdp_has_h264", "()Z", &jni_freerdp_has_h264 } }; static jclass gJavaActivityClass = NULL; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 7cba44e944e2..af7606bab7a3 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -43,7 +43,10 @@ if(FREERDP_VENDOR AND WITH_CLIENT) add_subdirectory(iOS) endif() else() - add_subdirectory(Mac) + option(WITH_CLIENT_MAC "Build native mac client" ON) + if (WITH_CLIENT_MAC) + add_subdirectory(Mac) + endif() endif() endif() diff --git a/client/Mac/CMakeLists.txt b/client/Mac/CMakeLists.txt index b3eb59187785..41b1db424f82 100644 --- a/client/Mac/CMakeLists.txt +++ b/client/Mac/CMakeLists.txt @@ -110,6 +110,16 @@ endforeach() set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Mac") +if(CMAKE_GENERATOR MATCHES "Ninja") + if (NOT ${CONFIGURATION} STREQUAL "") + set(safe_configuration "$CONFIGURATION") + else() + set(safe_configuration "") + endif() +else() + set(safe_configuration "$(CONFIGURATION)") +endif() + if (${BUILD_SHARED_LIBS}) # Add a post-build event to copy the dependent libraries in the framework bundle # Call install_name_tool to reassign the library install name @@ -118,15 +128,15 @@ if (${BUILD_SHARED_LIBS}) add_custom_command(TARGET ${MODULE_NAME} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E copy "$" - "${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/Contents/$" + "${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/Contents/$" COMMENT "Copying ${LIB} to output directory" COMMAND install_name_tool -change "$" "@executable_path/../Frameworks/${MODULE_OUTPUT_NAME}.framework/Contents/$" - "${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/${MODULE_OUTPUT_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/${MODULE_OUTPUT_NAME}" COMMENT Setting install name for ${LIB} COMMAND "${CMAKE_COMMAND}" -E echo install_name_tool -change "$" "@executable_path/../Frameworks/${MODULE_OUTPUT_NAME}.framework/Contents/$" - "${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/${MODULE_OUTPUT_NAME}") + "${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/${MODULE_OUTPUT_NAME}") endforeach() # Call install_name_tool to reassign the library install names in dependent libraries @@ -136,7 +146,7 @@ if (${BUILD_SHARED_LIBS}) add_custom_command(TARGET ${MODULE_NAME} POST_BUILD COMMAND install_name_tool -change "$" "@executable_path/../Frameworks/${MODULE_OUTPUT_NAME}.framework/Contents/$" - "${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/Contents/$" + "${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/Contents/$" COMMENT Setting install name for ${LIB} in module ${DEST}) endforeach() endforeach() @@ -144,7 +154,7 @@ if (${BUILD_SHARED_LIBS}) endif() # Add post-build NIB file generation in unix makefiles. XCode handles this implicitly. -# if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles") +# if(NOT "${CMAKE_GENERATOR}" MATCHES "Xcode") message(STATUS "Adding post-build NIB file generation event for ${MODULE_NAME}") # Make sure we can find the 'ibtool' program. If we can NOT find it we @@ -157,7 +167,7 @@ endif() # Make sure the 'Resources' Directory is correctly created before we build add_custom_command(TARGET ${MODULE_NAME} PRE_BUILD - COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/Versions/${MACOSX_BUNDLE_SHORT_VERSION_STRING}/Resources) + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/Versions/${MACOSX_BUNDLE_SHORT_VERSION_STRING}/Resources) # Compile the .xib files using the 'ibtool' program with the destination being the app package foreach(xib ${${MODULE_PREFIX}_XIBS}) @@ -165,23 +175,33 @@ endif() add_custom_command (TARGET ${MODULE_NAME} POST_BUILD COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text - --compile ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/Versions/${MACOSX_BUNDLE_SHORT_VERSION_STRING}/Resources/${XIB_WE}.nib ${CMAKE_CURRENT_SOURCE_DIR}/${xib} + --compile ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/Versions/${MACOSX_BUNDLE_SHORT_VERSION_STRING}/Resources/${XIB_WE}.nib ${CMAKE_CURRENT_SOURCE_DIR}/${xib} COMMENT "Compiling ${xib}") endforeach() # endif() +set(MAC_LIB_FRAMEWORK_HEADERS "") # Copy the public header files into the framework foreach(HEADER ${${MODULE_PREFIX}_HEADERS}) - # message("adding post-build dependency: ${LIB}") + #message("adding post-build dependency: ${MODULE_NAME} ${HEADER}") + if( POLICY CMP0058 ) + set(NinjaByproducts BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/${CONFIGURATION}/${MODULE_OUTPUT_NAME}.framework/Headers/${HEADER}) + endif() add_custom_command(TARGET ${MODULE_NAME} POST_BUILD - COMMAND ditto ${CMAKE_CURRENT_SOURCE_DIR}/${HEADER} ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/Headers/ + COMMAND ditto ${CMAKE_CURRENT_SOURCE_DIR}/${HEADER} ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/Headers/ + ${NinjaByproducts} COMMENT Copying public header files to ${MODULE_NAME}) + list(APPEND MAC_LIB_FRAMEWORK_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${CONFIGURATION}/${MODULE_OUTPUT_NAME}.framework/Headers/${HEADER}) endforeach() +add_custom_target( + prepare-framework-headers + DEPENDS ${MAC_LIB_FRAMEWORK_HEADERS} +) # Copy the FreeRDP header files into the framework add_custom_command(TARGET ${MODULE_NAME} POST_BUILD - COMMAND ditto ${CMAKE_SOURCE_DIR}/include/freerdp ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/Headers/freerdp - COMMAND ditto ${CMAKE_SOURCE_DIR}/winpr/include/winpr ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.framework/Headers/winpr + COMMAND ditto ${CMAKE_SOURCE_DIR}/include/freerdp ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.framework/Headers/freerdp + COMMAND ditto ${CMAKE_SOURCE_DIR}/winpr/include/winpr ${CMAKE_CURRENT_BINARY_DIR}/${CONFIGURATION}/${MODULE_OUTPUT_NAME}.framework/Headers/winpr COMMENT Copying FreeRDP header files to ${MODULE_NAME}) add_subdirectory(cli) diff --git a/client/Mac/cli/CMakeLists.txt b/client/Mac/cli/CMakeLists.txt index b481ab5f7563..e2822d027828 100644 --- a/client/Mac/cli/CMakeLists.txt +++ b/client/Mac/cli/CMakeLists.txt @@ -55,6 +55,7 @@ add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_RESOURCES}) set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${MODULE_OUTPUT_NAME}") +add_dependencies(${MODULE_NAME} prepare-framework-headers) # This is necessary for the xib file part below configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) @@ -66,9 +67,19 @@ set_target_properties(${MODULE_NAME} PROPERTIES RESOURCE "${${MODULE_PREFIX}_RES set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -F../") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -F../") +if(CMAKE_GENERATOR MATCHES "Ninja") + if (NOT ${CONFIGURATION} STREQUAL "") + set(safe_configuration "$CONFIGURATION") + else() + set(safe_configuration "") + endif() +else() + set(safe_configuration "$(CONFIGURATION)") +endif() + # Tell XCode where to look for the MacFreeRDP framework set_target_properties(${MODULE_NAME} PROPERTIES XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS - "${XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS} ${CMAKE_CURRENT_BINARY_DIR}/../$(CONFIGURATION)") + "${XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS} ${CMAKE_CURRENT_BINARY_DIR}/../${safe_configuration}") # Set the info plist to the custom instance set_target_properties(${MODULE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) @@ -80,15 +91,15 @@ set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Mac") # Embed the FreeRDP framework into the app bundle add_custom_command(TARGET ${MODULE_NAME} POST_BUILD - COMMAND mkdir ARGS -p ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.app/Contents/Frameworks - COMMAND ditto ${CMAKE_CURRENT_BINARY_DIR}/../$(CONFIGURATION)/MacFreeRDP.framework ${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.app/Contents/Frameworks/MacFreeRDP.framework + COMMAND mkdir ARGS -p ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.app/Contents/Frameworks + COMMAND ditto ${CMAKE_CURRENT_BINARY_DIR}/../${safe_configuration}/MacFreeRDP.framework ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.app/Contents/Frameworks/MacFreeRDP.framework COMMAND install_name_tool -change "@executable_path/../Frameworks/MacFreeRDP.framework/Versions/${MAC_OS_X_BUNDLE_BUNDLE_VERSION}/MacFreeRDP" "@executable_path/../Frameworks/MacFreeRDP.framework/Versions/Current/MacFreeRDP" - "${CMAKE_CURRENT_BINARY_DIR}/$(CONFIGURATION)/${MODULE_OUTPUT_NAME}.app/Contents/MacOS/${MODULE_NAME}" + "${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.app/Contents/MacOS/${MODULE_NAME}" COMMENT Setting install name for MacFreeRDP) # Add post-build NIB file generation in unix makefiles. XCode handles this implicitly. -if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles") +if(NOT "${CMAKE_GENERATOR}" MATCHES "Xcode") message(STATUS "Adding post-build NIB file generation event for ${MODULE_NAME}") # Make sure we can find the 'ibtool' program. If we can NOT find it we skip generation of this project @@ -99,7 +110,7 @@ if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles") endif() # Make sure the 'Resources' Directory is correctly created before we build - add_custom_command(TARGET ${MODULE_NAME} PRE_BUILD COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/\${CONFIGURATION}/${MODULE_OUTPUT_NAME}.app/Contents/Resources) + add_custom_command(TARGET ${MODULE_NAME} PRE_BUILD COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.app/Contents/Resources) # Compile the .xib files using the 'ibtool' program with the destination being the app package foreach(xib ${${MODULE_PREFIX}_XIBS}) @@ -107,7 +118,7 @@ if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles") add_custom_command (TARGET ${MODULE_NAME} POST_BUILD COMMAND ${IBTOOL} --errors --warnings --notices --output-format human-readable-text - --compile ${CMAKE_CURRENT_BINARY_DIR}/\${CONFIGURATION}/${MODULE_OUTPUT_NAME}.app/Contents/Resources/${XIB_WE}.nib ${CMAKE_CURRENT_SOURCE_DIR}/${xib} + --compile ${CMAKE_CURRENT_BINARY_DIR}/${safe_configuration}/${MODULE_OUTPUT_NAME}.app/Contents/Resources/${XIB_WE}.nib ${CMAKE_CURRENT_SOURCE_DIR}/${xib} COMMENT "Compiling ${xib}") endforeach() diff --git a/client/Wayland/wlf_cliprdr.c b/client/Wayland/wlf_cliprdr.c index 0762cfa3deaf..0f0195d7cd52 100644 --- a/client/Wayland/wlf_cliprdr.c +++ b/client/Wayland/wlf_cliprdr.c @@ -80,6 +80,7 @@ struct wlf_clipboard FILE* responseFile; UINT32 responseFormat; const char* responseMime; + CRITICAL_SECTION lock; }; static BOOL wlf_mime_is_text(const char* mime) @@ -275,7 +276,7 @@ BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent* return TRUE; case UWAC_EVENT_CLIPBOARD_OFFER: - WLog_Print(clipboard->log, WLOG_INFO, "client announces mime %s", event->mime); + WLog_Print(clipboard->log, WLOG_DEBUG, "client announces mime %s", event->mime); wlf_cliprdr_add_client_format(clipboard, event->mime); return TRUE; @@ -387,6 +388,8 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* wfClipboard* clipboard = (wfClipboard*)context; size_t x; WINPR_UNUSED(seat); + + EnterCriticalSection(&clipboard->lock); clipboard->responseMime = NULL; for (x = 0; x < ARRAYSIZE(mime_html); x++) @@ -427,6 +430,8 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* if (clipboard->responseMime != NULL) { + if (clipboard->responseFile != NULL) + fclose(clipboard->responseFile); clipboard->responseFile = fdopen(fd, "w"); if (clipboard->responseFile) @@ -436,6 +441,7 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* "failed to open clipboard file descriptor for MIME %s", clipboard->responseMime); } + LeaveCriticalSection(&clipboard->lock); } static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context) @@ -673,6 +679,8 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context, const WCHAR* wdata = (const WCHAR*)formatDataResponse->requestedFormatData; wfClipboard* clipboard = (wfClipboard*)context->custom; + EnterCriticalSection(&clipboard->lock); + if (size > INT_MAX * sizeof(WCHAR)) return ERROR_INTERNAL_ERROR; @@ -694,10 +702,16 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context, break; } - fwrite(data, 1, size, clipboard->responseFile); - fclose(clipboard->responseFile); + if (clipboard->responseFile) + { + fwrite(data, 1, size, clipboard->responseFile); + fclose(clipboard->responseFile); + clipboard->responseFile = NULL; + } rc = CHANNEL_RC_OK; free(cdata); + + LeaveCriticalSection(&clipboard->lock); return rc; } @@ -829,17 +843,24 @@ static UINT wlf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegat wfClipboard* wlf_clipboard_new(wlfContext* wfc) { rdpChannels* channels; - wfClipboard* clipboard; + wfClipboard* clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard)); - if (!(clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard)))) - return NULL; + if (!clipboard) + goto fail; + InitializeCriticalSection(&clipboard->lock); clipboard->wfc = wfc; channels = wfc->context.channels; clipboard->log = WLog_Get(TAG); clipboard->channels = channels; clipboard->system = ClipboardCreate(); + if (!clipboard->system) + goto fail; + clipboard->delegate = ClipboardGetDelegate(clipboard->system); + if (!clipboard->delegate) + goto fail; + clipboard->delegate->custom = clipboard; /* TODO: set up a filesystem base path for local URI */ /* clipboard->delegate->basePath = "file:///tmp/foo/bar/gaga"; */ @@ -848,6 +869,10 @@ wfClipboard* wlf_clipboard_new(wlfContext* wfc) clipboard->delegate->ClipboardFileRangeSuccess = wlf_cliprdr_clipboard_file_range_success; clipboard->delegate->ClipboardFileRangeFailure = wlf_cliprdr_clipboard_file_range_failure; return clipboard; + +fail: + wlf_clipboard_free(clipboard); + return NULL; } void wlf_clipboard_free(wfClipboard* clipboard) @@ -858,6 +883,12 @@ void wlf_clipboard_free(wfClipboard* clipboard) wlf_cliprdr_free_server_formats(clipboard); wlf_cliprdr_free_client_formats(clipboard); ClipboardDestroy(clipboard->system); + + EnterCriticalSection(&clipboard->lock); + if (clipboard->responseFile) + fclose(clipboard->responseFile); + LeaveCriticalSection(&clipboard->lock); + DeleteCriticalSection(&clipboard->lock); free(clipboard); } diff --git a/client/Wayland/wlf_input.c b/client/Wayland/wlf_input.c index 22041eaa2588..735245b3bd8c 100644 --- a/client/Wayland/wlf_input.c +++ b/client/Wayland/wlf_input.c @@ -134,26 +134,44 @@ BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* } BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev) +{ + wlfContext* context; + if (!instance || !instance->context || !ev) + return FALSE; + + context = (wlfContext*)instance->context; + ArrayList_Add(context->events, ev); + return TRUE; +} + +BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev) +{ + wlfContext* context; + if (!instance || !instance->context || !ev) + return FALSE; + + context = (wlfContext*)instance->context; + ArrayList_Add(context->events, ev); + return TRUE; +} + +static BOOL wlf_handle_wheel(freerdp* instance, uint32_t x, uint32_t y, uint32_t axis, + int32_t value) { rdpInput* input; UINT16 flags = 0; int32_t direction; - uint32_t x, y; - uint32_t i; + uint32_t avalue = abs(value); - if (!instance || !ev || !instance->input) - return FALSE; - - x = ev->x; - y = ev->y; + input = instance->input; if (!wlf_scale_coordinates(instance->context, &x, &y, TRUE)) return FALSE; input = instance->input; - direction = ev->value; - switch (ev->axis) + direction = value; + switch (axis) { case WL_POINTER_AXIS_VERTICAL_SCROLL: flags |= PTR_FLAGS_WHEEL; @@ -176,16 +194,102 @@ BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev) * positive: 0 ... 0xFF -> slow ... fast * negative: 0 ... 0xFF -> fast ... slow */ - for (i = 0; i < abs(direction); i++) + + while (avalue > 0) { - uint32_t cflags = flags | 0x78; + const uint32_t cval = avalue > 0xFF ? 0xFF : avalue; + uint32_t cflags = flags | cval; /* Convert negative values to 9bit twos complement */ if (flags & PTR_FLAGS_WHEEL_NEGATIVE) - cflags = (flags & 0xFF00) | (0x100 - (cflags & 0xFF)); + cflags = (flags & 0xFF00) | (0x100 - cval); if (!freerdp_input_send_mouse_event(input, cflags, (UINT16)x, (UINT16)y)) return FALSE; + + avalue -= cval; } + return TRUE; +} +BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev) +{ + BOOL success = TRUE; + BOOL handle = FALSE; + size_t x; + wlfContext* context; + enum wl_pointer_axis_source source; + + if (!instance || !ev || !instance->input || !instance->context) + return FALSE; + + context = (wlfContext*)instance->context; + + for (x = 0; x < ArrayList_Count(context->events); x++) + { + UwacEvent* ev = ArrayList_GetItem(context->events, x); + if (!ev) + continue; + if (ev->type == UWAC_EVENT_POINTER_SOURCE) + { + handle = TRUE; + source = ev->mouse_source.axis_source; + } + } + + /* We need source events to determine how to interpret the data */ + if (handle) + { + for (x = 0; x < ArrayList_Count(context->events); x++) + { + UwacEvent* ev = ArrayList_GetItem(context->events, x); + if (!ev) + continue; + + switch (source) + { + /* If we have a mouse wheel, just use discrete data */ + case WL_POINTER_AXIS_SOURCE_WHEEL: +#if defined(WL_POINTER_AXIS_SOURCE_WHEEL_TILT_SINCE_VERSION) + case WL_POINTER_AXIS_SOURCE_WHEEL_TILT: +#endif + if (ev->type == UWAC_EVENT_POINTER_AXIS_DISCRETE) + { + /* Get the number of steps, multiply by default step width of 120 */ + int32_t val = ev->mouse_axis.value * 0x78; + /* No wheel event received, success! */ + if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y, + ev->mouse_axis.axis, val)) + success = FALSE; + } + break; + /* If we have a touch pad we get actual data, scale */ + case WL_POINTER_AXIS_SOURCE_FINGER: + case WL_POINTER_AXIS_SOURCE_CONTINUOUS: + if (ev->type == UWAC_EVENT_POINTER_AXIS) + { + double dval = wl_fixed_to_double(ev->mouse_axis.value); + int32_t val = dval * 0x78 / 10.0; + if (!wlf_handle_wheel(instance, ev->mouse_axis.x, ev->mouse_axis.y, + ev->mouse_axis.axis, val)) + success = FALSE; + } + break; + default: + break; + } + } + } + ArrayList_Clear(context->events); + return success; +} + +BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev) +{ + wlfContext* context; + if (!instance || !instance->context || !ev) + return FALSE; + + context = (wlfContext*)instance->context; + ArrayList_Add(context->events, ev); return TRUE; } diff --git a/client/Wayland/wlf_input.h b/client/Wayland/wlf_input.h index 2382dd8b84b9..ff41300c5ec0 100644 --- a/client/Wayland/wlf_input.h +++ b/client/Wayland/wlf_input.h @@ -30,6 +30,9 @@ BOOL wlf_handle_pointer_enter(freerdp* instance, const UwacPointerEnterLeaveEven BOOL wlf_handle_pointer_motion(freerdp* instance, const UwacPointerMotionEvent* ev); BOOL wlf_handle_pointer_buttons(freerdp* instance, const UwacPointerButtonEvent* ev); BOOL wlf_handle_pointer_axis(freerdp* instance, const UwacPointerAxisEvent* ev); +BOOL wlf_handle_pointer_axis_discrete(freerdp* instance, const UwacPointerAxisEvent* ev); +BOOL wlf_handle_pointer_frame(freerdp* instance, const UwacPointerFrameEvent* ev); +BOOL wlf_handle_pointer_source(freerdp* instance, const UwacPointerSourceEvent* ev); BOOL wlf_handle_touch_up(freerdp* instance, const UwacTouchUp* ev); BOOL wlf_handle_touch_down(freerdp* instance, const UwacTouchDown* ev); BOOL wlf_handle_touch_motion(freerdp* instance, const UwacTouchMotion* ev); diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index aca7342118c8..5988aed6b03f 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -225,6 +225,7 @@ static BOOL wl_post_connect(freerdp* instance) wlfContext* context; rdpSettings* settings; char* title = "FreeRDP"; + char* app_id = "wlfreerdp"; UINT32 w, h; if (!instance || !instance->context) @@ -266,6 +267,7 @@ static BOOL wl_post_connect(freerdp* instance) UwacWindowSetFullscreenState(window, NULL, instance->context->settings->Fullscreen); UwacWindowSetTitle(window, title); + UwacWindowSetAppId(window, app_id); UwacWindowSetOpaqueRegion(context->window, 0, 0, w, h); instance->update->BeginPaint = wl_begin_paint; instance->update->EndPaint = wl_end_paint; @@ -331,12 +333,15 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display) break; case UWAC_EVENT_FRAME_DONE: + { + UwacReturnCode r; EnterCriticalSection(&context->critical); - rc = UwacWindowSubmitBuffer(context->window, false); + r = UwacWindowSubmitBuffer(context->window, false); LeaveCriticalSection(&context->critical); - if (rc != UWAC_SUCCESS) + if (r != UWAC_SUCCESS) return FALSE; - break; + } + break; case UWAC_EVENT_POINTER_ENTER: if (!wlf_handle_pointer_enter(instance, &event.mouse_enter_leave)) @@ -357,12 +362,22 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display) break; case UWAC_EVENT_POINTER_AXIS: + if (!wlf_handle_pointer_axis(instance, &event.mouse_axis)) + return FALSE; break; case UWAC_EVENT_POINTER_AXIS_DISCRETE: - if (!wlf_handle_pointer_axis(instance, &event.mouse_axis)) + if (!wlf_handle_pointer_axis_discrete(instance, &event.mouse_axis)) return FALSE; + break; + case UWAC_EVENT_POINTER_FRAME: + if (!wlf_handle_pointer_frame(instance, &event.mouse_frame)) + return FALSE; + break; + case UWAC_EVENT_POINTER_SOURCE: + if (!wlf_handle_pointer_source(instance, &event.mouse_source)) + return FALSE; break; case UWAC_EVENT_KEY: @@ -556,8 +571,37 @@ static int wlf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type) return 1; } +static void wlf_client_free(freerdp* instance, rdpContext* context) +{ + wlfContext* wlf = (wlfContext*)instance->context; + + if (!context) + return; + + if (wlf->display) + UwacCloseDisplay(&wlf->display); + + if (wlf->displayHandle) + CloseHandle(wlf->displayHandle); + ArrayList_Free(wlf->events); + DeleteCriticalSection(&wlf->critical); +} + +static void* uwac_event_clone(void* val) +{ + UwacEvent* copy; + UwacEvent* ev = (UwacEvent*)val; + + copy = calloc(1, sizeof(UwacEvent)); + if (!copy) + return NULL; + *copy = *ev; + return copy; +} + static BOOL wlf_client_new(freerdp* instance, rdpContext* context) { + wObject* obj; UwacReturnCode status; wlfContext* wfl = (wlfContext*)context; @@ -585,24 +629,17 @@ static BOOL wlf_client_new(freerdp* instance, rdpContext* context) if (!wfl->displayHandle) return FALSE; - InitializeCriticalSection(&wfl->critical); - - return TRUE; -} - -static void wlf_client_free(freerdp* instance, rdpContext* context) -{ - wlfContext* wlf = (wlfContext*)instance->context; + wfl->events = ArrayList_New(FALSE); + if (!wfl->events) + return FALSE; - if (!context) - return; + obj = ArrayList_Object(wfl->events); + obj->fnObjectNew = uwac_event_clone; + obj->fnObjectFree = free; - if (wlf->display) - UwacCloseDisplay(&wlf->display); + InitializeCriticalSection(&wfl->critical); - if (wlf->displayHandle) - CloseHandle(wlf->displayHandle); - DeleteCriticalSection(&wlf->critical); + return TRUE; } static int wfl_client_start(rdpContext* context) diff --git a/client/Wayland/wlfreerdp.h b/client/Wayland/wlfreerdp.h index d84f91cbba2a..6af13bbbc055 100644 --- a/client/Wayland/wlfreerdp.h +++ b/client/Wayland/wlfreerdp.h @@ -53,6 +53,7 @@ struct wlf_context wlfDispContext* disp; wLog* log; CRITICAL_SECTION critical; + wArrayList* events; }; BOOL wlf_scale_coordinates(rdpContext* context, UINT32* px, UINT32* py, BOOL fromLocalToRDP); diff --git a/client/Windows/wf_client.c b/client/Windows/wf_client.c index f61f0c8d2517..5ee059402c14 100644 --- a/client/Windows/wf_client.c +++ b/client/Windows/wf_client.c @@ -118,6 +118,18 @@ static BOOL wf_end_paint(rdpContext* context) updateRect.top = extents->top; updateRect.right = extents->right; updateRect.bottom = extents->bottom; + + if (wfc->xScrollVisible) + { + updateRect.left -= MIN(updateRect.left, wfc->xCurrentScroll); + updateRect.right -= MIN(updateRect.right, wfc->xCurrentScroll); + } + if (wfc->yScrollVisible) + { + updateRect.top -= MIN(updateRect.top, wfc->yCurrentScroll); + updateRect.bottom -= MIN(updateRect.bottom, wfc->yCurrentScroll); + } + InvalidateRect(wfc->hwnd, &updateRect, FALSE); if (wfc->rail) diff --git a/client/X11/xf_client.c b/client/X11/xf_client.c index 114dc6d88ee1..bd3eb0d5663e 100644 --- a/client/X11/xf_client.c +++ b/client/X11/xf_client.c @@ -27,6 +27,7 @@ #endif #include +#include #include #include @@ -814,8 +815,6 @@ void xf_lock_x11_(xfContext* xfc, const char* fkt) else XLockDisplay(xfc->display); - if (xfc->locked) - WLog_WARN(TAG, "%s:\t[%" PRIu32 "] recursive lock from %s", __FUNCTION__, xfc->locked, fkt); xfc->locked++; WLog_VRB(TAG, "%s:\t[%" PRIu32 "] from %s", __FUNCTION__, xfc->locked, fkt); } @@ -1183,20 +1182,12 @@ static BOOL xf_pre_connect(freerdp* instance) if (!settings->Username && !settings->CredentialsFromStdin && !settings->SmartcardLogon) { - int rc; char login_name[MAX_PATH] = { 0 }; + ULONG size = sizeof(login_name) - 1; -#ifdef HAVE_GETLOGIN_R - rc = getlogin_r(login_name, sizeof(login_name)); -#else - strncpy(login_name, getlogin(), sizeof(login_name)); - rc = 0; -#endif - if (rc == 0) + if (GetUserNameExA(NameSamCompatible, login_name, &size)) { - settings->Username = _strdup(login_name); - - if (!settings->Username) + if (!freerdp_settings_set_string(settings, FreeRDP_Username, login_name)) return FALSE; WLog_INFO(TAG, "No user name set. - Using login name: %s", settings->Username); @@ -1263,6 +1254,7 @@ static BOOL xf_post_connect(freerdp* instance) context = instance->context; settings = instance->settings; update = context->update; + BOOL serverIsWindowsPlatform; if (!gdi_init(instance, xf_get_local_color_format(xfc, TRUE))) return FALSE; @@ -1332,7 +1324,8 @@ static BOOL xf_post_connect(freerdp* instance) update->SetKeyboardIndicators = xf_keyboard_set_indicators; update->SetKeyboardImeStatus = xf_keyboard_set_ime_status; - if (!(xfc->clipboard = xf_clipboard_new(xfc))) + serverIsWindowsPlatform = (settings->OsMajorType == OSMAJORTYPE_WINDOWS); + if (!(xfc->clipboard = xf_clipboard_new(xfc, !serverIsWindowsPlatform))) return FALSE; if (!(xfc->xfDisp = xf_disp_new(xfc))) @@ -1391,7 +1384,10 @@ static int xf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type) const char* str_data = freerdp_get_logon_error_info_data(data); const char* str_type = freerdp_get_logon_error_info_type(type); WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type); - xf_rail_disable_remoteapp_mode(xfc); + if(type != LOGON_MSG_SESSION_CONTINUE) + { + xf_rail_disable_remoteapp_mode(xfc); + } return 1; } @@ -1468,7 +1464,7 @@ static BOOL handle_window_events(freerdp* instance) { if (!xf_process_x_events(instance)) { - WLog_INFO(TAG, "Closed from X11"); + WLog_DBG(TAG, "Closed from X11"); return FALSE; } } @@ -1512,6 +1508,84 @@ static DWORD WINAPI xf_client_thread(LPVOID param) else if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED) exit_code = XF_EXIT_NEGO_FAILURE; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_LOGON_FAILURE) + exit_code = XF_EXIT_LOGON_FAILURE; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_ACCOUNT_LOCKED_OUT) + exit_code = XF_EXIT_ACCOUNT_LOCKED_OUT; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_PRE_CONNECT_FAILED) + exit_code = XF_EXIT_PRE_CONNECT_FAILED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_UNDEFINED) + exit_code = XF_EXIT_CONNECT_UNDEFINED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_POST_CONNECT_FAILED) + exit_code = XF_EXIT_POST_CONNECT_FAILED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_DNS_ERROR) + exit_code = XF_EXIT_DNS_ERROR; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_DNS_NAME_NOT_FOUND) + exit_code = XF_EXIT_DNS_NAME_NOT_FOUND; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_FAILED) + exit_code = XF_EXIT_CONNECT_FAILED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_MCS_CONNECT_INITIAL_ERROR) + exit_code = XF_EXIT_MCS_CONNECT_INITIAL_ERROR; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_TLS_CONNECT_FAILED) + exit_code = XF_EXIT_TLS_CONNECT_FAILED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_INSUFFICIENT_PRIVILEGES) + exit_code = XF_EXIT_INSUFFICIENT_PRIVILEGES; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_CANCELLED) + exit_code = XF_EXIT_CONNECT_CANCELLED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_SECURITY_NEGO_CONNECT_FAILED) + exit_code = XF_EXIT_SECURITY_NEGO_CONNECT_FAILED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_TRANSPORT_FAILED) + exit_code = XF_EXIT_CONNECT_TRANSPORT_FAILED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED) + exit_code = XF_EXIT_CONNECT_PASSWORD_EXPIRED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_PASSWORD_MUST_CHANGE) + exit_code = XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_KDC_UNREACHABLE) + exit_code = XF_EXIT_CONNECT_KDC_UNREACHABLE; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_ACCOUNT_DISABLED) + exit_code = XF_EXIT_CONNECT_ACCOUNT_DISABLED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_PASSWORD_CERTAINLY_EXPIRED) + exit_code = XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_CLIENT_REVOKED) + exit_code = XF_EXIT_CONNECT_CLIENT_REVOKED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_WRONG_PASSWORD) + exit_code = XF_EXIT_CONNECT_WRONG_PASSWORD; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_ACCESS_DENIED) + exit_code = XF_EXIT_CONNECT_ACCESS_DENIED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_ACCOUNT_RESTRICTION) + exit_code = XF_EXIT_CONNECT_ACCOUNT_RESTRICTION; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_ACCOUNT_EXPIRED) + exit_code = XF_EXIT_CONNECT_ACCOUNT_EXPIRED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_LOGON_TYPE_NOT_GRANTED) + exit_code = XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED; + else if (freerdp_get_last_error(instance->context) == + FREERDP_ERROR_CONNECT_NO_OR_MISSING_CREDENTIALS) + exit_code = XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS; else exit_code = XF_EXIT_CONN_FAILED; } @@ -1676,7 +1750,7 @@ static DWORD WINAPI xf_client_thread(LPVOID param) DWORD xf_exit_code_from_disconnect_reason(DWORD reason) { - if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_NEGO_FAILURE)) + if (reason == 0 || (reason >= XF_EXIT_PARSE_ARGUMENTS && reason <= XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS)) return reason; /* License error set */ else if (reason >= 0x100 && reason <= 0x10A) diff --git a/client/X11/xf_cliprdr.c b/client/X11/xf_cliprdr.c index e08048f88b7e..513c79c2462c 100644 --- a/client/X11/xf_cliprdr.c +++ b/client/X11/xf_cliprdr.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -115,6 +116,14 @@ struct xf_clipboard /* last sent data */ CLIPRDR_FORMAT* lastSentFormats; UINT32 lastSentNumFormats; + + /* Lock protecting clipboard->data / data_raw cache against races + * between the X11 event thread (reader in + * xf_cliprdr_process_selection_request) and the cliprdr channel + * thread (writer in xf_cliprdr_server_format_data_response and + * xf_cliprdr_clear_cached_data). Backport of upstream commit + * d3e8b3b9365be96a4f11dda149d71b3287227d0a (CVE-2026-25959). */ + CRITICAL_SECTION lock; }; static UINT xf_cliprdr_send_client_format_list(xfClipboard* clipboard); @@ -577,7 +586,13 @@ static BOOL xf_clipboard_changed(xfClipboard* clipboard, const CLIPRDR_FORMAT* f static void xf_clipboard_formats_free(xfClipboard* clipboard) { + WINPR_ASSERT(clipboard); + + /* Synchronize RDP/X11 thread with channel thread */ + xf_lock_x11(clipboard->xfc); xf_cliprdr_free_formats(clipboard->lastSentFormats, clipboard->lastSentNumFormats); + xf_unlock_x11(clipboard->xfc); + clipboard->lastSentFormats = NULL; clipboard->lastSentNumFormats = 0; } @@ -996,6 +1011,13 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, matchingFormat = (formatId == clipboard->data_format_id) && (formatName == clipboard->data_format_name); + /* Lock the cached data while we read/free it. The cliprdr + * channel thread may concurrently free and re-populate + * clipboard->data / clipboard->data_raw via + * xf_cliprdr_server_format_data_response, leading to a heap + * use-after-free passed to XChangeProperty (CVE-2026-25959). */ + EnterCriticalSection(&clipboard->lock); + if (matchingFormat && (clipboard->data != 0) && !rawTransfer) { /* Cached converted clipboard data available. Send it now */ @@ -1029,6 +1051,8 @@ static BOOL xf_cliprdr_process_selection_request(xfClipboard* clipboard, delayRespond = TRUE; xf_cliprdr_send_data_request(clipboard, formatId); } + + LeaveCriticalSection(&clipboard->lock); } } @@ -1354,7 +1378,9 @@ static UINT xf_cliprdr_server_format_list(CliprdrClientContext* context, xfContext* xfc = clipboard->xfc; UINT ret; xf_clipboard_formats_free(clipboard); + EnterCriticalSection(&clipboard->lock); xf_cliprdr_clear_cached_data(clipboard); + LeaveCriticalSection(&clipboard->lock); clipboard->data_format_id = -1; clipboard->data_format_name = NULL; @@ -1504,6 +1530,12 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context, if (!clipboard->respond) return CHANNEL_RC_OK; + /* Hold the cache lock from the moment we drop the previous cached + * data through producing and consuming the new cached data, so the + * X11 event thread can never observe a freed pointer. Backport of + * upstream commit d3e8b3b9365be96a4f11dda149d71b3287227d0a + * (CVE-2026-25959). */ + EnterCriticalSection(&clipboard->lock); xf_cliprdr_clear_cached_data(clipboard); pDstData = NULL; DstSize = 0; @@ -1570,9 +1602,10 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context, { if (SrcSize == 0) { - WLog_INFO(TAG, "skipping, empty data detected!!!"); + WLog_DBG(TAG, "skipping, empty data detected!"); free(clipboard->respond); clipboard->respond = NULL; + LeaveCriticalSection(&clipboard->lock); return CHANNEL_RC_OK; } @@ -1614,6 +1647,7 @@ xf_cliprdr_server_format_data_response(CliprdrClientContext* context, } xf_cliprdr_provide_data(clipboard, clipboard->respond, pDstData, DstSize); + LeaveCriticalSection(&clipboard->lock); { union { @@ -1756,7 +1790,27 @@ static UINT xf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegate return clipboard->context->ClientFileContentsResponse(clipboard->context, &response); } -xfClipboard* xf_clipboard_new(xfContext* xfc) +static BOOL xf_cliprdr_clipboard_is_valid_unix_filename(LPCWSTR filename) +{ + LPCWSTR c; + + if (!filename) + return FALSE; + + if (filename[0] == L'\0') + return FALSE; + + /* Reserved characters */ + for (c = filename; *c; ++c) + { + if (*c == L'/') + return FALSE; + } + + return TRUE; +} + +xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction) { int i, n = 0; rdpChannels* channels; @@ -1769,6 +1823,13 @@ xfClipboard* xf_clipboard_new(xfContext* xfc) return NULL; } + if (!InitializeCriticalSectionAndSpinCount(&clipboard->lock, 4000)) + { + WLog_ERR(TAG, "failed to initialize clipboard cache lock"); + free(clipboard); + return NULL; + } + xfc->clipboard = clipboard; clipboard->xfc = xfc; channels = ((rdpContext*)xfc)->channels; @@ -1885,6 +1946,13 @@ xfClipboard* xf_clipboard_new(xfContext* xfc) clipboard->delegate->ClipboardFileSizeFailure = xf_cliprdr_clipboard_file_size_failure; clipboard->delegate->ClipboardFileRangeSuccess = xf_cliprdr_clipboard_file_range_success; clipboard->delegate->ClipboardFileRangeFailure = xf_cliprdr_clipboard_file_range_failure; + + if (relieveFilenameRestriction) + { + WLog_DBG(TAG, "Relieving CLIPRDR filename restriction"); + clipboard->delegate->IsFileNameComponentValid = xf_cliprdr_clipboard_is_valid_unix_filename; + } + return clipboard; error: @@ -1892,6 +1960,7 @@ xfClipboard* xf_clipboard_new(xfContext* xfc) free(clipboard->clientFormats[i].formatName); ClipboardDestroy(clipboard->system); + DeleteCriticalSection(&clipboard->lock); free(clipboard); return NULL; } @@ -1924,6 +1993,7 @@ void xf_clipboard_free(xfClipboard* clipboard) free(clipboard->data_raw); free(clipboard->respond); free(clipboard->incr_data); + DeleteCriticalSection(&clipboard->lock); free(clipboard); } diff --git a/client/X11/xf_cliprdr.h b/client/X11/xf_cliprdr.h index f2262f79eca6..7f571c477884 100644 --- a/client/X11/xf_cliprdr.h +++ b/client/X11/xf_cliprdr.h @@ -25,7 +25,7 @@ #include -xfClipboard* xf_clipboard_new(xfContext* xfc); +xfClipboard* xf_clipboard_new(xfContext* xfc, BOOL relieveFilenameRestriction); void xf_clipboard_free(xfClipboard* clipboard); void xf_cliprdr_init(xfContext* xfc, CliprdrClientContext* cliprdr); diff --git a/client/X11/xf_disp.c b/client/X11/xf_disp.c index 316e1829b5be..ef0cb93ff398 100644 --- a/client/X11/xf_disp.c +++ b/client/X11/xf_disp.c @@ -93,7 +93,7 @@ static BOOL xf_update_last_sent(xfDispContext* xfDisp) static BOOL xf_disp_sendResize(xfDispContext* xfDisp) { - DISPLAY_CONTROL_MONITOR_LAYOUT layout; + DISPLAY_CONTROL_MONITOR_LAYOUT layout = { 0 }; xfContext* xfc; rdpSettings* settings; @@ -242,7 +242,24 @@ static void xf_disp_OnTimer(void* context, TimerEventArgs* e) if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings)) return; - if (!xfDisp->activated || settings->Fullscreen) + if (!xfDisp->activated || xfc->fullscreen) + return; + + xf_disp_sendResize(xfDisp); +} + +static void xf_disp_OnWindowStateChange(void* context, WindowStateChangeEventArgs* e) +{ + xfContext* xfc; + xfDispContext* xfDisp; + rdpSettings* settings; + + WINPR_UNUSED(e); + + if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings)) + return; + + if (!xfDisp->activated || !xfc->fullscreen) return; xf_disp_sendResize(xfDisp); @@ -274,6 +291,7 @@ xfDispContext* xf_disp_new(xfContext* xfc) PubSub_SubscribeActivated(xfc->context.pubSub, xf_disp_OnActivated); PubSub_SubscribeGraphicsReset(xfc->context.pubSub, xf_disp_OnGraphicsReset); PubSub_SubscribeTimer(xfc->context.pubSub, xf_disp_OnTimer); + PubSub_SubscribeWindowStateChange(xfc->context.pubSub, xf_disp_OnWindowStateChange); return ret; } @@ -287,6 +305,7 @@ void xf_disp_free(xfDispContext* disp) PubSub_UnsubscribeActivated(disp->xfc->context.pubSub, xf_disp_OnActivated); PubSub_UnsubscribeGraphicsReset(disp->xfc->context.pubSub, xf_disp_OnGraphicsReset); PubSub_UnsubscribeTimer(disp->xfc->context.pubSub, xf_disp_OnTimer); + PubSub_UnsubscribeWindowStateChange(disp->xfc->context.pubSub, xf_disp_OnWindowStateChange); } free(disp); diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index 99577b1a1233..0d6d4a429cbc 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -24,6 +24,8 @@ #include #include +#include + #include #include @@ -33,6 +35,7 @@ #include "xf_disp.h" #include "xf_input.h" #include "xf_gfx.h" +#include "xf_graphics.h" #include "xf_event.h" #include "xf_input.h" @@ -518,6 +521,29 @@ static BOOL xf_event_KeyRelease(xfContext* xfc, const XKeyEvent* event, BOOL app return TRUE; } +/* Release a key, but ignore the event in case of autorepeat. + */ +static BOOL xf_event_KeyReleaseOrIgnore(xfContext* xfc, const XKeyEvent* event, BOOL app) +{ + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + + if ((event->type == KeyRelease) && XEventsQueued(xfc->display, QueuedAfterReading)) + { + XEvent nev = { 0 }; + XPeekEvent(xfc->display, &nev); + + if ((nev.type == KeyPress) && (nev.xkey.time == event->time) && + (nev.xkey.keycode == event->keycode)) + { + /* Key wasn’t actually released */ + return TRUE; + } + } + + return xf_event_KeyRelease(xfc, event, app); +} + static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL app) { if (event->mode == NotifyGrab) @@ -537,6 +563,7 @@ static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL ap /* Release all keys, should already be done at FocusOut but might be missed * if the WM decided to use an alternate event order */ xf_keyboard_release_all_keypress(xfc); + xf_pointer_update_scale(xfc); if (app) { @@ -645,6 +672,8 @@ static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, BOOL app) { + if (event->mode == NotifyGrab || event->mode == NotifyUngrab) + return TRUE; if (!app) { xfc->mouse_active = FALSE; @@ -668,6 +697,9 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even rdpSettings* settings; settings = xfc->context.settings; + WLog_DBG(TAG, "%s: x=%" PRId32 ", y=%" PRId32 ", w=%" PRId32 ", h=%" PRId32, __func__, event->x, + event->y, event->width, event->height); + if (!app) { if (!xfc->window) @@ -710,43 +742,42 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* even /* ask the server to resize using the display channel */ xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight); } - - return TRUE; } - - appWindow = xf_AppWindowFromX11Window(xfc, event->window); - - if (appWindow) + else { - /* - * ConfigureNotify coordinates are expressed relative to the window parent. - * Translate these to root window coordinates. - */ - XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen), 0, - 0, &appWindow->x, &appWindow->y, &childWindow); - appWindow->width = event->width; - appWindow->height = event->height; - - /* - * Additional checks for not in a local move and not ignoring configure to send - * position update to server, also should the window not be focused then do not - * send to server yet (i.e. resizing using window decoration). - * The server will be updated when the window gets refocused. - */ - if (appWindow->decorations) - { - /* moving resizing using window decoration */ - xf_rail_adjust_position(xfc, appWindow); - } - else + appWindow = xf_AppWindowFromX11Window(xfc, event->window); + + if (appWindow) { - if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) && - !appWindow->rail_ignore_configure && xfc->focused) + /* + * ConfigureNotify coordinates are expressed relative to the window parent. + * Translate these to root window coordinates. + */ + XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen), + 0, 0, &appWindow->x, &appWindow->y, &childWindow); + appWindow->width = event->width; + appWindow->height = event->height; + + /* + * Additional checks for not in a local move and not ignoring configure to send + * position update to server, also should the window not be focused then do not + * send to server yet (i.e. resizing using window decoration). + * The server will be updated when the window gets refocused. + */ + if (appWindow->decorations) + { + /* moving resizing using window decoration */ xf_rail_adjust_position(xfc, appWindow); + } + else + { + if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) && + !appWindow->rail_ignore_configure && xfc->focused) + xf_rail_adjust_position(xfc, appWindow); + } } } - - return TRUE; + return xf_pointer_update_scale(xfc); } static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) @@ -759,14 +790,14 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) { appWindow = xf_AppWindowFromX11Window(xfc, event->window); - if (appWindow) + if (appWindow && (appWindow->rail_state == WINDOW_SHOW)) { /* local restore event */ /* This is now handled as part of the PropertyNotify * Doing this here would inhibit the ability to restore a maximized window * that is minimized back to the maximized state */ - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + // xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); appWindow->is_mapped = TRUE; } } @@ -777,6 +808,10 @@ static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app) static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app) { xfAppWindow* appWindow; + + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + xf_keyboard_release_all_keypress(xfc); if (!app) @@ -796,6 +831,9 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app) { + WINPR_ASSERT(xfc); + WINPR_ASSERT(event); + /* * This section handles sending the appropriate commands to the rail server * when the window has been minimized, maximized, restored locally @@ -830,18 +868,27 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, if (status) { + if (appWindow) + { + appWindow->maxVert = FALSE; + appWindow->maxHorz = FALSE; + } for (i = 0; i < nitems; i++) { if ((Atom)((UINT16**)prop)[i] == XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False)) { maxVert = TRUE; + if (appWindow) + appWindow->maxVert = TRUE; } if ((Atom)((UINT16**)prop)[i] == XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False)) { maxHorz = TRUE; + if (appWindow) + appWindow->maxHorz = TRUE; } } @@ -858,9 +905,17 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, { /* If the window is in the iconic state */ if (((UINT32)*prop == 3)) + { minimized = TRUE; + if (appWindow) + appWindow->minimized = TRUE; + } else + { minimized = FALSE; + if (appWindow) + appWindow->minimized = FALSE; + } minimizedChanged = TRUE; XFree(prop); @@ -869,22 +924,30 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, if (app) { - if (maxVert && maxHorz && !minimized && - (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED)) + WINPR_ASSERT(appWindow); + if (appWindow->maxVert && appWindow->maxHorz && !appWindow->minimized) { - appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MAXIMIZED; + xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE); + } } - else if (minimized && (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)) + else if (appWindow->minimized) { - appWindow->rail_state = WINDOW_SHOW_MINIMIZED; - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED) + { + appWindow->rail_state = WINDOW_SHOW_MINIMIZED; + xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE); + } } - else if (!minimized && !maxVert && !maxHorz && (appWindow->rail_state != WINDOW_SHOW) && - (appWindow->rail_state != WINDOW_HIDE)) + else { - appWindow->rail_state = WINDOW_SHOW; - xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE) + { + appWindow->rail_state = WINDOW_SHOW; + xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE); + } } } else if (minimizedChanged) @@ -1048,7 +1111,7 @@ BOOL xf_event_process(freerdp* instance, const XEvent* event) break; case KeyRelease: - status = xf_event_KeyRelease(xfc, &event->xkey, xfc->remote_app); + status = xf_event_KeyReleaseOrIgnore(xfc, &event->xkey, xfc->remote_app); break; case FocusIn: diff --git a/client/X11/xf_gdi.c b/client/X11/xf_gdi.c index 4f52853bfeef..aecd389cc2e5 100644 --- a/client/X11/xf_gdi.c +++ b/client/X11/xf_gdi.c @@ -1066,7 +1066,7 @@ static BOOL xf_gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* case RDP_CODEC_ID_NONE: pSrcData = cmd->bmp.bitmapData; format = gdi_get_pixel_format(cmd->bmp.bpp); - size = cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format) * 1ULL; + size = 1ull * cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format); if (size > cmd->bmp.bitmapDataLength) { WLog_ERR(TAG, "Short nocodec message: got %" PRIu32 " bytes, require %" PRIuz, diff --git a/client/X11/xf_gfx.c b/client/X11/xf_gfx.c index 97d3ad3cca6e..3863d6698cf8 100644 --- a/client/X11/xf_gfx.c +++ b/client/X11/xf_gfx.c @@ -288,7 +288,7 @@ static UINT xf_CreateSurface(RdpgfxClientContext* context, surface->gdi.scanline = surface->gdi.width * GetBytesPerPixel(surface->gdi.format); surface->gdi.scanline = x11_pad_scanline(surface->gdi.scanline, xfc->scanline_pad); - size = surface->gdi.scanline * surface->gdi.height * 1ULL; + size = 1ull * surface->gdi.scanline * surface->gdi.height; surface->gdi.data = (BYTE*)_aligned_malloc(size, 16); if (!surface->gdi.data) @@ -312,7 +312,7 @@ static UINT xf_CreateSurface(RdpgfxClientContext* context, UINT32 bytes = GetBytesPerPixel(gdi->dstFormat); surface->stageScanline = width * bytes; surface->stageScanline = x11_pad_scanline(surface->stageScanline, xfc->scanline_pad); - size = surface->stageScanline * surface->gdi.height * 1ULL; + size = 1ull * surface->stageScanline * surface->gdi.height; surface->stage = (BYTE*)_aligned_malloc(size, 16); if (!surface->stage) diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c index 8de32c5572a7..fe81e0ed91cb 100644 --- a/client/X11/xf_graphics.c +++ b/client/X11/xf_graphics.c @@ -30,6 +30,9 @@ #include #endif +#include +#include + #include #include @@ -42,6 +45,8 @@ #include #define TAG CLIENT_TAG("x11") +static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer); + BOOL xf_decode_color(xfContext* xfc, const UINT32 srcColor, XColor* color) { rdpGdi* gdi; @@ -227,14 +232,14 @@ static BOOL xf_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL pr return TRUE; } -static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPointer* pointer, - Cursor* cursor) +static BOOL xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpPointer* pointer, + Cursor* cursor) { #ifdef WITH_XCURSOR UINT32 CursorFormat; xfContext* xfc = (xfContext*)context; xfPointer* xpointer = (xfPointer*)pointer; - XcursorImage ci; + XcursorImage ci = { 0 }; rdpSettings* settings; UINT32 xTargetSize; UINT32 yTargetSize; @@ -253,8 +258,11 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP xscale = (settings->SmartSizing ? xfc->scaledWidth / (double)settings->DesktopWidth : 1); yscale = (settings->SmartSizing ? xfc->scaledHeight / (double)settings->DesktopHeight : 1); - xTargetSize = pointer->width * xscale; - yTargetSize = pointer->height * yscale; + xTargetSize = MAX(1, pointer->width * xscale); + yTargetSize = MAX(1, pointer->height * yscale); + + WLog_DBG(TAG, "%s: scaled: %" PRIu32 "x%" PRIu32 ", desktop: %" PRIu32 "x%" PRIu32, __func__, + xfc->scaledWidth, xfc->savedHeight, settings->DesktopWidth, settings->DesktopHeight); for (int i = 0; i < xpointer->nCursors; i++) { @@ -297,23 +305,29 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP } } - ZeroMemory(&ci, sizeof(ci)); ci.version = XCURSOR_IMAGE_VERSION; ci.size = sizeof(ci); ci.width = xTargetSize; ci.height = yTargetSize; ci.xhot = pointer->xPos * xscale; ci.yhot = pointer->yPos * yscale; - size = ci.height * ci.width * GetBytesPerPixel(CursorFormat) * 1ULL; + size = 1ull * ci.height * ci.width * GetBytesPerPixel(CursorFormat); - if (xscale != 1 || yscale != 1) + if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16))) { - if (!(ci.pixels = (XcursorPixel*)_aligned_malloc(size, 16))) - { - xf_unlock_x11(xfc); - return FALSE; - } + xf_unlock_x11(xfc); + return FALSE; + } + const double xs = fabs(fabs(xscale) - 1.0); + const double ys = fabs(fabs(yscale) - 1.0); + WLog_DBG(TAG, + "%s: cursorIndex %" PRId32 " scaling pointer %" PRIu32 "x%" PRIu32 " --> %" PRIu32 + "x%" PRIu32 " [%lfx%lf]", + __func__, cursorIndex, pointer->width, pointer->height, ci.width, ci.height, + xscale, yscale); + if ((xs > DBL_EPSILON) || (ys > DBL_EPSILON)) + { if (!freerdp_image_scale((BYTE*)ci.pixels, CursorFormat, 0, 0, 0, ci.width, ci.height, (BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height)) @@ -325,7 +339,7 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP } else { - ci.pixels = xpointer->cursorPixels; + memcpy(ci.pixels, xpointer->cursorPixels, size); } cursorIndex = xpointer->nCursors; @@ -333,11 +347,14 @@ static BOOL _xf_Pointer_GetCursorForCurrentScale(rdpContext* context, const rdpP xpointer->cursorHeights[cursorIndex] = ci.height; xpointer->cursors[cursorIndex] = XcursorImageLoadCursor(xfc->display, &ci); xpointer->nCursors += 1; - if (xscale != 1 || yscale != 1) - _aligned_free(ci.pixels); + _aligned_free(ci.pixels); xf_unlock_x11(xfc); } + else + { + WLog_DBG(TAG, "%s: using cached cursor %" PRId32, __func__, cursorIndex); + } cursor[0] = xpointer->cursors[cursorIndex]; #endif @@ -372,8 +389,21 @@ static Window xf_Pointer_get_window(xfContext* xfc) } } +BOOL xf_pointer_update_scale(xfContext* xfc) +{ + xfPointer* pointer; + WINPR_ASSERT(xfc); + + pointer = xfc->pointer; + if (!pointer) + return TRUE; + + return xf_Pointer_Set(&xfc->context, &xfc->pointer->pointer); +} + static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) { + BOOL rc = FALSE; #ifdef WITH_XCURSOR UINT32 CursorFormat; size_t size; @@ -381,7 +411,7 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) xfPointer* xpointer = (xfPointer*)pointer; if (!context || !pointer || !context->gdi) - return FALSE; + goto fail; if (!xfc->invert) CursorFormat = (!xfc->big_endian) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_ABGR32; @@ -391,10 +421,10 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) xpointer->nCursors = 0; xpointer->mCursors = 0; - size = pointer->height * pointer->width * GetBytesPerPixel(CursorFormat) * 1ULL; + size = 1ull * pointer->height * pointer->width * GetBytesPerPixel(CursorFormat); if (!(xpointer->cursorPixels = (XcursorPixel*)_aligned_malloc(size, 16))) - return FALSE; + goto fail; if (!freerdp_image_copy_from_pointer_data( (BYTE*)xpointer->cursorPixels, CursorFormat, 0, 0, 0, pointer->width, pointer->height, @@ -404,15 +434,18 @@ static BOOL xf_Pointer_New(rdpContext* context, rdpPointer* pointer) _aligned_free(xpointer->cursorPixels); return FALSE; } + rc = TRUE; - if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xpointer->cursor))) - return FALSE; #endif - return TRUE; +fail: + WLog_DBG(TAG, "%s: %p", __func__, rc ? pointer : NULL); + return rc; } static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer) { + WLog_DBG(TAG, "%s: %p", __func__, pointer); + #ifdef WITH_XCURSOR xfContext* xfc = (xfContext*)context; xfPointer* xpointer = (xfPointer*)pointer; @@ -438,6 +471,8 @@ static void xf_Pointer_Free(rdpContext* context, rdpPointer* pointer) static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer) { + WLog_DBG(TAG, "%s: %p", __func__, pointer); + #ifdef WITH_XCURSOR xfContext* xfc = (xfContext*)context; Window handle = xf_Pointer_get_window(xfc); @@ -447,18 +482,23 @@ static BOOL xf_Pointer_Set(rdpContext* context, const rdpPointer* pointer) if (handle) { - if (!_xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor))) + if (!xf_Pointer_GetCursorForCurrentScale(context, pointer, &(xfc->pointer->cursor))) return FALSE; xf_lock_x11(xfc); XDefineCursor(xfc->display, handle, xfc->pointer->cursor); xf_unlock_x11(xfc); } + else + { + WLog_WARN(TAG, "%s: handle=%ld", __func__, handle); + } #endif return TRUE; } static BOOL xf_Pointer_SetNull(rdpContext* context) { + WLog_DBG(TAG, "%s", __func__); #ifdef WITH_XCURSOR xfContext* xfc = (xfContext*)context; static Cursor nullcursor = None; @@ -515,11 +555,12 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) if (!handle) { - WLog_WARN(TAG, "xf_Pointer_SetPosition: focus %d, handle%lu", xfc->focused, handle); + WLog_WARN(TAG, "%s: focus %d, handle%lu", __func__, xfc->focused, handle); return TRUE; } - if (xfc->remote_app && !xfc->focused) + WLog_DBG(TAG, "%s: %" PRIu32 "x%" PRIu32, __func__, x, y); + if (!xfc->focused) return TRUE; xf_adjust_coordinates_to_screen(xfc, &x, &y); @@ -529,7 +570,7 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) rc = XGetWindowAttributes(xfc->display, handle, ¤t); if (rc == 0) { - WLog_WARN(TAG, "xf_Pointer_SetPosition: XGetWindowAttributes==%d", rc); + WLog_WARN(TAG, "%s: XGetWindowAttributes==%d", __func__, rc); goto out; } @@ -538,17 +579,17 @@ static BOOL xf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) rc = XChangeWindowAttributes(xfc->display, handle, CWEventMask, &tmp); if (rc == 0) { - WLog_WARN(TAG, "xf_Pointer_SetPosition: XChangeWindowAttributes==%d", rc); + WLog_WARN(TAG, "%s: XChangeWindowAttributes==%d", __func__, rc); goto out; } - rc = XWarpPointer(xfc->display, None, handle, 0, 0, 0, 0, x, y); + rc = XWarpPointer(xfc->display, handle, handle, 0, 0, 0, 0, x, y); if (rc == 0) - WLog_WARN(TAG, "xf_Pointer_SetPosition: XWarpPointer==%d", rc); + WLog_WARN(TAG, "%s: XWarpPointer==%d", __func__, rc); tmp.event_mask = current.your_event_mask; rc = XChangeWindowAttributes(xfc->display, handle, CWEventMask, &tmp); if (rc == 0) - WLog_WARN(TAG, "xf_Pointer_SetPosition: 2.try XChangeWindowAttributes==%d", rc); + WLog_WARN(TAG, "%s: 2.try XChangeWindowAttributes==%d", __func__, rc); ret = TRUE; out: xf_unlock_x11(xfc); @@ -729,6 +770,8 @@ UINT32 xf_get_local_color_format(xfContext* xfc, BOOL aligned) if (xfc->depth == 32) DstFormat = (!invert) ? PIXEL_FORMAT_RGBA32 : PIXEL_FORMAT_BGRA32; + else if (xfc->depth == 30) + DstFormat = (!invert) ? PIXEL_FORMAT_RGBX32_DEPTH30 : PIXEL_FORMAT_BGRX32_DEPTH30; else if (xfc->depth == 24) { if (aligned) diff --git a/client/X11/xf_graphics.h b/client/X11/xf_graphics.h index 303e116c19cb..96e1d982f881 100644 --- a/client/X11/xf_graphics.h +++ b/client/X11/xf_graphics.h @@ -29,4 +29,6 @@ BOOL xf_register_graphics(rdpGraphics* graphics); BOOL xf_decode_color(xfContext* xfc, const UINT32 srcColor, XColor* color); UINT32 xf_get_local_color_format(xfContext* xfc, BOOL aligned); +BOOL xf_pointer_update_scale(xfContext* xfc); + #endif /* FREERDP_CLIENT_X11_GRAPHICS_H */ diff --git a/client/X11/xf_input.c b/client/X11/xf_input.c index 9bb417ea5bc1..50a52e0359d1 100644 --- a/client/X11/xf_input.c +++ b/client/X11/xf_input.c @@ -152,8 +152,8 @@ int xf_input_init(xfContext* xfc, Window window) if (xfc->context.settings->MultiTouchInput) { - WLog_INFO(TAG, "%s (%d) \"%s\" id: %d", xf_input_get_class_string(class->type), - class->type, dev->name, dev->deviceid); + WLog_DBG(TAG, "%s (%d) \"%s\" id: %d", xf_input_get_class_string(class->type), + class->type, dev->name, dev->deviceid); } evmasks[nmasks].mask = masks[nmasks]; @@ -166,9 +166,9 @@ int xf_input_init(xfContext* xfc, Window window) { if (xfc->context.settings->MultiTouchInput) { - WLog_INFO(TAG, "%s %s touch device (id: %d, mode: %d), supporting %d touches.", - dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent", - dev->deviceid, t->mode, t->num_touches); + WLog_DBG(TAG, "%s %s touch device (id: %d, mode: %d), supporting %d touches.", + dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent", + dev->deviceid, t->mode, t->num_touches); } XISetMask(masks[nmasks], XI_TouchBegin); @@ -182,8 +182,8 @@ int xf_input_init(xfContext* xfc, Window window) if (!touch && (class->type == XIButtonClass) && strcmp(dev->name, "Virtual core pointer")) { - WLog_INFO(TAG, "%s button device (id: %d, mode: %d)", dev->name, dev->deviceid, - t->mode); + WLog_DBG(TAG, "%s button device (id: %d, mode: %d)", dev->name, dev->deviceid, + t->mode); XISetMask(masks[nmasks], XI_ButtonPress); XISetMask(masks[nmasks], XI_ButtonRelease); XISetMask(masks[nmasks], XI_Motion); diff --git a/client/X11/xf_keyboard.c b/client/X11/xf_keyboard.c index 26a1fd2e50cc..377e9bdd80f6 100644 --- a/client/X11/xf_keyboard.c +++ b/client/X11/xf_keyboard.c @@ -400,18 +400,19 @@ static int xf_keyboard_execute_action_script(xfContext* xfc, XF_MODIFIER_KEYS* m } if (mod->Shift) - strcat(combination, "Shift+"); + winpr_str_append("Shift", combination, sizeof(combination), "+"); if (mod->Ctrl) - strcat(combination, "Ctrl+"); + winpr_str_append("Ctrl", combination, sizeof(combination), "+"); if (mod->Alt) - strcat(combination, "Alt+"); + winpr_str_append("Alt", combination, sizeof(combination), "+"); if (mod->Super) - strcat(combination, "Super+"); + winpr_str_append("Super", combination, sizeof(combination), "+"); + + winpr_str_append(keyStr, combination, sizeof(combination), NULL); - strcat(combination, keyStr); count = ArrayList_Count(xfc->keyCombinations); for (index = 0; index < count; index++) diff --git a/client/X11/xf_rail.c b/client/X11/xf_rail.c index 770234f7dc24..c4f7173af502 100644 --- a/client/X11/xf_rail.c +++ b/client/X11/xf_rail.c @@ -33,22 +33,51 @@ #define TAG CLIENT_TAG("x11") -static const char* error_code_names[] = { "RAIL_EXEC_S_OK", - "RAIL_EXEC_E_HOOK_NOT_LOADED", - "RAIL_EXEC_E_DECODE_FAILED", - "RAIL_EXEC_E_NOT_IN_ALLOWLIST", - "RAIL_EXEC_E_FILE_NOT_FOUND", - "RAIL_EXEC_E_FAIL", - "RAIL_EXEC_E_SESSION_LOCKED" }; - -#ifdef WITH_DEBUG_RAIL -static const char* movetype_names[] = { - "(invalid)", "RAIL_WMSZ_LEFT", "RAIL_WMSZ_RIGHT", - "RAIL_WMSZ_TOP", "RAIL_WMSZ_TOPLEFT", "RAIL_WMSZ_TOPRIGHT", - "RAIL_WMSZ_BOTTOM", "RAIL_WMSZ_BOTTOMLEFT", "RAIL_WMSZ_BOTTOMRIGHT", - "RAIL_WMSZ_MOVE", "RAIL_WMSZ_KEYMOVE", "RAIL_WMSZ_KEYSIZE" -}; -#endif +static const char* error_code2str(UINT32 code) +{ +#define EVCASE(x) \ + case x: \ + return #x + switch (code) + { + EVCASE(RAIL_EXEC_S_OK); + EVCASE(RAIL_EXEC_E_HOOK_NOT_LOADED); + EVCASE(RAIL_EXEC_E_DECODE_FAILED); + EVCASE(RAIL_EXEC_E_NOT_IN_ALLOWLIST); + EVCASE(RAIL_EXEC_E_FILE_NOT_FOUND); + EVCASE(RAIL_EXEC_E_FAIL); + EVCASE(RAIL_EXEC_E_SESSION_LOCKED); + default: + return "RAIL_EXEC_E_UNKNOWN"; + } +#undef EVCASE +} + +static const char* movetype2str(UINT32 code) +{ +#define EVCASE(x) \ + case x: \ + return #x + + switch (code) + { + + EVCASE(RAIL_WMSZ_LEFT); + EVCASE(RAIL_WMSZ_RIGHT); + EVCASE(RAIL_WMSZ_TOP); + EVCASE(RAIL_WMSZ_TOPLEFT); + EVCASE(RAIL_WMSZ_TOPRIGHT); + EVCASE(RAIL_WMSZ_BOTTOM); + EVCASE(RAIL_WMSZ_BOTTOMLEFT); + EVCASE(RAIL_WMSZ_BOTTOMRIGHT); + EVCASE(RAIL_WMSZ_MOVE); + EVCASE(RAIL_WMSZ_KEYMOVE); + EVCASE(RAIL_WMSZ_KEYSIZE); + default: + return "RAIL_WMSZ_INVALID"; + } +#undef EVCASE +} struct xf_rail_icon { @@ -136,10 +165,10 @@ void xf_rail_adjust_position(xfContext* xfc, xfAppWindow* appWindow) * Calculate new size/position for the rail window(new values for * windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server */ - windowMove.left = appWindow->x; - windowMove.top = appWindow->y; - windowMove.right = windowMove.left + appWindow->width; - windowMove.bottom = windowMove.top + appWindow->height; + windowMove.left = appWindow->x - appWindow->resizeMarginLeft; + windowMove.top = appWindow->y - appWindow->resizeMarginTop; + windowMove.right = appWindow->x + appWindow->width + appWindow->resizeMarginRight; + windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom; xfc->rail->ClientWindowMove(xfc->rail, &windowMove); } } @@ -163,12 +192,12 @@ void xf_rail_end_local_move(xfContext* xfc, xfAppWindow* appWindow) * windowOffsetX/windowOffsetY/windowWidth/windowHeight) on the server * */ - windowMove.left = appWindow->x; - windowMove.top = appWindow->y; + windowMove.left = appWindow->x - appWindow->resizeMarginLeft; + windowMove.top = appWindow->y - appWindow->resizeMarginTop; windowMove.right = - windowMove.left + - appWindow->width; /* In the update to RDP the position is one past the window */ - windowMove.bottom = windowMove.top + appWindow->height; + appWindow->x + + appWindow->width + appWindow->resizeMarginRight; /* In the update to RDP the position is one past the window */ + windowMove.bottom = appWindow->y + appWindow->height + appWindow->resizeMarginBottom; xfc->rail->ClientWindowMove(xfc->rail, &windowMove); /* * Simulate button up at new position to end the local move (per RDP spec) @@ -259,11 +288,10 @@ void xf_rail_paint(xfContext* xfc, INT32 uleft, INT32 utop, UINT32 uright, UINT3 static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo, const WINDOW_STATE_ORDER* windowState) { - xfAppWindow* appWindow = NULL; xfContext* xfc = (xfContext*)context; UINT32 fieldFlags = orderInfo->fieldFlags; BOOL position_or_size_updated = FALSE; - appWindow = xf_rail_get_window(xfc, orderInfo->windowId); + xfAppWindow* appWindow = xf_rail_get_window(xfc, orderInfo->windowId); if (fieldFlags & WINDOW_ORDER_STATE_NEW) { @@ -308,10 +336,7 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* } if (!appWindow->title) - { - free(appWindow); return FALSE; - } xf_AppWindowInit(xfc, appWindow); } @@ -344,6 +369,18 @@ static BOOL xf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* appWindow->windowWidth = windowState->windowWidth; appWindow->windowHeight = windowState->windowHeight; } + + if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_X) + { + appWindow->resizeMarginLeft = windowState->resizeMarginLeft; + appWindow->resizeMarginRight = windowState->resizeMarginRight; + } + + if (fieldFlags & WINDOW_ORDER_FIELD_RESIZE_MARGIN_Y) + { + appWindow->resizeMarginTop = windowState->resizeMarginTop; + appWindow->resizeMarginBottom = windowState->resizeMarginBottom; + } if (fieldFlags & WINDOW_ORDER_FIELD_OWNER) { @@ -532,7 +569,7 @@ static xfRailIconCache* RailIconCache_New(rdpSettings* settings) cache->numCaches = settings->RemoteAppNumIconCaches; cache->numCacheEntries = settings->RemoteAppNumIconCacheEntries; - cache->entries = calloc(cache->numCaches * cache->numCacheEntries * 1ULL, sizeof(xfRailIcon)); + cache->entries = calloc(1ull * cache->numCaches * cache->numCacheEntries, sizeof(xfRailIcon)); if (!cache->entries) { @@ -602,7 +639,7 @@ static BOOL convert_rail_icon(const ICON_INFO* iconInfo, xfRailIcon* railIcon) long* pixels; int i; int nelements; - argbPixels = calloc(iconInfo->width * iconInfo->height * 1ULL, 4); + argbPixels = calloc(1ull * iconInfo->width * iconInfo->height, 4); if (!argbPixels) goto error; @@ -796,8 +833,9 @@ static UINT xf_rail_server_execute_result(RailClientContext* context, if (execResult->execResult != RAIL_EXEC_S_OK) { - WLog_ERR(TAG, "RAIL exec error: execResult=%s NtError=0x%X\n", - error_code_names[execResult->execResult], execResult->rawResult); + WLog_ERR(TAG, "RAIL exec error: execResult=%s [0x%08" PRIx32 "] NtError=0x%X\n", + error_code2str(execResult->execResult), execResult->execResult, + execResult->rawResult); freerdp_abort_connect(xfc->context.instance); } else @@ -928,6 +966,8 @@ static UINT xf_rail_server_local_move_size(RailClientContext* context, if (!appWindow) return ERROR_INTERNAL_ERROR; + WLog_DBG(TAG, "%s [0x%08" PRIx32 "]", + movetype2str(localMoveSize->moveSizeType), localMoveSize->moveSizeType); switch (localMoveSize->moveSizeType) { case RAIL_WMSZ_LEFT: diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index 55ea71232a33..9b5b1c4a6061 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -355,7 +356,7 @@ BOOL xf_GetWindowProperty(xfContext* xfc, Window window, Atom property, int leng if (actual_type == None) { - WLog_INFO(TAG, "Property %lu does not exist", (unsigned long)property); + WLog_DBG(TAG, "Property %lu does not exist", (unsigned long)property); return FALSE; } @@ -775,6 +776,9 @@ int xf_AppWindowCreate(xfContext* xfc, xfAppWindow* appWindow) appWindow->is_mapped = FALSE; appWindow->is_transient = FALSE; appWindow->rail_state = 0; + appWindow->maxVert = FALSE; + appWindow->maxHorz = FALSE; + appWindow->minimized = FALSE; appWindow->rail_ignore_configure = FALSE; appWindow->handle = XCreateWindow(xfc->display, RootWindowOfScreen(xfc->screen), appWindow->x, appWindow->y, appWindow->width, appWindow->height, 0, @@ -930,6 +934,9 @@ void xf_MoveWindow(xfContext* xfc, xfAppWindow* appWindow, int x, int y, int wid void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state) { + WINPR_ASSERT(xfc); + WINPR_ASSERT(appWindow); + switch (state) { case WINDOW_HIDE: @@ -937,11 +944,14 @@ void xf_ShowWindow(xfContext* xfc, xfAppWindow* appWindow, BYTE state) break; case WINDOW_SHOW_MINIMIZED: + appWindow->minimized = TRUE; XIconifyWindow(xfc->display, appWindow->handle, xfc->screen_number); break; case WINDOW_SHOW_MAXIMIZED: /* Set the window as maximized */ + appWindow->maxHorz = TRUE; + appWindow->maxVert = TRUE; xf_SendClientEvent(xfc, appWindow->handle, xfc->_NET_WM_STATE, 4, _NET_WM_STATE_ADD, xfc->_NET_WM_STATE_MAXIMIZED_VERT, xfc->_NET_WM_STATE_MAXIMIZED_HORZ, 0); diff --git a/client/X11/xf_window.h b/client/X11/xf_window.h index 3adaa3c7f567..0f85af1d294e 100644 --- a/client/X11/xf_window.h +++ b/client/X11/xf_window.h @@ -126,6 +126,11 @@ struct xf_app_window UINT32 localWindowOffsetCorrX; UINT32 localWindowOffsetCorrY; + + UINT32 resizeMarginLeft; + UINT32 resizeMarginTop; + UINT32 resizeMarginRight; + UINT32 resizeMarginBottom; GC gc; int shmid; @@ -137,6 +142,9 @@ struct xf_app_window BOOL is_transient; xfLocalMove local_move; BYTE rail_state; + BOOL maxVert; + BOOL maxHorz; + BOOL minimized; BOOL rail_ignore_configure; }; diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h index b4488489ab0e..636e60ad1d5b 100644 --- a/client/X11/xfreerdp.h +++ b/client/X11/xfreerdp.h @@ -318,7 +318,32 @@ enum XF_EXIT_CODE XF_EXIT_CONN_FAILED = 131, XF_EXIT_AUTH_FAILURE = 132, XF_EXIT_NEGO_FAILURE = 133, - + XF_EXIT_LOGON_FAILURE = 134, + XF_EXIT_ACCOUNT_LOCKED_OUT = 135, + XF_EXIT_PRE_CONNECT_FAILED = 136, + XF_EXIT_CONNECT_UNDEFINED = 137, + XF_EXIT_POST_CONNECT_FAILED = 138, + XF_EXIT_DNS_ERROR = 139, + XF_EXIT_DNS_NAME_NOT_FOUND = 140, + XF_EXIT_CONNECT_FAILED = 141, + XF_EXIT_MCS_CONNECT_INITIAL_ERROR = 142, + XF_EXIT_TLS_CONNECT_FAILED = 143, + XF_EXIT_INSUFFICIENT_PRIVILEGES = 144, + XF_EXIT_CONNECT_CANCELLED = 145, + XF_EXIT_SECURITY_NEGO_CONNECT_FAILED = 146, + XF_EXIT_CONNECT_TRANSPORT_FAILED = 147, + XF_EXIT_CONNECT_PASSWORD_EXPIRED = 148, + XF_EXIT_CONNECT_PASSWORD_MUST_CHANGE = 149, + XF_EXIT_CONNECT_KDC_UNREACHABLE = 150, + XF_EXIT_CONNECT_ACCOUNT_DISABLED = 151, + XF_EXIT_CONNECT_PASSWORD_CERTAINLY_EXPIRED = 152, + XF_EXIT_CONNECT_CLIENT_REVOKED = 153, + XF_EXIT_CONNECT_WRONG_PASSWORD = 154, + XF_EXIT_CONNECT_ACCESS_DENIED = 155, + XF_EXIT_CONNECT_ACCOUNT_RESTRICTION = 156, + XF_EXIT_CONNECT_ACCOUNT_EXPIRED = 157, + XF_EXIT_CONNECT_LOGON_TYPE_NOT_GRANTED = 158, + XF_EXIT_CONNECT_NO_OR_MISSING_CREDENTIALS = 159, XF_EXIT_UNKNOWN = 255, }; diff --git a/client/common/client.c b/client/common/client.c index 0cdb636d8943..6862deb362fe 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -730,6 +730,7 @@ BOOL client_auto_reconnect(freerdp* instance) BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* instance)) { + BOOL retry = TRUE; UINT32 error; UINT32 maxRetries; UINT32 numRetries = 0; @@ -765,7 +766,7 @@ BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* } /* Perform an auto-reconnect. */ - while (TRUE) + while (retry) { UINT32 x; @@ -781,12 +782,21 @@ BOOL client_auto_reconnect_ex(freerdp* instance, BOOL (*window_events)(freerdp* if (freerdp_reconnect(instance)) return TRUE; + switch (freerdp_get_last_error(instance->context)) + { + case FREERDP_ERROR_CONNECT_CANCELLED: + WLog_WARN(TAG, "Autoreconnect aborted by user"); + retry = FALSE; + break; + default: + break; + } for (x = 0; x < 50; x++) { if (!IFCALLRESULT(TRUE, window_events, instance)) return FALSE; - Sleep(100); + Sleep(10); } } diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 6982eefe35a4..df5e8e820f63 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -24,9 +24,10 @@ #endif #include -#include #include +#include + #include #include #include @@ -46,6 +47,7 @@ #include "cmdline.h" #include + #define TAG CLIENT_TAG("common.cmdline") static BOOL freerdp_client_print_codepages(const char* arg) @@ -209,6 +211,7 @@ static BOOL copy_value(const char* value, char** dst) static BOOL append_value(const char* value, char** dst) { size_t x = 0, y; + size_t size; char* tmp; if (!dst || !value) return FALSE; @@ -216,14 +219,16 @@ static BOOL append_value(const char* value, char** dst) if (*dst) x = strlen(*dst); y = strlen(value); - tmp = realloc(*dst, x + y + 2); + + size = x + y + 2; + tmp = realloc(*dst, size); if (!tmp) return FALSE; if (x == 0) tmp[0] = '\0'; else - strcat(tmp, ","); - strcat(tmp, value); + winpr_str_append(",", tmp, size, NULL); + winpr_str_append(value, tmp, size, NULL); *dst = tmp; return TRUE; } @@ -1567,6 +1572,28 @@ static BOOL parseSizeValue(const char* input, unsigned long* v1, unsigned long* return TRUE; } +static BOOL prepare_default_settings(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_A* args, + BOOL rdp_file) +{ + size_t x; + const char* arguments[] = { "network", "gfx", "rfx", "bpp" }; + WINPR_ASSERT(settings); + WINPR_ASSERT(args); + + if (rdp_file) + return FALSE; + + for (x = 0; x < ARRAYSIZE(arguments); x++) + { + const char* arg = arguments[x]; + COMMAND_LINE_ARGUMENT_A* p = CommandLineFindArgumentA(args, arg); + if (p && (p->Flags & COMMAND_LINE_ARGUMENT_PRESENT)) + return FALSE; + } + + return freerdp_set_connection_type(settings, CONNECTION_TYPE_AUTODETECT); +} + int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, int argc, char** argv, BOOL allowUnknown) { @@ -1606,7 +1633,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, #if !defined(DEFINE_NO_DEPRECATED) if (compatibility) { + WLog_WARN(TAG, "----------------------------------------"); WLog_WARN(TAG, "Using deprecated command-line interface!"); + WLog_WARN(TAG, "This will be removed with FreeRDP 3!"); + WLog_WARN(TAG, "----------------------------------------"); return freerdp_client_parse_old_command_line_arguments(argc, argv, settings); } else @@ -1634,6 +1664,8 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, if (status < 0) return status; + + prepare_default_settings(settings, largs, ext); } CommandLineFindArgumentA(largs, "v"); @@ -2262,8 +2294,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, { if (_stricmp(arg->Value, "rpc") == 0) { - settings->GatewayRpcTransport = TRUE; - settings->GatewayHttpTransport = FALSE; + if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, TRUE) || + !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, FALSE) || + !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpUseWebsockets, FALSE)) + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; } else { @@ -2272,21 +2306,24 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, { *c++ = '\0'; if (_stricmp(c, "no-websockets") != 0) - { return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; - } - freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpUseWebsockets, FALSE); + + if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpUseWebsockets, + FALSE)) + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; } if (_stricmp(arg->Value, "http") == 0) { - settings->GatewayRpcTransport = FALSE; - settings->GatewayHttpTransport = TRUE; + if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, FALSE) || + !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, TRUE)) + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; } else if (_stricmp(arg->Value, "auto") == 0) { - settings->GatewayRpcTransport = TRUE; - settings->GatewayHttpTransport = TRUE; + if (!freerdp_settings_set_bool(settings, FreeRDP_GatewayRpcTransport, TRUE) || + !freerdp_settings_set_bool(settings, FreeRDP_GatewayHttpTransport, TRUE)) + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; } } } @@ -2691,7 +2728,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, if (strcmp(arg->Value, "video") == 0) settings->RemoteFxCodecMode = 0x00; else if (strcmp(arg->Value, "image") == 0) + { + settings->RemoteFxImageCodec = TRUE; settings->RemoteFxCodecMode = 0x02; + } } CommandLineSwitchCase(arg, "frame-ack") { @@ -2881,6 +2921,12 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, settings->TlsSecLevel = (UINT32)val; } + CommandLineSwitchCase(arg, "enforce-tlsv1_2") + { + if (!(freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, TLS1_2_VERSION) && + freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, TLS1_2_VERSION))) + return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; + } CommandLineSwitchCase(arg, "cert") { int rc = 0; @@ -2953,6 +2999,10 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, { settings->GrabKeyboard = enable; } + CommandLineSwitchCase(arg, "grab-mouse") + { + settings->GrabMouse = enable; + } CommandLineSwitchCase(arg, "unmap-buttons") { settings->UnmapButtons = enable; @@ -3496,7 +3546,7 @@ static BOOL freerdp_client_load_static_channel_addin(rdpChannels* channels, rdpS { if (freerdp_channels_client_load_ex(channels, settings, entryEx, data) == 0) { - WLog_INFO(TAG, "loading channelEx %s", name); + WLog_DBG(TAG, "loading channelEx %s", name); return TRUE; } } @@ -3504,7 +3554,7 @@ static BOOL freerdp_client_load_static_channel_addin(rdpChannels* channels, rdpS { if (freerdp_channels_client_load(channels, settings, entry, data) == 0) { - WLog_INFO(TAG, "loading channel %s", name); + WLog_DBG(TAG, "loading channel %s", name); return TRUE; } } diff --git a/client/common/cmdline.h b/client/common/cmdline.h index 9a60e3df922b..6ecc8c57ae43 100644 --- a/client/common/cmdline.h +++ b/client/common/cmdline.h @@ -184,6 +184,7 @@ static const COMMAND_LINE_ARGUMENT_A args[] = { { "gp", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Gateway password" }, { "grab-keyboard", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Grab keyboard" }, + { "grab-mouse", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Grab mouse" }, { "gt", COMMAND_LINE_VALUE_REQUIRED, "[rpc|http[,no-websockets]|auto[,no-websockets]]", NULL, NULL, -1, NULL, "Gateway transport type" }, { "gu", COMMAND_LINE_VALUE_REQUIRED, "[[\\]|[@]]", NULL, NULL, -1, @@ -353,6 +354,9 @@ static const COMMAND_LINE_ARGUMENT_A args[] = { "Allowed TLS ciphers" }, { "tls-seclevel", COMMAND_LINE_VALUE_REQUIRED, "", "1", NULL, -1, NULL, "TLS security level - defaults to 1" }, + { "enforce-tlsv1_2", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, + "Force use of TLS1.2 for connection. Some servers have a buggy TLS version negotiation and " + "might fail without this" }, { "toggle-fullscreen", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Alt+Ctrl+Enter to toggle fullscreen" }, { "tune", COMMAND_LINE_VALUE_REQUIRED, ",", "", NULL, -1, NULL, diff --git a/cmake/ClangDetectTool.cmake b/cmake/ClangDetectTool.cmake new file mode 100644 index 000000000000..a1ecd56d3f2a --- /dev/null +++ b/cmake/ClangDetectTool.cmake @@ -0,0 +1,48 @@ +function (clang_detect_tool VAR NAME OPTS) + set(NAMES "") + foreach(CNT RANGE 12 22) + list(APPEND NAMES "${NAME}-${CNT}") + endforeach() + list(REVERSE NAMES) + list(APPEND NAMES ${NAME}) + + find_program(${VAR} + NAMES ${NAMES} + ${OPTS} + ) + if (NOT ${VAR}) + message(WARNING "clang tool ${NAME} (${VAR}) not detected, skipping") + unset(${VAR}) + return() + endif() + + execute_process( + COMMAND ${${VAR}} "--version" + OUTPUT_VARIABLE _CLANG_TOOL_VERSION + RESULT_VARIABLE _CLANG_TOOL_VERSION_FAILED + ) + + if (_CLANG_TOOL_VERSION_FAILED) + message(WARNING "A problem was encounterd with ${${VAR}}") + message(WARNING "${_CLANG_TOOL_VERSION_FAILED}") + unset(${VAR}) + return() + endif() + + string(REGEX MATCH "([7-9]|[1-9][0-9])\\.[0-9]\\.[0-9]" CLANG_TOOL_VERSION + "${_CLANG_TOOL_VERSION}") + + if (NOT CLANG_TOOL_VERSION) + message(WARNING "problem parsing ${NAME} version for ${${VAR}}") + unset(${VAR}) + return() + endif() + + set(_CLANG_TOOL_MINIMUM_VERSION "12.0.0") + if (${CLANG_TOOL_VERSION} VERSION_LESS ${_CLANG_TOOL_MINIMUM_VERSION}) + message(WARNING "clang-format version ${CLANG_TOOL_VERSION} not supported") + message(WARNING "Minimum version required: ${_CLANG_TOOL_MINIMUM_VERSION}") + unset(${VAR}) + return() + endif() +endfunction() diff --git a/cmake/ClangFormat.cmake b/cmake/ClangFormat.cmake index 8985ef358e8c..b815822fafa0 100644 --- a/cmake/ClangFormat.cmake +++ b/cmake/ClangFormat.cmake @@ -1,43 +1,12 @@ # get all project files file(GLOB_RECURSE ALL_SOURCE_FILES *.cpp *.c *.h *.m *.java) -# minimum version required -set(_CLANG_FORMAT_MINIMUM_VERSION 7.0.0) -find_program(CLANG_FORMAT - NAMES - clang-format-8 - clang-format-7 - clang-format - ) +include(ClangDetectTool) +clang_detect_tool(CLANG_FORMAT clang-format "") if (NOT CLANG_FORMAT) message(WARNING "clang-format not found in path! code format target not available.") else() - execute_process( - COMMAND ${CLANG_FORMAT} "--version" - OUTPUT_VARIABLE _CLANG_FORMAT_VERSION - RESULT_VARIABLE _CLANG_FORMAT_VERSION_FAILED - ) - - if (_CLANG_FORMAT_VERSION_FAILED) - message(WARNING "A problem was encounterd with ${CLANG_FORMAT}") - return() - endif() - - string(REGEX MATCH "([7-9]|[1-9][0-9])\\.[0-9]\\.[0-9]" CLANG_FORMAT_VERSION - "${_CLANG_FORMAT_VERSION}") - - if (NOT CLANG_FORMAT_VERSION) - message(WARNING "problem parsing clang-fromat version for ${CLANG_FORMAT}") - return() - endif() - - if (${CLANG_FORMAT_VERSION} VERSION_LESS ${_CLANG_FORMAT_MINIMUM_VERSION}) - message(WARNING "clang-format version ${CLANG_FORMAT_VERSION} not supported") - message(WARNING "Minimum version required: ${_CLANG_FORMAT_MINIMUM_VERSION}") - return() - endif() - add_custom_target( clangformat COMMAND ${CLANG_FORMAT} diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index 1a6a359b7e9e..f16a42ca116b 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -47,12 +47,12 @@ if(NOT WIN32) CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_ADDRESS "Compile with gcc/clang address sanitizer." OFF "NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_MEMORY; NOT WITH_SANITIZE_THREAD" OFF) CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_MEMORY "Compile with gcc/clang memory sanitizer." OFF - "NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_THREAD" OFF) + "NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_THREAD" OFF) CMAKE_DEPENDENT_OPTION(WITH_SANITIZE_THREAD "Compile with gcc/clang thread sanitizer." OFF "NOT WITH_VALGRIND_MEMCHECK; NOT WITH_SANITIZE_ADDRESS; NOT WITH_SANITIZE_MEMORY" OFF) else() if(NOT UWP) - option(WITH_MEDIA_FOUNDATION "Enable H264 media foundation decoder." ON) + option(WITH_MEDIA_FOUNDATION "Enable H264 media foundation decoder." OFF) endif() endif() @@ -98,7 +98,7 @@ option(WITH_SERVER_INTERFACE "Build servers as a library with an interface" ON) option(WITH_DEBUG_ALL "Print all debug messages." OFF) if(WITH_DEBUG_ALL) - message(WARNING "WITH_DEBUG_ALL=ON, the build will be slow and might leak sensitive information, do not use with release builds!") + message(WARNING "WITH_DEBUG_ALL=ON, the build will be slow and might leak sensitive information, do not use with release builds!") set(DEFAULT_DEBUG_OPTION "ON") else() set(DEFAULT_DEBUG_OPTION "OFF") @@ -106,7 +106,7 @@ endif() option(WITH_DEBUG_CERTIFICATE "Print certificate related debug messages." ${DEFAULT_DEBUG_OPTION}) if(WITH_DEBUG_CERTIFICATE) - message(WARNING "WITH_DEBUG_CERTIFICATE=ON, the build might leak sensitive information, do not use with release builds!") + message(WARNING "WITH_DEBUG_CERTIFICATE=ON, the build might leak sensitive information, do not use with release builds!") endif() option(WITH_DEBUG_CAPABILITIES "Print capability negotiation debug messages." ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_CHANNELS "Print channel manager debug messages." ${DEFAULT_DEBUG_OPTION}) @@ -116,23 +116,23 @@ option(WITH_DEBUG_DVC "Print dynamic virtual channel debug messages." ${DEFAULT_ CMAKE_DEPENDENT_OPTION(WITH_DEBUG_TSMF "Print TSMF virtual channel debug messages." ${DEFAULT_DEBUG_OPTION} "CHANNEL_TSMF" OFF) option(WITH_DEBUG_KBD "Print keyboard related debug messages." OFF) if(WITH_DEBUG_KBD) - message(WARNING "WITH_DEBUG_KBD=ON, the build might leak sensitive information, do not use with release builds!") + message(WARNING "WITH_DEBUG_KBD=ON, the build might leak sensitive information, do not use with release builds!") endif() option(WITH_DEBUG_LICENSE "Print license debug messages." OFF) if(WITH_DEBUG_LICENSE) - message(WARNING "WITH_DEBUG_LICENSE=ON, the build might leak sensitive information, do not use with release builds!") + message(WARNING "WITH_DEBUG_LICENSE=ON, the build might leak sensitive information, do not use with release builds!") endif() option(WITH_DEBUG_NEGO "Print negotiation related debug messages." OFF) if(WITH_DEBUG_NEGO) - message(WARNING "WITH_DEBUG_NEGO=ON, the build might leak sensitive information, do not use with release builds!") + message(WARNING "WITH_DEBUG_NEGO=ON, the build might leak sensitive information, do not use with release builds!") endif() option(WITH_DEBUG_NLA "Print authentication related debug messages." OFF) if(WITH_DEBUG_NLA) - message(WARNING "WITH_DEBUG_NLA=ON, the build might leak sensitive information, do not use with release builds!") + message(WARNING "WITH_DEBUG_NLA=ON, the build might leak sensitive information, do not use with release builds!") endif() option(WITH_DEBUG_NTLM "Print NTLM debug messages" OFF) if(WITH_DEBUG_NTLM) - message(WARNING "WITH_DEBUG_NTLM=ON, the build might leak sensitive information, do not use with release builds!") + message(WARNING "WITH_DEBUG_NTLM=ON, the build might leak sensitive information, do not use with release builds!") endif() option(WITH_DEBUG_TSG "Print Terminal Server Gateway debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_RAIL "Print RemoteApp debug messages" ${DEFAULT_DEBUG_OPTION}) @@ -163,13 +163,13 @@ option(WITH_GSSAPI "Compile support for kerberos authentication. (EXPERIMENTAL)" option(WITH_DSP_EXPERIMENTAL "Enable experimental sound encoder/decoder formats" OFF) if (WITH_FFMPEG) - option(WITH_DSP_FFMPEG "Use FFMPEG for audio encoding/decoding" OFF) - option(WITH_VAAPI "Use FFMPEG VAAPI (EXPERIMENTAL)" OFF) + option(WITH_DSP_FFMPEG "Use FFMPEG for audio encoding/decoding" OFF) + option(WITH_VAAPI "Use FFMPEG VAAPI (EXPERIMENTAL)" OFF) endif(WITH_FFMPEG) option(USE_VERSION_FROM_GIT_TAG "Extract FreeRDP version from git tag." OFF) -option(WITH_CAIRO "Use CAIRO image library for screen resizing" OFF) +option(WITH_CAIRO "Use CAIRO image library for screen resizing" OFF) option(WITH_SWSCALE "Use SWScale image library for screen resizing" OFF) option(DEFINE_NO_DEPRECATED "Compile without legacy functions and symbols" OFF) diff --git a/cmake/ConfigOptionsAndroid.cmake b/cmake/ConfigOptionsAndroid.cmake index 07dd604105f2..b241fabe7a29 100644 --- a/cmake/ConfigOptionsAndroid.cmake +++ b/cmake/ConfigOptionsAndroid.cmake @@ -19,4 +19,4 @@ option(WITH_OPENSLES "Enable sound and microphone redirection using OpenSLES" ON set(ANDROID_APP_TARGET_SDK 21 CACHE STRING "Application target android SDK") set(ANDROID_APP_MIN_SDK 14 CACHE STRING "Application minimum android SDK requirement") - +option(WITH_MEDIACODEC "Use MediaCodec API" ON) diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake index 78d9bf6ab1e2..ffa16412accd 100644 --- a/cmake/FindFFmpeg.cmake +++ b/cmake/FindFFmpeg.cmake @@ -27,12 +27,20 @@ find_library(AVUTIL_LIBRARY avutil PATHS ${AVUTIL_LIBRARY_DIRS}) # swresample find_path(SWRESAMPLE_INCLUDE_DIR libswresample/swresample.h PATHS ${SWRESAMPLE_INCLUDE_DIRS}) -find_library(SWRESAMPLE_LIBRARY swresample PATHS ${SWRESAMPLE_LIBRARY_DIRS}) +find_library(SWRESAMPLE_LIBRARY NAMES swresample swresample-3 PATHS ${SWRESAMPLE_LIBRARY_DIRS}) + +if (SWRESAMPLE_INCLUDE_DIR AND SWRESAMPLE_LIBRARY) + set(SWRESAMPLE_FOUND ON) +endif() # avresample find_path(AVRESAMPLE_INCLUDE_DIR libavresample/avresample.h PATHS ${AVRESAMPLE_INCLUDE_DIRS}) find_library(AVRESAMPLE_LIBRARY avresample PATHS ${AVRESAMPLE_LIBRARY_DIRS}) +if (AVRESAMPLE_INCLUDE_DIR AND AVRESAMPLE_LIBRARY) + set(AVRESAMPLE_FOUND ON) +endif() + if (AVCODEC_INCLUDE_DIR AND AVCODEC_LIBRARY) set(AVCODEC_FOUND TRUE) endif(AVCODEC_INCLUDE_DIR AND AVCODEC_LIBRARY) diff --git a/cmake/FindOpenSSL.cmake b/cmake/FindOpenSSL.cmake index d42a50cde7b7..f630d1a16ec5 100644 --- a/cmake/FindOpenSSL.cmake +++ b/cmake/FindOpenSSL.cmake @@ -55,7 +55,7 @@ FIND_PATH(OPENSSL_INCLUDE_DIR NAMES openssl/ssl.h PATH_SUFFIXES - "include" + "include" HINTS ${_OPENSSL_INCLUDEDIR} ${_OPENSSL_ROOT_HINTS_AND_PATHS} @@ -172,8 +172,8 @@ ELSEIF(WIN32 AND NOT CYGWIN) ) set( OPENSSL_DEBUG_LIBRARIES ${SSL_EAY_DEBUG} ${LIB_EAY_DEBUG} ) - set( OPENSSL_RELEASE_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} ) - set( OPENSSL_LIBRARIES ${OPENSSL_RELEASE_LIBRARIES} ) + set( OPENSSL_RELEASE_LIBRARIES ${SSL_EAY_RELEASE} ${LIB_EAY_RELEASE} ) + set( OPENSSL_LIBRARIES ${OPENSSL_RELEASE_LIBRARIES} ) MARK_AS_ADVANCED(SSL_EAY_DEBUG SSL_EAY_RELEASE) MARK_AS_ADVANCED(LIB_EAY_DEBUG LIB_EAY_RELEASE) @@ -313,25 +313,50 @@ if (OPENSSL_INCLUDE_DIR) string(REGEX REPLACE "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F]).*$" "\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}") - list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR) - list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR) - from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR) - list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX) - from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX) - list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH) - - if (NOT OPENSSL_VERSION_PATCH STREQUAL "00") - from_hex("${OPENSSL_VERSION_PATCH}" _tmp) - # 96 is the ASCII code of 'a' minus 1 - math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96") - unset(_tmp) - # Once anyone knows how OpenSSL would call the patch versions beyond 'z' - # this should be updated to handle that, too. This has not happened yet - # so it is simply ignored here for now. - string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING) - endif (NOT OPENSSL_VERSION_PATCH STREQUAL "00") - - set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}") + if (OPENSSL_VERSION_LIST) + list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR) + list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR) + from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR) + list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX) + from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX) + list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH) + + if (NOT OPENSSL_VERSION_PATCH STREQUAL "00") + from_hex("${OPENSSL_VERSION_PATCH}" _tmp) + # 96 is the ASCII code of 'a' minus 1 + math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96") + unset(_tmp) + # Once anyone knows how OpenSSL would call the patch versions beyond 'z' + # this should be updated to handle that, too. This has not happened yet + # so it is simply ignored here for now. + string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING) + endif (NOT OPENSSL_VERSION_PATCH STREQUAL "00") + + set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}") + endif() + + if (NOT OPENSSL_VERSION_LIST) + file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str_str + REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_STR[\t ]\"([0-9a-fA-F]+)\\.([0-9a-fA-F]+)\\.([0-9a-fA-F]+).*\".*$") + string(REGEX REPLACE "^.*OPENSSL_VERSION_STR[\t ]+\"([0-9a-fA-F]+)\\.([0-9a-fA-F]+)\\.([0-9a-fA-F]+).*\".*$" + "\\1.\\2.\\3" OPENSSL_VERSION "${openssl_version_str_str}") + endif() + + if (NOT OPENSSL_VERSION_LIST) + file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_major_str + REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_MAJOR[\t ]+([0-9a-fA-F]+).*$") + string(REGEX REPLACE "^#[\t ]*define[\t ]+OPENSSL_VERSION_MAJOR[\t ]+([0-9a-fA-F]+).*$" + "\\1" OPENSSL_VERSION_MAJOR "${openssl_version_major_str}") + file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_minor_str + REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_MINOR[\t ]+([0-9a-fA-F]+).*$") + string(REGEX REPLACE "^#[\t ]*define[\t ]+OPENSSL_VERSION_MINOR[\t ]+([0-9a-fA-F]+).*$" + "\\1" OPENSSL_VERSION_MINOR "${openssl_version_minor_str}") + file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_patch_str + REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_PATCH[\t ]+([0-9a-fA-F]+).*$") + string(REGEX REPLACE "^#[\t ]*define[\t ]+OPENSSL_VERSION_PATCH[\t ]+([0-9a-fA-F]+).*$" + "\\1" OPENSSL_VERSION_PATCH "${openssl_version_patch_str}") + set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_PATCH}") + endif() endif (_OPENSSL_VERSION) endif (OPENSSL_INCLUDE_DIR) diff --git a/cmake/Findx264.cmake b/cmake/Findx264.cmake deleted file mode 100644 index f8df4754eeed..000000000000 --- a/cmake/Findx264.cmake +++ /dev/null @@ -1,33 +0,0 @@ - -if (X264_INCLUDE_DIR AND X264_LIBRARY) - set(X264_FIND_QUIETLY TRUE) -endif (X264_INCLUDE_DIR AND X264_LIBRARY) - -find_path(X264_INCLUDE_DIR NAMES x264.h - PATH_SUFFIXES include - HINTS ${X264_ROOT}) -find_library(X264_LIBRARY - NAMES x264 - PATH_SUFFIXES lib - HINTS ${X264_ROOT}) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(x264 DEFAULT_MSG X264_LIBRARY X264_INCLUDE_DIR) - -if (X264_INCLUDE_DIR AND X264_LIBRARY) - set(X264_FOUND TRUE) - set(X264_LIBRARIES ${X264_LIBRARY}) -endif (X264_INCLUDE_DIR AND X264_LIBRARY) - -if (X264_FOUND) - if (NOT X264_FIND_QUIETLY) - message(STATUS "Found x264: ${X264_LIBRARIES}") - endif (NOT X264_FIND_QUIETLY) -else (X264_FOUND) - if (X264_FIND_REQUIRED) - message(FATAL_ERROR "x264 was not found") - endif(X264_FIND_REQUIRED) -endif (X264_FOUND) - -mark_as_advanced(X264_INCLUDE_DIR X264_LIBRARY) - diff --git a/cmake/iOSToolchain.cmake b/cmake/iOSToolchain.cmake deleted file mode 100644 index 1824ecbdfa77..000000000000 --- a/cmake/iOSToolchain.cmake +++ /dev/null @@ -1,196 +0,0 @@ -# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake -# files which are included with CMake 2.8.4 -# It has been altered for iOS development - -# Options: -# -# IOS_PLATFORM = OS (default) or SIMULATOR -# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders -# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. -# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. -# -# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder -# By default this location is automatcially chosen based on the IOS_PLATFORM value above. -# If set manually, it will override the default location and force the user of a particular Developer Platform -# -# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder -# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value. -# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path. -# If set manually, this will force the use of a specific SDK version - -# Macros: -# -# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE) -# A convenience macro for setting xcode specific properties on targets -# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1") -# -# find_host_package (PROGRAM ARGS) -# A macro used to find executable programs on the host system, not within the iOS environment. -# Thanks to the android-cmake project for providing the command - - -# Standard settings -set (CMAKE_SYSTEM_NAME Darwin) -set (CMAKE_SYSTEM_VERSION 1) -set (UNIX True) -set (APPLE True) -set (IOS True) - -# Required as of cmake 2.8.10 -#set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE) - -# Determine the cmake host system version so we know where to find the iOS SDKs -find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) -if (CMAKE_UNAME) - exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) - string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") -endif (CMAKE_UNAME) - -# Force the compilers to gcc for iOS -include (CMakeForceCompiler) -CMAKE_FORCE_C_COMPILER (/usr/bin/clang Apple) -CMAKE_FORCE_CXX_COMPILER (/usr/bin/clang++ Apple) -set(CMAKE_AR ar CACHE FILEPATH "" FORCE) - -# Skip the platform compiler checks for cross compiling -#set (CMAKE_CXX_COMPILER_WORKS TRUE) -#set (CMAKE_C_COMPILER_WORKS TRUE) - -# All iOS/Darwin specific settings - some may be redundant -set (CMAKE_SHARED_LIBRARY_PREFIX "lib") -set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") -set (CMAKE_SHARED_MODULE_PREFIX "lib") -set (CMAKE_SHARED_MODULE_SUFFIX ".so") -set (CMAKE_MODULE_EXISTS 1) -set (CMAKE_DL_LIBS "") - -set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") -set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") -set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") -set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") - -# Hidden visibilty is required for cxx on iOS -set (CMAKE_C_FLAGS_INIT "") -set (CMAKE_CXX_FLAGS_INIT "-fvisibility=hidden -fvisibility-inlines-hidden -isysroot ${CMAKE_OSX_SYSROOT}") - -set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") -set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") - -set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) -set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names") -set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names") -set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") -set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") -set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") - -# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree -# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache -# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun) -# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex -if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) - find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) -endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) - -# Setup iOS platform unless specified manually with IOS_PLATFORM -if (NOT DEFINED IOS_PLATFORM) - set (IOS_PLATFORM "OS") -endif (NOT DEFINED IOS_PLATFORM) -set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") - -# Check the platform selection and setup for developer root -if (${IOS_PLATFORM} STREQUAL "OS") - set (IOS_PLATFORM_LOCATION "iPhoneOS.platform") - - # This causes the installers to properly locate the output libraries - set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") - set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") - - # This causes the installers to properly locate the output libraries - set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") -else (${IOS_PLATFORM} STREQUAL "OS") - message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR") -endif (${IOS_PLATFORM} STREQUAL "OS") - -# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT -# Note Xcode 4.3 changed the installation location, choose the most recent one available -set (XCODE_POST_43_ROOT "/Applications/Xcode.app/Contents/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") -set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") -if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) - if (EXISTS ${XCODE_POST_43_ROOT}) - set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT}) - elseif(EXISTS ${XCODE_PRE_43_ROOT}) - set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT}) - endif (EXISTS ${XCODE_POST_43_ROOT}) -endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) -set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") - -# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT -if (NOT DEFINED CMAKE_IOS_SDK_ROOT) - file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") - if (_CMAKE_IOS_SDKS) - list (SORT _CMAKE_IOS_SDKS) - list (REVERSE _CMAKE_IOS_SDKS) - list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT) - else (_CMAKE_IOS_SDKS) - message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.") - endif (_CMAKE_IOS_SDKS) - message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}") -endif (NOT DEFINED CMAKE_IOS_SDK_ROOT) -set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK") - -# Set the sysroot default to the most recent SDK -set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") - -# set the architecture for iOS -# NOTE: Currently both ARCHS_STANDARD_32_BIT and ARCHS_UNIVERSAL_IPHONE_OS set armv7 only, so set both manually -if (${IOS_PLATFORM} STREQUAL "OS") - set (IOS_ARCH armv7 armv7s) - set (CMAKE_SYSTEM_PROCESSOR armv7) -else (${IOS_PLATFORM} STREQUAL "OS") - set (IOS_ARCH i386) - set (CMAKE_SYSTEM_PROCESSOR i386) -endif (${IOS_PLATFORM} STREQUAL "OS") - -set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS") - -# Set the find root to the iOS developer roots and to user defined paths -set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root") - -# default to searching for frameworks first -set (CMAKE_FIND_FRAMEWORK FIRST) - -# set up the default search directories for frameworks -set (CMAKE_SYSTEM_FRAMEWORK_PATH - ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks - ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks - ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks -) - -# only search the iOS sdks, not the remainder of the host filesystem -set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - - -# This little macro lets you set any XCode specific property -macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE) - set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE}) -endmacro (set_xcode_property) - - -# This macro lets you find executable programs on the host system -macro (find_host_package) - set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) - set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) - set (IOS FALSE) - - find_package(${ARGN}) - - set (IOS TRUE) - set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) - set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endmacro (find_host_package) - diff --git a/cmake/ios.toolchain.cmake b/cmake/ios.toolchain.cmake new file mode 100644 index 000000000000..1ecfd25567ce --- /dev/null +++ b/cmake/ios.toolchain.cmake @@ -0,0 +1,945 @@ +# This file is part of the ios-cmake project. It was retrieved from +# https://github.com/leetal/ios-cmake.git, which is a fork of +# https://github.com/gerstrong/ios-cmake.git, which is a fork of +# https://github.com/cristeab/ios-cmake.git, which is a fork of +# https://code.google.com/p/ios-cmake/. Which in turn is based off of +# the Platform/Darwin.cmake and Platform/UnixPaths.cmake files which +# are included with CMake 2.8.4 +# +# The ios-cmake project is licensed under the new BSD license. +# +# Copyright (c) 2014, Bogdan Cristea and LTE Engineering Software, +# Kitware, Inc., Insight Software Consortium. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# This file is based off of the Platform/Darwin.cmake and +# Platform/UnixPaths.cmake files which are included with CMake 2.8.4 +# It has been altered for iOS development. +# +# Updated by Alex Stewart (alexs.mac@gmail.com) +# +# ***************************************************************************** +# Now maintained by Alexander Widerberg (widerbergaren [at] gmail.com) +# under the BSD-3-Clause license +# https://github.com/leetal/ios-cmake +# ***************************************************************************** +# +# INFORMATION / HELP +# +# The following options control the behaviour of this toolchain: +# +# PLATFORM: (default "OS64") +# OS = Build for iPhoneOS. +# OS64 = Build for arm64 iphoneOS. +# OS64COMBINED = Build for arm64 x86_64 iphoneOS. Combined into FAT STATIC lib (supported on 3.14+ of CMakewith "-G Xcode" argument ONLY) +# SIMULATOR = Build for x86 i386 iphoneOS Simulator. +# SIMULATOR64 = Build for x86_64 iphoneOS Simulator. +# SIMULATORARM64 = Build for arm64 iphoneOS Simulator. +# TVOS = Build for arm64 tvOS. +# TVOSCOMBINED = Build for arm64 x86_64 tvOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) +# SIMULATOR_TVOS = Build for x86_64 tvOS Simulator. +# WATCHOS = Build for armv7k arm64_32 for watchOS. +# WATCHOSCOMBINED = Build for armv7k arm64_32 x86_64 watchOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) +# SIMULATOR_WATCHOS = Build for x86_64 for watchOS Simulator. +# MAC = Build for x86_64 macOS. +# MAC_ARM64 = Build for Apple Silicon macOS. +# MAC_CATALYST = Build for x86_64 macOS with Catalyst support (iOS toolchain on macOS). +# Note: The build argument "MACOSX_DEPLOYMENT_TARGET" can be used to control min-version of macOS +# MAC_CATALYST_ARM64 = Build for Apple Silicon macOS with Catalyst support (iOS toolchain on macOS). +# Note: The build argument "MACOSX_DEPLOYMENT_TARGET" can be used to control min-version of macOS +# +# CMAKE_OSX_SYSROOT: Path to the SDK to use. By default this is +# automatically determined from PLATFORM and xcodebuild, but +# can also be manually specified (although this should not be required). +# +# CMAKE_DEVELOPER_ROOT: Path to the Developer directory for the platform +# being compiled for. By default this is automatically determined from +# CMAKE_OSX_SYSROOT, but can also be manually specified (although this should +# not be required). +# +# DEPLOYMENT_TARGET: Minimum SDK version to target. Default 2.0 on watchOS and 9.0 on tvOS+iOS +# +# ENABLE_BITCODE: (1|0) Enables or disables bitcode support. Default 1 (true) +# +# ENABLE_ARC: (1|0) Enables or disables ARC support. Default 1 (true, ARC enabled by default) +# +# ENABLE_VISIBILITY: (1|0) Enables or disables symbol visibility support. Default 0 (false, visibility hidden by default) +# +# ENABLE_STRICT_TRY_COMPILE: (1|0) Enables or disables strict try_compile() on all Check* directives (will run linker +# to actually check if linking is possible). Default 0 (false, will set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY) +# +# ARCHS: (armv7 armv7s armv7k arm64 arm64_32 i386 x86_64) If specified, will override the default architectures for the given PLATFORM +# OS = armv7 armv7s arm64 (if applicable) +# OS64 = arm64 (if applicable) +# SIMULATOR = i386 +# SIMULATOR64 = x86_64 +# SIMULATORARM64 = arm64 +# TVOS = arm64 +# SIMULATOR_TVOS = x86_64 (i386 has since long been deprecated) +# WATCHOS = armv7k arm64_32 (if applicable) +# SIMULATOR_WATCHOS = x86_64 (i386 has since long been deprecated) +# MAC = x86_64 +# MAC_ARM64 = arm64 +# MAC_CATALYST = x86_64 +# MAC_CATALYST_ARM64 = arm64 +# +# This toolchain defines the following properties (available via get_property()) for use externally: +# +# PLATFORM: The currently targeted platform. +# XCODE_VERSION: Version number (not including Build version) of Xcode detected. +# SDK_VERSION: Version of SDK being used. +# OSX_ARCHITECTURES: Architectures being compiled for (generated from PLATFORM). +# APPLE_TARGET_TRIPLE: Used by autoconf build systems. NOTE: If "ARCHS" are overridden, this will *NOT* be set! +# +# This toolchain defines the following macros for use externally: +# +# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE XCODE_VARIANT) +# A convenience macro for setting xcode specific properties on targets. +# Available variants are: All, Release, RelWithDebInfo, Debug, MinSizeRel +# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1" "all"). +# +# find_host_package (PROGRAM ARGS) +# A macro used to find executable programs on the host system, not within the +# environment. Thanks to the android-cmake project for providing the +# command. +# + +cmake_minimum_required(VERSION 3.8.0) + +# CMake invokes the toolchain file twice during the first build, but only once during subsequent rebuilds. +if(DEFINED ENV{_IOS_TOOLCHAIN_HAS_RUN}) + return() +endif() +set(ENV{_IOS_TOOLCHAIN_HAS_RUN} true) + +############################################################################### +# OPTIONS # +############################################################################### + +option(DROP_32_BIT "Drops the 32-bit targets universally." YES) + +############################################################################### +# END OPTIONS # +############################################################################### + +# List of supported platform values +list(APPEND _supported_platforms + "OS" "OS64" "OS64COMBINED" "SIMULATOR" "SIMULATOR64" "SIMULATORARM64" + "TVOS" "TVOSCOMBINED" "SIMULATOR_TVOS" + "WATCHOS" "WATCHOSCOMBINED" "SIMULATOR_WATCHOS" + "MAC" "MAC_ARM64" + "MAC_CATALYST" "MAC_CATALYST_ARM64") + +# Cache what generator is used +set(USED_CMAKE_GENERATOR "${CMAKE_GENERATOR}") + +# Check if using a CMake version capable of building combined FAT builds (simulator and target slices combined in one static lib) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14") + set(MODERN_CMAKE YES) +endif() + +# Get the Xcode version being used. +# Problem: CMake runs toolchain files multiple times, but can't read cache variables on some runs. +# Workaround: On first run (in which cache variables are always accessible), set an intermediary environment variable. +# +# NOTE: This pattern is used i many places in this toolchain to speed up checks of all sorts +if(DEFINED XCODE_VERSION_INT) + # Environment variables are always preserved. + set(ENV{_XCODE_VERSION_INT} "${XCODE_VERSION_INT}") +elseif(DEFINED ENV{_XCODE_VERSION_INT}) + set(XCODE_VERSION_INT "$ENV{_XCODE_VERSION_INT}") +elseif(NOT DEFINED XCODE_VERSION_INT) + find_program(XCODEBUILD_EXECUTABLE xcodebuild) + if(NOT XCODEBUILD_EXECUTABLE) + message(FATAL_ERROR "xcodebuild not found. Please install either the standalone commandline tools or Xcode.") + endif() + execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version + OUTPUT_VARIABLE XCODE_VERSION_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REGEX MATCH "Xcode [0-9\\.]+" XCODE_VERSION_INT "${XCODE_VERSION_INT}") + string(REGEX REPLACE "Xcode ([0-9\\.]+)" "\\1" XCODE_VERSION_INT "${XCODE_VERSION_INT}") + set(XCODE_VERSION_INT "${XCODE_VERSION_INT}" CACHE INTERNAL "") +endif() + +# Assuming that xcode 12.0 is installed you most probably have ios sdk 14.0 or later installed (tested on Big Sur) +# if you don't set a deployment target it will be set the way you only get 64-bit builds +if(NOT DEFINED DEPLOYMENT_TARGET AND XCODE_VERSION_INT VERSION_GREATER 12.0) + # Temporarily fix the arm64 issues in CMake install-combined by excluding arm64 for simulator builds (needed for Apple Silicon...) + set(CMAKE_XCODE_ATTRIBUTE_EXCLUDED_ARCHS[sdk=iphonesimulator*] "arm64") +endif() + +# Check if the platform variable is set +if(DEFINED PLATFORM) + # Environment variables are always preserved. + set(ENV{_PLATFORM} "${PLATFORM}") +elseif(DEFINED ENV{_PLATFORM}) + set(PLATFORM "$ENV{_PLATFORM}") +elseif(NOT DEFINED PLATFORM) + message(FATAL_ERROR "PLATFORM argument not set. Bailing configure since I don't know what target you want to build for!") +endif () + +if(PLATFORM MATCHES ".*COMBINED" AND NOT CMAKE_GENERATOR MATCHES "Xcode") + message(FATAL_ERROR "The combined builds support requires Xcode to be used as generator via '-G Xcode' command-line argument in CMake") +endif() + +# Safeguard that the platform value is set and is one of the supported values +list(FIND _supported_platforms ${PLATFORM} contains_PLATFORM) +if("${contains_PLATFORM}" EQUAL "-1") + string(REPLACE ";" "\n * " _supported_platforms_formatted "${_supported_platforms}") + message(FATAL_ERROR " Invalid PLATFORM specified! Current value: ${PLATFORM}.\n" + " Supported PLATFORM values: \n * ${_supported_platforms_formatted}") +endif() + +# Check if Apple Silicon is supported +if(PLATFORM MATCHES "^(MAC_ARM64)$|^(MAC_CATALYST_ARM64)$" AND ${CMAKE_VERSION} VERSION_LESS "3.19.5") + message(FATAL_ERROR "Apple Silicon builds requires a minimum of CMake 3.19.5") +endif() + +# Touch toolchain variable to suppress "unused variable" warning. +# This happens if CMake is invoked with the same command line the second time. +if(CMAKE_TOOLCHAIN_FILE) +endif() + +# Fix for PThread library not in path +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) + +# Specify minimum version of deployment target. +if(NOT DEFINED DEPLOYMENT_TARGET) + if (PLATFORM MATCHES "WATCHOS") + # Unless specified, SDK version 4.0 is used by default as minimum target version (watchOS). + set(DEPLOYMENT_TARGET "4.0") + elseif(PLATFORM STREQUAL "MAC") + # Unless specified, SDK version 10.13 (High sierra) is used by default as minimum target version (macos). + set(DEPLOYMENT_TARGET "10.13") + elseif(PLATFORM STREQUAL "MAC_ARM64") + # Unless specified, SDK version 11.0 (Big Sur) is used by default as minimum target version (macos on arm). + set(DEPLOYMENT_TARGET "11.0") + elseif(PLATFORM STREQUAL "MAC_CATALYST" OR PLATFORM STREQUAL "MAC_CATALYST_ARM64") + # Unless specified, SDK version 13.0 is used by default as minimum target version (mac catalyst minimum requirement). + set(DEPLOYMENT_TARGET "13.0") + else() + # Unless specified, SDK version 11.0 is used by default as minimum target version (iOS, tvOS). + set(DEPLOYMENT_TARGET "11.0") + endif() + message(STATUS "[DEFAULTS] Using the default min-version since DEPLOYMENT_TARGET not provided!") +elseif(DEFINED DEPLOYMENT_TARGET AND PLATFORM MATCHES "^MAC_CATALYST" AND ${DEPLOYMENT_TARGET} VERSION_LESS "13.0") + message(FATAL_ERROR "Mac Catalyst builds requires a minimum deployment target of 13.0!") +endif() + +# Store the DEPLOYMENT_TARGET in the cache +set(DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}" CACHE INTERNAL "") + +# Handle the case where we are targeting iOS and a version above 10.3.4 (32-bit support dropped officially) +if(PLATFORM STREQUAL "OS" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) + set(PLATFORM "OS64") + message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") +elseif(PLATFORM STREQUAL "SIMULATOR" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) + set(PLATFORM "SIMULATOR64") + message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") +endif() + +set(PLATFORM_INT "${PLATFORM}") + +if(DEFINED ARCHS) + string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") +endif() + +# Determine the platform name and architectures for use in xcodebuild commands +# from the specified PLATFORM_INT name. +if(PLATFORM_INT STREQUAL "OS") + set(SDK_NAME iphoneos) + if(NOT ARCHS) + set(ARCHS armv7 armv7s arm64) + set(APPLE_TARGET_TRIPLE_INT arm-apple-ios$${DEPLOYMENT_TARGET}) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}) + endif() +elseif(PLATFORM_INT STREQUAL "OS64") + set(SDK_NAME iphoneos) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS arm64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example + else() + set(ARCHS arm64) + endif() + set(APPLE_TARGET_TRIPLE_INT aarch64-apple-ios${DEPLOYMENT_TARGET}) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}) + endif() +elseif(PLATFORM_INT STREQUAL "OS64COMBINED") + set(SDK_NAME iphoneos) + if(MODERN_CMAKE) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS arm64 x86_64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "x86_64") + else() + set(ARCHS arm64 x86_64) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*] "x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphoneos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=iphonesimulator*] "x86_64") + endif() + set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-ios${DEPLOYMENT_TARGET}) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the OS64COMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS i386) + set(APPLE_TARGET_TRIPLE_INT i386-apple-ios${DEPLOYMENT_TARGET}-simulator) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-simulator) + endif() + message(DEPRECATION "SIMULATOR IS DEPRECATED. Consider using SIMULATOR64 instead.") +elseif(PLATFORM_INT STREQUAL "SIMULATOR64") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS x86_64) + set(APPLE_TARGET_TRIPLE_INT x86_64-apple-ios${DEPLOYMENT_TARGET}-simulator) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-simulator) + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATORARM64") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS arm64) + set(APPLE_TARGET_TRIPLE_INT aarch64-apple-ios${DEPLOYMENT_TARGET}-simulator) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-simulator) + endif() +elseif(PLATFORM_INT STREQUAL "TVOS") + set(SDK_NAME appletvos) + if(NOT ARCHS) + set(ARCHS arm64) + set(APPLE_TARGET_TRIPLE_INT aarch64-apple-tvos${DEPLOYMENT_TARGET}) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos${DEPLOYMENT_TARGET}) + endif() +elseif (PLATFORM_INT STREQUAL "TVOSCOMBINED") + set(SDK_NAME appletvos) + if(MODERN_CMAKE) + if(NOT ARCHS) + set(ARCHS arm64 x86_64) + set(APPLE_TARGET_TRIPLE_INT aarch64-x86_64-apple-tvos${DEPLOYMENT_TARGET}) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=appletvsimulator*] "x86_64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=appletvos*] "arm64") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=appletvsimulator*] "x86_64") + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos${DEPLOYMENT_TARGET}) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the TVOSCOMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") + set(SDK_NAME appletvsimulator) + if(NOT ARCHS) + set(ARCHS x86_64) + set(APPLE_TARGET_TRIPLE_INT x86_64-apple-tvos${DEPLOYMENT_TARGET}-simulator) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-tvos${DEPLOYMENT_TARGET}-simulator) + endif() +elseif(PLATFORM_INT STREQUAL "WATCHOS") + set(SDK_NAME watchos) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS armv7k arm64_32) + set(APPLE_TARGET_TRIPLE_INT aarch64_32-apple-watchos${DEPLOYMENT_TARGET}) + else() + set(ARCHS armv7k) + set(APPLE_TARGET_TRIPLE_INT arm-apple-watchos${DEPLOYMENT_TARGET}) + endif() + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET}) + endif() +elseif(PLATFORM_INT STREQUAL "WATCHOSCOMBINED") + set(SDK_NAME watchos) + if(MODERN_CMAKE) + if(NOT ARCHS) + if (XCODE_VERSION_INT VERSION_GREATER 10.0) + set(ARCHS armv7k arm64_32 i386) + set(APPLE_TARGET_TRIPLE_INT aarch64_32-i386-apple-watchos${DEPLOYMENT_TARGET}) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "armv7k arm64_32") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "armv7k arm64_32") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386") + else() + set(ARCHS armv7k i386) + set(APPLE_TARGET_TRIPLE_INT arm-i386-apple-watchos${DEPLOYMENT_TARGET}) + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchos*] "armv7k") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=watchsimulator*] "i386") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchos*] "armv7k") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=watchsimulator*] "i386") + endif() + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET}) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the WATCHOSCOMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") + set(SDK_NAME watchsimulator) + if(NOT ARCHS) + set(ARCHS i386) + set(APPLE_TARGET_TRIPLE_INT i386-apple-watchos${DEPLOYMENT_TARGET}-simulator) + else() + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-watchos${DEPLOYMENT_TARGET}-simulator) + endif() +elseif(PLATFORM_INT STREQUAL "MAC" OR PLATFORM_INT STREQUAL "MAC_CATALYST") + set(SDK_NAME macosx) + if(NOT ARCHS) + set(ARCHS x86_64) + endif() + string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") + if(PLATFORM_INT STREQUAL "MAC") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx${DEPLOYMENT_TARGET}) + elseif(PLATFORM_INT STREQUAL "MAC_CATALYST") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-macabi) + endif() +elseif(PLATFORM_INT MATCHES "^(MAC_ARM64)$|^(MAC_CATALYST_ARM64)$") + set(SDK_NAME macosx) + if(NOT ARCHS) + set(ARCHS arm64) + endif() + string(REPLACE ";" "-" ARCHS_SPLIT "${ARCHS}") + if(PLATFORM_INT STREQUAL "MAC_ARM64") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-macosx${DEPLOYMENT_TARGET}) + elseif(PLATFORM_INT STREQUAL "MAC_CATALYST_ARM64") + set(APPLE_TARGET_TRIPLE_INT ${ARCHS_SPLIT}-apple-ios${DEPLOYMENT_TARGET}-macabi) + endif() +else() + message(FATAL_ERROR "Invalid PLATFORM: ${PLATFORM_INT}") +endif() + +if(MODERN_CMAKE AND PLATFORM_INT MATCHES ".*COMBINED" AND NOT CMAKE_GENERATOR MATCHES "Xcode") + message(FATAL_ERROR "The COMBINED options only work with Xcode generator, -G Xcode") +endif() + +if(CMAKE_GENERATOR MATCHES "Xcode" AND PLATFORM_INT MATCHES "^MAC_CATALYST") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") + set(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "macosx") + set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-maccatalyst") + if(NOT DEFINED MACOSX_DEPLOYMENT_TARGET) + set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "10.15") + else() + set(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "${MACOSX_DEPLOYMENT_TARGET}") + endif() +elseif(CMAKE_GENERATOR MATCHES "Xcode") + set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET "${DEPLOYMENT_TARGET}") + if(NOT PLATFORM_INT MATCHES ".*COMBINED") + set(CMAKE_XCODE_ATTRIBUTE_ARCHS[sdk=${SDK_NAME}*] "${ARCHS}") + set(CMAKE_XCODE_ATTRIBUTE_VALID_ARCHS[sdk=${SDK_NAME}*] "${ARCHS}") + endif() +endif() + +# If user did not specify the SDK root to use, then query xcodebuild for it. +if(DEFINED CMAKE_OSX_SYSROOT_INT) + # Environment variables are always preserved. + set(ENV{_CMAKE_OSX_SYSROOT_INT} "${CMAKE_OSX_SYSROOT_INT}") +elseif(DEFINED ENV{_CMAKE_OSX_SYSROOT_INT}) + set(CMAKE_OSX_SYSROOT_INT "$ENV{_CMAKE_OSX_SYSROOT_INT}") +elseif(NOT DEFINED CMAKE_OSX_SYSROOT_INT) + execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -version -sdk ${SDK_NAME} Path + OUTPUT_VARIABLE CMAKE_OSX_SYSROOT_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +if (NOT DEFINED CMAKE_OSX_SYSROOT_INT AND NOT DEFINED CMAKE_OSX_SYSROOT) + message(SEND_ERROR "Please make sure that Xcode is installed and that the toolchain" + "is pointing to the correct path. Please run:" + "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer" + "and see if that fixes the problem for you.") + message(FATAL_ERROR "Invalid CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} " + "does not exist.") +elseif(DEFINED CMAKE_OSX_SYSROOT_INT) + set(CMAKE_OSX_SYSROOT_INT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") + # Specify the location or name of the platform SDK to be used in CMAKE_OSX_SYSROOT. + set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") +endif() + +# Use bitcode or not +if(NOT DEFINED ENABLE_BITCODE AND NOT ARCHS MATCHES "((^|;|, )(i386|x86_64))+") + # Unless specified, enable bitcode support by default + message(STATUS "[DEFAULTS] Enabling bitcode support by default. ENABLE_BITCODE not provided!") + set(ENABLE_BITCODE TRUE) +elseif(NOT DEFINED ENABLE_BITCODE) + message(STATUS "[DEFAULTS] Disabling bitcode support by default on simulators. ENABLE_BITCODE not provided for override!") + set(ENABLE_BITCODE FALSE) +endif() +set(ENABLE_BITCODE_INT ${ENABLE_BITCODE} CACHE BOOL + "Whether or not to enable bitcode" FORCE) +# Use ARC or not +if(NOT DEFINED ENABLE_ARC) + # Unless specified, enable ARC support by default + set(ENABLE_ARC TRUE) + message(STATUS "[DEFAULTS] Enabling ARC support by default. ENABLE_ARC not provided!") +endif() +set(ENABLE_ARC_INT ${ENABLE_ARC} CACHE BOOL "Whether or not to enable ARC" FORCE) +# Use hidden visibility or not +if(NOT DEFINED ENABLE_VISIBILITY) + # Unless specified, disable symbols visibility by default + set(ENABLE_VISIBILITY FALSE) + message(STATUS "[DEFAULTS] Hiding symbols visibility by default. ENABLE_VISIBILITY not provided!") +endif() +set(ENABLE_VISIBILITY_INT ${ENABLE_VISIBILITY} CACHE BOOL "Whether or not to hide symbols from the dynamic linker (-fvisibility=hidden)" FORCE) +# Set strict compiler checks or not +if(NOT DEFINED ENABLE_STRICT_TRY_COMPILE) + # Unless specified, disable strict try_compile() + set(ENABLE_STRICT_TRY_COMPILE FALSE) + message(STATUS "[DEFAULTS] Using NON-strict compiler checks by default. ENABLE_STRICT_TRY_COMPILE not provided!") +endif() +set(ENABLE_STRICT_TRY_COMPILE_INT ${ENABLE_STRICT_TRY_COMPILE} CACHE BOOL + "Whether or not to use strict compiler checks" FORCE) + +# Get the SDK version information. +if(DEFINED SDK_VERSION) + # Environment variables are always preserved. + set(ENV{_SDK_VERSION} "${SDK_VERSION}") +elseif(DEFINED ENV{_SDK_VERSION}) + set(SDK_VERSION "$ENV{_SDK_VERSION}") +elseif(NOT DEFINED SDK_VERSION) + execute_process(COMMAND ${XCODEBUILD_EXECUTABLE} -sdk ${CMAKE_OSX_SYSROOT_INT} -version SDKVersion + OUTPUT_VARIABLE SDK_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() + +# Find the Developer root for the specific iOS platform being compiled for +# from CMAKE_OSX_SYSROOT. Should be ../../ from SDK specified in +# CMAKE_OSX_SYSROOT. There does not appear to be a direct way to obtain +# this information from xcrun or xcodebuild. +if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT CMAKE_GENERATOR MATCHES "Xcode") + get_filename_component(PLATFORM_SDK_DIR ${CMAKE_OSX_SYSROOT_INT} PATH) + get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH) + if (NOT EXISTS "${CMAKE_DEVELOPER_ROOT}") + message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: ${CMAKE_DEVELOPER_ROOT} does not exist.") + endif() +endif() + +# Find the C & C++ compilers for the specified SDK. +if(DEFINED CMAKE_C_COMPILER) + # Environment variables are always preserved. + set(ENV{_CMAKE_C_COMPILER} "${CMAKE_C_COMPILER}") +elseif(DEFINED ENV{_CMAKE_C_COMPILER}) + set(CMAKE_C_COMPILER "$ENV{_CMAKE_C_COMPILER}") + set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) +elseif(NOT DEFINED CMAKE_C_COMPILER) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find clang + OUTPUT_VARIABLE CMAKE_C_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) +endif() +if(DEFINED CMAKE_CXX_COMPILER) + # Environment variables are always preserved. + set(ENV{_CMAKE_CXX_COMPILER} "${CMAKE_CXX_COMPILER}") +elseif(DEFINED ENV{_CMAKE_CXX_COMPILER}) + set(CMAKE_CXX_COMPILER "$ENV{_CMAKE_CXX_COMPILER}") +elseif(NOT DEFINED CMAKE_CXX_COMPILER) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find clang++ + OUTPUT_VARIABLE CMAKE_CXX_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +# Find (Apple's) libtool. +if(DEFINED BUILD_LIBTOOL) + # Environment variables are always preserved. + set(ENV{_BUILD_LIBTOOL} "${BUILD_LIBTOOL}") +elseif(DEFINED ENV{_BUILD_LIBTOOL}) + set(BUILD_LIBTOOL "$ENV{_BUILD_LIBTOOL}") +elseif(NOT DEFINED BUILD_LIBTOOL) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find libtool + OUTPUT_VARIABLE BUILD_LIBTOOL + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +endif() +# Find the toolchain's provided install_name_tool if none is found on the host +if(DEFINED CMAKE_INSTALL_NAME_TOOL) + # Environment variables are always preserved. + set(ENV{_CMAKE_INSTALL_NAME_TOOL} "${CMAKE_INSTALL_NAME_TOOL}") +elseif(DEFINED ENV{_CMAKE_INSTALL_NAME_TOOL}) + set(CMAKE_INSTALL_NAME_TOOL "$ENV{_CMAKE_INSTALL_NAME_TOOL}") +elseif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT_INT} -find install_name_tool + OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE INTERNAL "") +endif() + +# Configure libtool to be used instead of ar + ranlib to build static libraries. +# This is required on Xcode 7+, but should also work on previous versions of +# Xcode. +get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach(lang ${languages}) + set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o " CACHE INTERNAL "") +endforeach() + +# CMake 3.14+ support building for iOS, watchOS and tvOS out of the box. +if(MODERN_CMAKE) + if(SDK_NAME MATCHES "iphone") + set(CMAKE_SYSTEM_NAME iOS) + elseif(SDK_NAME MATCHES "macosx") + set(CMAKE_SYSTEM_NAME Darwin) + elseif(SDK_NAME MATCHES "appletv") + set(CMAKE_SYSTEM_NAME tvOS) + elseif(SDK_NAME MATCHES "watch") + set(CMAKE_SYSTEM_NAME watchOS) + endif() + # Provide flags for a combined FAT library build on newer CMake versions + if(PLATFORM_INT MATCHES ".*COMBINED") + set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "NO") + set(CMAKE_IOS_INSTALL_COMBINED YES) + message(STATUS "Will combine built (static) artifacts into FAT lib...") + endif() +elseif(NOT DEFINED CMAKE_SYSTEM_NAME AND ${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.10") + # Legacy code path prior to CMake 3.14 or fallback if no CMAKE_SYSTEM_NAME specified + set(CMAKE_SYSTEM_NAME iOS) +elseif(NOT DEFINED CMAKE_SYSTEM_NAME) + # Legacy code path prior to CMake 3.14 or fallback if no CMAKE_SYSTEM_NAME specified + set(CMAKE_SYSTEM_NAME Darwin) +endif() +# Standard settings. +set(CMAKE_SYSTEM_VERSION ${SDK_VERSION} CACHE INTERNAL "") +set(UNIX TRUE CACHE BOOL "") +set(APPLE TRUE CACHE BOOL "") +if(PLATFORM STREQUAL "MAC" OR PLATFORM STREQUAL "MAC_ARM64") + set(IOS FALSE CACHE BOOL "") + set(MACOS TRUE CACHE BOOL "") +elseif(PLATFORM STREQUAL "MAC_CATALYST" OR PLATFORM STREQUAL "MAC_CATALYST_ARM64") + set(IOS TRUE CACHE BOOL "") + set(MACOS TRUE CACHE BOOL "") +else() + set(IOS TRUE CACHE BOOL "") +endif() +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) +set(CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE) +set(CMAKE_STRIP strip CACHE FILEPATH "" FORCE) +# Set the architectures for which to build. +set(CMAKE_OSX_ARCHITECTURES ${ARCHS} CACHE INTERNAL "") +# Change the type of target generated for try_compile() so it'll work when cross-compiling, weak compiler checks +if(NOT ENABLE_STRICT_TRY_COMPILE_INT) + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() +# All iOS/Darwin specific settings - some may be redundant. +set(CMAKE_MACOSX_BUNDLE YES) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") +set(CMAKE_SHARED_LIBRARY_PREFIX "lib") +set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set(CMAKE_SHARED_MODULE_PREFIX "lib") +set(CMAKE_SHARED_MODULE_SUFFIX ".so") +set(CMAKE_C_COMPILER_ABI ELF) +set(CMAKE_CXX_COMPILER_ABI ELF) +set(CMAKE_C_HAS_ISYSROOT 1) +set(CMAKE_CXX_HAS_ISYSROOT 1) +set(CMAKE_MODULE_EXISTS 1) +set(CMAKE_DL_LIBS "") +set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +if(ARCHS MATCHES "((^|;|, )(arm64|arm64e|x86_64))+") + set(CMAKE_C_SIZEOF_DATA_PTR 8) + set(CMAKE_CXX_SIZEOF_DATA_PTR 8) + if(ARCHS MATCHES "((^|;|, )(arm64|arm64e))+") + set(CMAKE_SYSTEM_PROCESSOR "aarch64") + else() + set(CMAKE_SYSTEM_PROCESSOR "x86_64") + endif() +else() + set(CMAKE_C_SIZEOF_DATA_PTR 4) + set(CMAKE_CXX_SIZEOF_DATA_PTR 4) + set(CMAKE_SYSTEM_PROCESSOR "arm") +endif() + +# Note that only Xcode 7+ supports the newer more specific: +# -m${SDK_NAME}-version-min flags, older versions of Xcode use: +# -m(ios/ios-simulator)-version-min instead. +if(${CMAKE_VERSION} VERSION_LESS "3.11") + if(PLATFORM_INT STREQUAL "OS" OR PLATFORM_INT STREQUAL "OS64") + if(XCODE_VERSION_INT VERSION_LESS 7.0) + set(SDK_NAME_VERSION_FLAGS + "-mios-version-min=${DEPLOYMENT_TARGET}") + else() + # Xcode 7.0+ uses flags we can build directly from SDK_NAME. + set(SDK_NAME_VERSION_FLAGS + "-m${SDK_NAME}-version-min=${DEPLOYMENT_TARGET}") + endif() + elseif(PLATFORM_INT STREQUAL "TVOS") + set(SDK_NAME_VERSION_FLAGS + "-mtvos-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") + set(SDK_NAME_VERSION_FLAGS + "-mtvos-simulator-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "WATCHOS") + set(SDK_NAME_VERSION_FLAGS + "-mwatchos-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") + set(SDK_NAME_VERSION_FLAGS + "-mwatchos-simulator-version-min=${DEPLOYMENT_TARGET}") + elseif(PLATFORM_INT STREQUAL "MAC") + set(SDK_NAME_VERSION_FLAGS + "-mmacosx-version-min=${DEPLOYMENT_TARGET}") + else() + # SIMULATOR or SIMULATOR64 both use -mios-simulator-version-min. + set(SDK_NAME_VERSION_FLAGS + "-mios-simulator-version-min=${DEPLOYMENT_TARGET}") + endif() +elseif(NOT PLATFORM_INT MATCHES "^MAC_CATALYST") + # Newer versions of CMake sets the version min flags correctly, skip this for Mac Catalyst targets + set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET}) +endif() + +if(DEFINED APPLE_TARGET_TRIPLE_INT) + set(APPLE_TARGET_TRIPLE ${APPLE_TARGET_TRIPLE_INT} CACHE INTERNAL "") + set(CMAKE_C_COMPILER_TARGET ${APPLE_TARGET_TRIPLE}) + set(CMAKE_CXX_COMPILER_TARGET ${APPLE_TARGET_TRIPLE}) + set(CMAKE_ASM_COMPILER_TARGET ${APPLE_TARGET_TRIPLE}) +endif() + +if(PLATFORM_INT MATCHES "^MAC_CATALYST") + set(C_TARGET_FLAGS "-isystem ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/usr/include -iframework ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/System/Library/Frameworks") +endif() + +if(ENABLE_BITCODE_INT) + set(BITCODE "-fembed-bitcode") + set(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE "bitcode") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") +else() + set(BITCODE "") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") +endif() + +if(ENABLE_ARC_INT) + set(FOBJC_ARC "-fobjc-arc") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "YES") +else() + set(FOBJC_ARC "-fno-objc-arc") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC "NO") +endif() + +if(NOT ENABLE_VISIBILITY_INT) + foreach(lang ${languages}) + set(CMAKE_${lang}_VISIBILITY_PRESET "hidden" CACHE INTERNAL "") + endforeach() + set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "YES") + set(VISIBILITY "-fvisibility=hidden -fvisibility-inlines-hidden") +else() + foreach(lang ${languages}) + set(CMAKE_${lang}_VISIBILITY_PRESET "default" CACHE INTERNAL "") + endforeach() + set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN "NO") + set(VISIBILITY "-fvisibility=default") +endif() + +if(DEFINED APPLE_TARGET_TRIPLE) + set(APPLE_TARGET_TRIPLE_FLAG "-target ${APPLE_TARGET_TRIPLE}") +endif() + +#Check if Xcode generator is used, since that will handle these flags automagically +if(CMAKE_GENERATOR MATCHES "Xcode") + message(STATUS "Not setting any manual command-line buildflags, since Xcode is selected as generator.") +else() + # Hidden visibility is required for C++ on iOS. + set(CMAKE_C_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${C_TARGET_FLAGS} ${APPLE_TARGET_TRIPLE_FLAG} ${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -g ${CMAKE_CXX_FLAGS_DEBUG}") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DNDEBUG -Os -ffast-math ${CMAKE_CXX_FLAGS_MINSIZEREL}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -DNDEBUG -O2 -g -ffast-math ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DNDEBUG -O3 -ffast-math ${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_C_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") + set(CMAKE_CXX_LINK_FLAGS "${C_TARGET_FLAGS} ${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") + set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -arch ${CMAKE_OSX_ARCHITECTURES} ${APPLE_TARGET_TRIPLE_FLAG}") +endif() + +## Print status messages to inform of the current state +message(STATUS "Configuring ${SDK_NAME} build for platform: ${PLATFORM_INT}, architecture(s): ${ARCHS}") +message(STATUS "Using SDK: ${CMAKE_OSX_SYSROOT_INT}") +message(STATUS "Using C compiler: ${CMAKE_C_COMPILER}") +message(STATUS "Using CXX compiler: ${CMAKE_CXX_COMPILER}") +message(STATUS "Using libtool: ${BUILD_LIBTOOL}") +message(STATUS "Using install name tool: ${CMAKE_INSTALL_NAME_TOOL}") +if(DEFINED APPLE_TARGET_TRIPLE) + message(STATUS "Autoconf target triple: ${APPLE_TARGET_TRIPLE}") +endif() +message(STATUS "Using minimum deployment version: ${DEPLOYMENT_TARGET}" + " (SDK version: ${SDK_VERSION})") +if(MODERN_CMAKE) + message(STATUS "Merging integrated CMake 3.14+ iOS,tvOS,watchOS,macOS toolchain(s) with this toolchain!") +endif() +if(CMAKE_GENERATOR MATCHES "Xcode") + message(STATUS "Using Xcode version: ${XCODE_VERSION_INT}") +endif() +message(STATUS "CMake version: ${CMAKE_VERSION}") +if(DEFINED SDK_NAME_VERSION_FLAGS) + message(STATUS "Using version flags: ${SDK_NAME_VERSION_FLAGS}") +endif() +message(STATUS "Using a data_ptr size of: ${CMAKE_CXX_SIZEOF_DATA_PTR}") +if(ENABLE_BITCODE_INT) + message(STATUS "Bitcode: Enabled") +else() + message(STATUS "Bitcode: Disabled") +endif() + +if(ENABLE_ARC_INT) + message(STATUS "ARC: Enabled") +else() + message(STATUS "ARC: Disabled") +endif() + +if(ENABLE_VISIBILITY_INT) + message(STATUS "Hiding symbols: Disabled") +else() + message(STATUS "Hiding symbols: Enabled") +endif() + +# Set global properties +set_property(GLOBAL PROPERTY PLATFORM "${PLATFORM}") +set_property(GLOBAL PROPERTY APPLE_TARGET_TRIPLE "${APPLE_TARGET_TRIPLE_INT}") +set_property(GLOBAL PROPERTY SDK_VERSION "${SDK_VERSION}") +set_property(GLOBAL PROPERTY XCODE_VERSION "${XCODE_VERSION_INT}") +set_property(GLOBAL PROPERTY OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}") + +# Export configurable variables for the try_compile() command. +set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + PLATFORM + XCODE_VERSION_INT + SDK_VERSION + DEPLOYMENT_TARGET + CMAKE_DEVELOPER_ROOT + CMAKE_OSX_SYSROOT_INT + ENABLE_BITCODE + ENABLE_ARC + CMAKE_ASM_COMPILER + CMAKE_C_COMPILER + CMAKE_C_COMPILER_TARGET + CMAKE_CXX_COMPILER + CMAKE_CXX_COMPILER_TARGET + BUILD_LIBTOOL + CMAKE_INSTALL_NAME_TOOL + CMAKE_C_FLAGS + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_LINK_FLAGS + CMAKE_CXX_LINK_FLAGS + CMAKE_ASM_FLAGS + ) + +set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") +set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") +set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") + +# Set the find root to the SDK developer roots. +# Note: CMAKE_FIND_ROOT_PATH is only useful when cross-compiling. Thus, do not set on macOS builds. +if(NOT PLATFORM_INT STREQUAL "MAC" AND NOT PLATFORM_INT STREQUAL "MAC_ARM64") + list(APPEND CMAKE_FIND_ROOT_PATH "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") + set(CMAKE_IGNORE_PATH "/System/Library/Frameworks;/usr/local/lib" CACHE INTERNAL "") +endif() + +# Default to searching for frameworks first. +set(CMAKE_FIND_FRAMEWORK FIRST) + +# Set up the default search directories for frameworks. +if(PLATFORM_INT MATCHES "^MAC_CATALYST") + set(CMAKE_FRAMEWORK_PATH + ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks + ${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks + ${CMAKE_OSX_SYSROOT_INT}/System/iOSSupport/System/Library/Frameworks + ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") +else() + set(CMAKE_FRAMEWORK_PATH + ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks + ${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks + ${CMAKE_FRAMEWORK_PATH} CACHE INTERNAL "") +endif() + +# By default, search both the specified iOS SDK and the remainder of the host filesystem. +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH CACHE INTERNAL "") +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH CACHE INTERNAL "") +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH CACHE INTERNAL "") +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH CACHE INTERNAL "") +endif() + +# +# Some helper-macros below to simplify and beautify the CMakeFile +# + +# This little macro lets you set any Xcode specific property. +macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE XCODE_RELVERSION) + set(XCODE_RELVERSION_I "${XCODE_RELVERSION}") + if(XCODE_RELVERSION_I STREQUAL "All") + set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} "${XCODE_VALUE}") + else() + set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY}[variant=${XCODE_RELVERSION_I}] "${XCODE_VALUE}") + endif() +endmacro(set_xcode_property) + +# This macro lets you find executable programs on the host system. +macro(find_host_package) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) + set(_TOOLCHAIN_IOS ${IOS}) + set(IOS FALSE) + find_package(${ARGN}) + set(IOS ${_TOOLCHAIN_IOS}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) +endmacro(find_host_package) diff --git a/config.h.in b/config.h.in index 2264e5952aeb..2edd6ba37fd8 100644 --- a/config.h.in +++ b/config.h.in @@ -25,6 +25,7 @@ #cmakedefine HAVE_VALGRIND_MEMCHECK_H #cmakedefine HAVE_EXECINFO_H #cmakedefine HAVE_GETLOGIN_R +#cmakedefine HAVE_GETPWUID_R /* Features */ #cmakedefine SWRESAMPLE_FOUND @@ -63,17 +64,22 @@ #cmakedefine WITH_FFMPEG #cmakedefine WITH_DSP_EXPERIMENTAL #cmakedefine WITH_DSP_FFMPEG -#cmakedefine WITH_X264 #cmakedefine WITH_OPENCL #cmakedefine WITH_MEDIA_FOUNDATION +#cmakedefine WITH_MEDIACODEC #cmakedefine WITH_VAAPI +#cmakedefine WITH_CHANNEL_GFXREDIR + /* Plugins */ #cmakedefine BUILTIN_CHANNELS #cmakedefine WITH_RDPDR /* Channels */ +#cmakedefine CHANNEL_AINPUT +#cmakedefine CHANNEL_AINPUT_CLIENT +#cmakedefine CHANNEL_AINPUT_SERVER #cmakedefine CHANNEL_AUDIN #cmakedefine CHANNEL_AUDIN_CLIENT #cmakedefine CHANNEL_AUDIN_SERVER @@ -95,6 +101,9 @@ #cmakedefine CHANNEL_ENCOMSP #cmakedefine CHANNEL_ENCOMSP_CLIENT #cmakedefine CHANNEL_ENCOMSP_SERVER +#cmakedefine CHANNEL_GFXREDIR +#cmakedefine CHANNEL_GFXREDIR_CLIENT +#cmakedefine CHANNEL_GFXREDIR_SERVER #cmakedefine CHANNEL_PARALLEL #cmakedefine CHANNEL_PARALLEL_CLIENT #cmakedefine CHANNEL_PARALLEL_SERVER @@ -107,6 +116,9 @@ #cmakedefine CHANNEL_RDPDR #cmakedefine CHANNEL_RDPDR_CLIENT #cmakedefine CHANNEL_RDPDR_SERVER +#cmakedefine CHANNEL_RDPECAM +#cmakedefine CHANNEL_RDPECAM_CLIENT +#cmakedefine CHANNEL_RDPECAM_SERVER #cmakedefine CHANNEL_RDPEI #cmakedefine CHANNEL_RDPEI_CLIENT #cmakedefine CHANNEL_RDPEI_SERVER @@ -128,6 +140,9 @@ #cmakedefine CHANNEL_SSHAGENT #cmakedefine CHANNEL_SSHAGENT_CLIENT #cmakedefine CHANNEL_SSHAGENT_SERVER +#cmakedefine CHANNEL_TELEMETRY +#cmakedefine CHANNEL_TELEMETRY_CLIENT +#cmakedefine CHANNEL_TELEMETRY_SERVER #cmakedefine CHANNEL_TSMF #cmakedefine CHANNEL_TSMF_CLIENT #cmakedefine CHANNEL_TSMF_SERVER @@ -173,4 +188,10 @@ /* Proxy */ #cmakedefine WITH_PROXY_MODULES +#cmakedefine WITH_INTERNAL_MD4 +#cmakedefine WITH_INTERNAL_MD5 + +/* Uwac */ +#cmakedefine HAVE_PIXMAN_REGION + #endif /* FREERDP_CONFIG_H */ diff --git a/docs/version_detection.md b/docs/version_detection.md new file mode 100644 index 000000000000..0cd3b896306f --- /dev/null +++ b/docs/version_detection.md @@ -0,0 +1,35 @@ +As FreeRDPs is build on different OS with different build tools and methods the +"version detection" has grown historically. +This document quickly describes how it's currently used. + +When doing a `xfreerdp /version` for example the following is shown + +`This is FreeRDP version 3.0.0-dev (c99c4cecddee4e5b914b122bc1531d47a668bb8e)` + +The first part ist the Version as defined in `RAW_VERSION_STRING` and the second part, in braces, +the `GIT_REVISON` of this version. + +`RAW_VERSION_STRING` is very vital as it determines the version used for libraries as well also for +all sub-projects as WinPR. + +As default both variables are equal. + +For nightly or development builds it is often of advantage to have the actual version from git +instead of having the hard coded value set in CMakeLists.txt. For this the cmake variable `USE_VERSION_FROM_GIT_TAG` +can be set. In order for this to work you need a) source checkout and b) git command line utility. +If enabled the information from the last git tag (in the format major.minor.patch-extra like +2.6.0-android12) will be used. + +If you are building FreeRDP and can't use git because it's not available or the source is not in an +git repository - for example when building packages - the files `.source_tag` and `.source_version` +in the top-level source directory can be used. `.source_tag` is equal to `RAW_VERSION_STRING` and +need to contain the version in the same format as the git tag. `.source_version` is used to pre-fill +`GIT_REVISON`. Although mostly used for that it must not contain a git commit or tag - it can be +used to set additional arbitrary information. Our recommendation for packagers is to create +`.source_version` when importing and set it to the upstream commit or tag to simplify issue +tracking. + +As summary the different mechanisms are applied in that order: +* `.source_tag` and `.source_version` if found +* version set from the last git tag if `RAW_VERSION_STRING` is set +* hard coded version in CMakeLists.txt diff --git a/include/freerdp/channels/ainput.h b/include/freerdp/channels/ainput.h new file mode 100644 index 000000000000..fe9faef3f453 --- /dev/null +++ b/include/freerdp/channels/ainput.h @@ -0,0 +1,61 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Audio Input Redirection Virtual Channel + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_AINPUT_H +#define FREERDP_CHANNEL_AINPUT_H + +#include +#include +#include + +#define AINPUT_CHANNEL_NAME "ainput" +#define AINPUT_DVC_CHANNEL_NAME "FreeRDP::Advanced::Input" + +typedef enum +{ + MSG_AINPUT_VERSION = 0x01, + MSG_AINPUT_MOUSE = 0x02 +} eAInputMsgType; + +typedef enum +{ + AINPUT_FLAGS_WHEEL = 0x0001, + AINPUT_FLAGS_MOVE = 0x0004, + AINPUT_FLAGS_DOWN = 0x0008, + + AINPUT_FLAGS_REL = 0x0010, + AINPUT_FLAGS_HAVE_REL = 0x0020, + + /* Pointer Flags */ + AINPUT_FLAGS_BUTTON1 = 0x1000, /* left */ + AINPUT_FLAGS_BUTTON2 = 0x2000, /* right */ + AINPUT_FLAGS_BUTTON3 = 0x4000, /* middle */ + + /* Extended Pointer Flags */ + AINPUT_XFLAGS_BUTTON1 = 0x0100, + AINPUT_XFLAGS_BUTTON2 = 0x0200 +} AInputEventFlags; + +typedef struct ainput_client_context AInputClientContext; + +#define AINPUT_VERSION_MAJOR 1 +#define AINPUT_VERSION_MINOR 0 + +#endif /* FREERDP_CHANNEL_AINPUT_H */ diff --git a/include/freerdp/channels/cliprdr.h b/include/freerdp/channels/cliprdr.h index 86fc65890ba3..fbf23f6e5c9e 100644 --- a/include/freerdp/channels/cliprdr.h +++ b/include/freerdp/channels/cliprdr.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -93,17 +94,6 @@ extern "C" { #endif - FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, - FILEDESCRIPTORW** file_descriptor_array, - UINT32* file_descriptor_count); - FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, BYTE** format_data, - UINT32* format_data_length); - FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags, - const FILEDESCRIPTORW* file_descriptor_array, - UINT32 file_descriptor_count, - BYTE** format_data, UINT32* format_data_length); - #ifdef __cplusplus } #endif diff --git a/include/freerdp/channels/gfxredir.h b/include/freerdp/channels/gfxredir.h new file mode 100644 index 000000000000..297911564c0b --- /dev/null +++ b/include/freerdp/channels/gfxredir.h @@ -0,0 +1,183 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Hideyuki Nagase + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_GFXREDIR_H +#define FREERDP_CHANNEL_GFXREDIR_H + +#include +#include + +#define GFXREDIR_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::RemoteAppGraphicsRedirection" + +/* GFXREDIR_LEGACY_CAPS_PDU.version */ +#define GFXREDIR_CHANNEL_VERSION_LEGACY 1 + +#define GFXREDIR_CHANNEL_VERSION_MAJOR 2 +#define GFXREDIR_CHANNEL_VERSION_MINOR 0 + +/* GFXREDIR_CAPS_VERSION1 */ +#define GFXREDIR_CMDID_LEGACY_CAPS 0x00000001 +#define GFXREDIR_CMDID_ERROR 0x00000006 +#define GFXREDIR_CMDID_CAPS_ADVERTISE 0x00000008 +#define GFXREDIR_CMDID_CAPS_CONFIRM 0x00000009 +/* GFXREDIR_CAPS_VERSION2_0 */ +#define GFXREDIR_CMDID_OPEN_POOL 0x0000000a +#define GFXREDIR_CMDID_CLOSE_POOL 0x0000000b +#define GFXREDIR_CMDID_CREATE_BUFFER 0x0000000c +#define GFXREDIR_CMDID_DESTROY_BUFFER 0x0000000d +#define GFXREDIR_CMDID_PRESENT_BUFFER 0x0000000e +#define GFXREDIR_CMDID_PRESENT_BUFFER_ACK 0x0000000f + +/* GFXREDIR_HEADER */ +#define GFXREDIR_HEADER_SIZE 8 + +/* GFXREDIR_CAPS_HEADER */ +#define GFXREDIR_CAPS_HEADER_SIZE 12 +/* GFXREDIR_CAPS_HEADER.signature */ +#define GFXREDIR_CAPS_SIGNATURE 0x53504143 /* = 'SPAC' */ +/* GFXREDIR_CAPS_HEADER.version */ +#define GFXREDIR_CAPS_VERSION1 0x1 +#define GFXREDIR_CAPS_VERSION2_0 0x2000 + +/* GFXREDIR_CREATE_BUFFER_PDU.format */ +#define GFXREDIR_BUFFER_PIXEL_FORMAT_XRGB_8888 1 +#define GFXREDIR_BUFFER_PIXEL_FORMAT_ARGB_8888 2 + +/* GFXREDIR_PRESENT_BUFFER_PDU.numOpaqueRects */ +#define GFXREDIR_MAX_OPAQUE_RECTS 0x10 + +struct _GFXREDIR_HEADER +{ + UINT32 cmdId; + UINT32 length; +}; + +typedef struct _GFXREDIR_HEADER GFXREDIR_HEADER; + +struct _GFXREDIR_LEGACY_CAPS_PDU +{ + UINT16 version; // GFXREDIR_CHANNEL_VERSION_LEGACY +}; + +typedef struct _GFXREDIR_LEGACY_CAPS_PDU GFXREDIR_LEGACY_CAPS_PDU; + +struct _GFXREDIR_CAPS_HEADER +{ + UINT32 signature; // GFXREDIR_CAPS_SIGNATURE + UINT32 version; // GFXREDIR_CAPS_VERSION + UINT32 length; // GFXREDIR_CAPS_HEADER_SIZE + size of capsData + const BYTE capsData[0]; // GFXREDIR_CAPS_HEADER.length - GFXREDIR_CAPS_HEADER_SIZE +}; + +typedef struct _GFXREDIR_CAPS_HEADER GFXREDIR_CAPS_HEADER; + +struct _GFXREDIR_CAPS_V2_0_PDU +{ + GFXREDIR_CAPS_HEADER header; + UINT32 supportedFeatures; /* Reserved for future extensions */ +}; + +typedef struct _GFXREDIR_CAPS_V2_0_PDU GFXREDIR_CAPS_V2_0_PDU; + +struct _GFXREDIR_ERROR_PDU +{ + UINT32 errorCode; +}; + +typedef struct _GFXREDIR_ERROR_PDU GFXREDIR_ERROR_PDU; + +struct _GFXREDIR_CAPS_ADVERTISE_PDU +{ + UINT32 length; // length of caps; + const BYTE *caps; // points variable length array of GFXREDIR_CAPS_HEADER. +}; + +typedef struct _GFXREDIR_CAPS_ADVERTISE_PDU GFXREDIR_CAPS_ADVERTISE_PDU; + +struct _GFXREDIR_CAPS_CONFIRM_PDU +{ + UINT32 version; // confirmed version, must be one of advertised by client. + UINT32 length; // GFXREDIR_CAPS_HEADER_SIZE + size of capsData. + const BYTE *capsData; // confirmed capsData from selected GFXREDIR_CAPS_HEADER.capsData. +}; + +typedef struct _GFXREDIR_CAPS_CONFIRM_PDU GFXREDIR_CAPS_CONFIRM_PDU; + +struct _GFXREDIR_OPEN_POOL_PDU +{ + UINT64 poolId; + UINT64 poolSize; + UINT32 sectionNameLength; // number of charactor, must include null terminated char. + const unsigned short *sectionName; // Windows-style 2 bytes wchar_t with null-terminated. +}; + +typedef struct _GFXREDIR_OPEN_POOL_PDU GFXREDIR_OPEN_POOL_PDU; + +struct _GFXREDIR_CLOSE_POOL_PDU +{ + UINT64 poolId; +}; + +typedef struct _GFXREDIR_CLOSE_POOL_PDU GFXREDIR_CLOSE_POOL_PDU; + +struct _GFXREDIR_CREATE_BUFFER_PDU +{ + UINT64 poolId; + UINT64 bufferId; + UINT64 offset; + UINT32 stride; + UINT32 width; + UINT32 height; + UINT32 format; // GFXREDIR_BUFFER_PIXEL_FORMAT_ +}; + +typedef struct _GFXREDIR_CREATE_BUFFER_PDU GFXREDIR_CREATE_BUFFER_PDU; + +struct _GFXREDIR_DESTROY_BUFFER_PDU +{ + UINT64 bufferId; +}; + +typedef struct _GFXREDIR_DESTROY_BUFFER_PDU GFXREDIR_DESTROY_BUFFER_PDU; + +struct _GFXREDIR_PRESENT_BUFFER_PDU +{ + UINT64 timestamp; + UINT64 presentId; + UINT64 windowId; + UINT64 bufferId; + UINT32 orientation; // 0, 90, 180 or 270. + UINT32 targetWidth; + UINT32 targetHeight; + RECTANGLE_32 dirtyRect; + UINT32 numOpaqueRects; + RECTANGLE_32* opaqueRects; +}; + +typedef struct _GFXREDIR_PRESENT_BUFFER_PDU GFXREDIR_PRESENT_BUFFER_PDU; + +struct _GFXREDIR_PRESENT_BUFFER_ACK_PDU +{ + UINT64 windowId; + UINT64 presentId; +}; + +typedef struct _GFXREDIR_PRESENT_BUFFER_ACK_PDU GFXREDIR_PRESENT_BUFFER_ACK_PDU; + +#endif /* FREERDP_CHANNEL_GFXREDIR_H */ diff --git a/include/freerdp/channels/rdpdr.h b/include/freerdp/channels/rdpdr.h index b61a4c7e1891..6cd283b8e620 100644 --- a/include/freerdp/channels/rdpdr.h +++ b/include/freerdp/channels/rdpdr.h @@ -41,6 +41,14 @@ #define RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH 32 #define RDPDR_DEVICE_IO_CONTROL_RSP_HDR_LENGTH 4 +#define RDPDR_VERSION_MAJOR 0x0001 + +#define RDPDR_VERSION_MINOR_RDP50 0x0002 +#define RDPDR_VERSION_MINOR_RDP51 0x0005 +#define RDPDR_VERSION_MINOR_RDP52 0x000A +#define RDPDR_VERSION_MINOR_RDP6X 0x000C +#define RDPDR_VERSION_MINOR_RDP10X 0x000D + /* RDPDR_HEADER.Component */ enum RDPDR_CTYP { diff --git a/include/freerdp/channels/rdpecam.h b/include/freerdp/channels/rdpecam.h new file mode 100644 index 000000000000..2d5ae40088d1 --- /dev/null +++ b/include/freerdp/channels/rdpecam.h @@ -0,0 +1,336 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_RDPECAM_H +#define FREERDP_CHANNEL_RDPECAM_H + +#include +#include +#include + +#define RDPECAM_DVC_CHANNEL_NAME "rdpecam" +#define RDPECAM_CONTROL_DVC_CHANNEL_NAME "RDCamera_Device_Enumerator" + +typedef enum +{ + CAM_MSG_ID_SuccessResponse = 0x01, + CAM_MSG_ID_ErrorResponse = 0x02, + CAM_MSG_ID_SelectVersionRequest = 0x03, + CAM_MSG_ID_SelectVersionResponse = 0x04, + CAM_MSG_ID_DeviceAddedNotification = 0x05, + CAM_MSG_ID_DeviceRemovedNotification = 0x06, + CAM_MSG_ID_ActivateDeviceRequest = 0x07, + CAM_MSG_ID_DeactivateDeviceRequest = 0x08, + CAM_MSG_ID_StreamListRequest = 0x09, + CAM_MSG_ID_StreamListResponse = 0x0A, + CAM_MSG_ID_MediaTypeListRequest = 0x0B, + CAM_MSG_ID_MediaTypeListResponse = 0x0C, + CAM_MSG_ID_CurrentMediaTypeRequest = 0x0D, + CAM_MSG_ID_CurrentMediaTypeResponse = 0x0E, + CAM_MSG_ID_StartStreamsRequest = 0x0F, + CAM_MSG_ID_StopStreamsRequest = 0x10, + CAM_MSG_ID_SampleRequest = 0x11, + CAM_MSG_ID_SampleResponse = 0x12, + CAM_MSG_ID_SampleErrorResponse = 0x13, + CAM_MSG_ID_PropertyListRequest = 0x14, + CAM_MSG_ID_PropertyListResponse = 0x15, + CAM_MSG_ID_PropertyValueRequest = 0x16, + CAM_MSG_ID_PropertyValueResponse = 0x17, + CAM_MSG_ID_SetPropertyValueRequest = 0x18, +} CAM_MSG_ID; + +#define CAM_HEADER_SIZE 2 + +typedef struct +{ + BYTE Version; + CAM_MSG_ID MessageId; +} CAM_SHARED_MSG_HEADER; + +/* Messages Exchanged on the Device Enumeration Channel (2.2.2) */ + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_SELECT_VERSION_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_SELECT_VERSION_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + WCHAR* DeviceName; + char* VirtualChannelName; +} CAM_DEVICE_ADDED_NOTIFICATION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + char* VirtualChannelName; +} CAM_DEVICE_REMOVED_NOTIFICATION; + +/* Messages Exchanged on Device Channels (2.2.3) */ + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_SUCCESS_RESPONSE; + +typedef enum +{ + CAM_ERROR_CODE_UnexpectedError = 0x00000001, + CAM_ERROR_CODE_InvalidMessage = 0x00000002, + CAM_ERROR_CODE_NotInitialized = 0x00000003, + CAM_ERROR_CODE_InvalidRequest = 0x00000004, + CAM_ERROR_CODE_InvalidStreamNumber = 0x00000005, + CAM_ERROR_CODE_InvalidMediaType = 0x00000006, + CAM_ERROR_CODE_OutOfMemory = 0x00000007, + CAM_ERROR_CODE_ItemNotFound = 0x00000008, + CAM_ERROR_CODE_SetNotFound = 0x00000009, + CAM_ERROR_CODE_OperationNotSupported = 0x0000000A, +} CAM_ERROR_CODE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_ERROR_CODE ErrorCode; +} CAM_ERROR_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_ACTIVATE_DEVICE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_DEACTIVATE_DEVICE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_STREAM_LIST_REQUEST; + +typedef enum +{ + CAM_STREAM_FRAME_SOURCE_TYPE_Color = 0x0001, + CAM_STREAM_FRAME_SOURCE_TYPE_Infrared = 0x0002, + CAM_STREAM_FRAME_SOURCE_TYPE_Custom = 0x0008, +} CAM_STREAM_FRAME_SOURCE_TYPES; + +typedef enum +{ + CAM_STREAM_CATEGORY_Capture = 0x01, +} CAM_STREAM_CATEGORY; + +typedef struct +{ + CAM_STREAM_FRAME_SOURCE_TYPES FrameSourceTypes; + CAM_STREAM_CATEGORY StreamCategory; + BYTE Selected; + BYTE CanBeShared; +} CAM_STREAM_DESCRIPTION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE N_Descriptions; + CAM_STREAM_DESCRIPTION StreamDescriptions[255]; +} CAM_STREAM_LIST_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; +} CAM_MEDIA_TYPE_LIST_REQUEST; + +typedef enum +{ + CAM_MEDIA_FORMAT_H264 = 0x01, + CAM_MEDIA_FORMAT_MJPG = 0x02, + CAM_MEDIA_FORMAT_YUY2 = 0x03, + CAM_MEDIA_FORMAT_NV12 = 0x04, + CAM_MEDIA_FORMAT_I420 = 0x05, + CAM_MEDIA_FORMAT_RGB24 = 0x06, + CAM_MEDIA_FORMAT_RGB32 = 0x07, +} CAM_MEDIA_FORMAT; + +typedef enum +{ + CAM_MEDIA_TYPE_DESCRIPTION_FLAG_DecodingRequired = 0x01, + CAM_MEDIA_TYPE_DESCRIPTION_FLAG_BottomUpImage = 0x02, +} CAM_MEDIA_TYPE_DESCRIPTION_FLAGS; + +typedef struct +{ + CAM_MEDIA_FORMAT Format; + UINT32 Width; + UINT32 Height; + UINT32 FrameRateNumerator; + UINT32 FrameRateDenominator; + UINT32 PixelAspectRatioNumerator; + UINT32 PixelAspectRatioDenominator; + CAM_MEDIA_TYPE_DESCRIPTION_FLAGS Flags; +} CAM_MEDIA_TYPE_DESCRIPTION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + size_t N_Descriptions; + CAM_MEDIA_TYPE_DESCRIPTION* MediaTypeDescriptions; +} CAM_MEDIA_TYPE_LIST_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; +} CAM_CURRENT_MEDIA_TYPE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription; +} CAM_CURRENT_MEDIA_TYPE_RESPONSE; + +typedef struct +{ + BYTE StreamIndex; + CAM_MEDIA_TYPE_DESCRIPTION MediaTypeDescription; +} CAM_START_STREAM_INFO; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE N_Infos; + CAM_START_STREAM_INFO StartStreamsInfo[255]; +} CAM_START_STREAMS_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_STOP_STREAMS_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; +} CAM_SAMPLE_REQUEST; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; + size_t SampleSize; + BYTE* Sample; +} CAM_SAMPLE_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + BYTE StreamIndex; + CAM_ERROR_CODE ErrorCode; +} CAM_SAMPLE_ERROR_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; +} CAM_PROPERTY_LIST_REQUEST; + +typedef enum +{ + CAM_PROPERTY_SET_CameraControl = 0x01, + CAM_PROPERTY_SET_VideoProcAmp = 0x02, +} CAM_PROPERTY_SET; + +/* CameraControl properties */ +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Exposure 0x01 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Focus 0x02 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Pan 0x03 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Roll 0x04 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Tilt 0x05 +#define CAM_PROPERTY_ID_CAMERA_CONTROL_Zoom 0x06 + +/* VideoProcAmp properties */ +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_BacklightCompensation 0x01 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Brightness 0x02 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Contrast 0x03 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_Hue 0x04 +#define CAM_PROPERTY_ID_VIDEO_PROC_AMP_WhiteBalance 0x05 + +typedef enum +{ + CAM_PROPERTY_CAPABILITY_Manual = 0x01, + CAM_PROPERTY_CAPABILITY_Auto = 0x02, +} CAM_PROPERTY_CAPABILITIES; + +typedef struct +{ + CAM_PROPERTY_SET PropertySet; + BYTE PropertyId; + CAM_PROPERTY_CAPABILITIES Capabilities; + INT32 MinValue; + INT32 MaxValue; + INT32 Step; + INT32 DefaultValue; +} CAM_PROPERTY_DESCRIPTION; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + size_t N_Properties; + CAM_PROPERTY_DESCRIPTION* Properties; +} CAM_PROPERTY_LIST_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_PROPERTY_SET PropertySet; + BYTE PropertyId; +} CAM_PROPERTY_VALUE_REQUEST; + +typedef enum +{ + CAM_PROPERTY_MODE_Manual = 0x01, + CAM_PROPERTY_MODE_Auto = 0x02, +} CAM_PROPERTY_MODE; + +typedef struct +{ + CAM_PROPERTY_MODE Mode; + INT32 Value; +} CAM_PROPERTY_VALUE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_PROPERTY_VALUE PropertyValue; +} CAM_PROPERTY_VALUE_RESPONSE; + +typedef struct +{ + CAM_SHARED_MSG_HEADER Header; + CAM_PROPERTY_SET PropertySet; + BYTE PropertyId; + CAM_PROPERTY_VALUE PropertyValue; +} CAM_SET_PROPERTY_VALUE_REQUEST; + +#endif /* FREERDP_CHANNEL_RDPECAM_H */ diff --git a/include/freerdp/channels/rdpgfx.h b/include/freerdp/channels/rdpgfx.h index 095022ddfa15..b7511f864732 100644 --- a/include/freerdp/channels/rdpgfx.h +++ b/include/freerdp/channels/rdpgfx.h @@ -99,11 +99,16 @@ typedef struct _RDPGFX_HEADER RDPGFX_HEADER; #define RDPGFX_CAPVERSION_103 0x000A0301 /** [MS-RDPEGFX] 2.2.3.6 */ #define RDPGFX_CAPVERSION_104 0x000A0400 /** [MS-RDPEGFX] 2.2.3.7 */ #define RDPGFX_CAPVERSION_105 0x000A0502 /** [MS-RDPEGFX] 2.2.3.8 */ -#define RDPGFX_CAPVERSION_106 \ - 0x000A0600 /** [MS-RDPEGFX] 2.2.3.9, [MS-RDPEGFX-errata] 2018-12-10 \ +#define RDPGFX_CAPVERSION_106 \ + 0x000A0600 /** [MS-RDPEGFX] 2.2.3.9 (the value in the doc is wrong, see \ + * [MS-RDPEGFX]-180912-errata] \ + * Since this is/was documented for a long time, also define \ + * the incorrect value in case some server actually uses it. \ */ +#define RDPGFX_CAPVERSION_106_ERR 0x000A0601 +#define RDPGFX_CAPVERSION_107 0x000A0701 /** [MS-RDPEGFX] 2.2.3.10 */ -#define RDPGFX_NUMBER_CAPSETS 9 +#define RDPGFX_NUMBER_CAPSETS 11 #define RDPGFX_CAPSET_BASE_SIZE 8 struct _RDPGFX_CAPSET @@ -119,6 +124,7 @@ typedef struct _RDPGFX_CAPSET RDPGFX_CAPSET; #define RDPGFX_CAPS_FLAG_AVC420_ENABLED 0x00000010U /* 8.1+ */ #define RDPGFX_CAPS_FLAG_AVC_DISABLED 0x00000020U /* 10.0+ */ #define RDPGFX_CAPS_FLAG_AVC_THINCLIENT 0x00000040U /* 10.3+ */ +#define RDPGFX_CAPS_FLAG_SCALEDMAP_DISABLE 0x00000080U /* 10.7+ */ struct _RDPGFX_CAPSET_VERSION8 { diff --git a/include/freerdp/channels/telemetry.h b/include/freerdp/channels/telemetry.h new file mode 100644 index 000000000000..85ad1ff29c40 --- /dev/null +++ b/include/freerdp/channels/telemetry.h @@ -0,0 +1,38 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Telemetry Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_TELEMETRY_H +#define FREERDP_CHANNEL_TELEMETRY_H + +#include +#include +#include + +#define TELEMETRY_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::Telemetry" + +struct _TELEMETRY_RDP_TELEMETRY_PDU +{ + UINT32 PromptForCredentialsMillis; + UINT32 PromptForCredentialsDoneMillis; + UINT32 GraphicsChannelOpenedMillis; + UINT32 FirstGraphicsReceivedMillis; +}; +typedef struct _TELEMETRY_RDP_TELEMETRY_PDU TELEMETRY_RDP_TELEMETRY_PDU; + +#endif /* FREERDP_CHANNEL_TELEMETRY_H */ diff --git a/include/freerdp/channels/wtsvc.h b/include/freerdp/channels/wtsvc.h index fe937872c3bd..286c7520505e 100644 --- a/include/freerdp/channels/wtsvc.h +++ b/include/freerdp/channels/wtsvc.h @@ -52,6 +52,9 @@ extern "C" DRDYNVC_STATE_FAILED = 3 }; + typedef BOOL (*psDVCCreationStatusCallback)(void* userdata, UINT32 channelId, + INT32 creationStatus); + /** * WTSVirtualChannelManager functions are FreeRDP extensions to the API. */ @@ -62,6 +65,9 @@ extern "C" FREERDP_API HANDLE WTSVirtualChannelManagerGetEventHandle(HANDLE hServer); FREERDP_API BOOL WTSVirtualChannelManagerIsChannelJoined(HANDLE hServer, const char* name); FREERDP_API BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer); + FREERDP_API void WTSVirtualChannelManagerSetDVCCreationCallback(HANDLE hServer, + psDVCCreationStatusCallback cb, + void* userdata); /** * Extended FreeRDP WTS functions for channel handling @@ -76,6 +82,10 @@ extern "C" FREERDP_API void* WTSChannelGetHandleByName(freerdp_peer* client, const char* channel_name); FREERDP_API void* WTSChannelGetHandleById(freerdp_peer* client, const UINT16 channel_id); FREERDP_API const char* WTSChannelGetName(freerdp_peer* client, UINT16 channel_id); + FREERDP_API INT64 WTSChannelGetOptions(freerdp_peer* client, UINT16 channel_id); + FREERDP_API char** WTSGetAcceptedChannelNames(freerdp_peer* client, size_t* count); + + FREERDP_API UINT32 WTSChannelGetIdByHandle(HANDLE hChannelHandle); #ifdef __cplusplus } diff --git a/include/freerdp/client/ainput.h b/include/freerdp/client/ainput.h new file mode 100644 index 000000000000..14474835e7aa --- /dev/null +++ b/include/freerdp/client/ainput.h @@ -0,0 +1,38 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Display Update Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_AINPUT_CLIENT_AINPUT_H +#define FREERDP_CHANNEL_AINPUT_CLIENT_AINPUT_H + +#include + +typedef UINT (*pcAInputSendInputEvent)(AInputClientContext* context, UINT64 flags, INT32 x, + INT32 y); + +struct ainput_client_context +{ + void* handle; + void* custom; + + pcAInputSendInputEvent AInputSendInputEvent; +}; + +#endif /* FREERDP_CHANNEL_AINPUT_CLIENT_AINPUT_H */ diff --git a/include/freerdp/client/rail.h b/include/freerdp/client/rail.h index 0ce7dc33299e..ef5aefdaec71 100644 --- a/include/freerdp/client/rail.h +++ b/include/freerdp/client/rail.h @@ -86,6 +86,8 @@ typedef UINT (*pcRailServerGetAppidResponseExtended)(RailClientContext* context, const RAIL_GET_APPID_RESP_EX* id); typedef UINT (*pcRailClientCompartmentInfo)(RailClientContext* context, const RAIL_COMPARTMENT_INFO_ORDER* compartmentInfo); +typedef UINT (*pcRailClientTextScale)(RailClientContext* context, UINT32 TextScale); +typedef UINT (*pcRailClientCaretBlinkRate)(RailClientContext* context, UINT32 CaretBlinkRate); struct _rail_client_context { @@ -121,6 +123,8 @@ struct _rail_client_context pcRailServerGetAppidResponseExtended ServerGetAppidResponseExtended; pcRailClientCompartmentInfo ClientCompartmentInfo; pcRailOnOpen OnOpen; + pcRailClientTextScale ClientTextScale; + pcRailClientCaretBlinkRate ClientCaretBlinkRate; }; #endif /* FREERDP_CHANNEL_RAIL_CLIENT_RAIL_H */ diff --git a/include/freerdp/codec/color.h b/include/freerdp/codec/color.h index 42cb104d1c61..5d3e27904780 100644 --- a/include/freerdp/codec/color.h +++ b/include/freerdp/codec/color.h @@ -68,6 +68,8 @@ #define PIXEL_FORMAT_BGRX32 FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_BGRA, 0, 8, 8, 8) #define PIXEL_FORMAT_RGBA32 FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_RGBA, 8, 8, 8, 8) #define PIXEL_FORMAT_RGBX32 FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_RGBA, 0, 8, 8, 8) +#define PIXEL_FORMAT_BGRX32_DEPTH30 FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_BGRA, 0, 10, 10, 10) +#define PIXEL_FORMAT_RGBX32_DEPTH30 FREERDP_PIXEL_FORMAT(32, FREERDP_PIXEL_FORMAT_TYPE_RGBA, 0, 10, 10, 10) /* 24bpp formats */ #define PIXEL_FORMAT_RGB24 FREERDP_PIXEL_FORMAT(24, FREERDP_PIXEL_FORMAT_TYPE_ARGB, 0, 8, 8, 8) @@ -149,6 +151,12 @@ extern "C" case PIXEL_FORMAT_RGBX32: return "PIXEL_FORMAT_RGBX32"; + case PIXEL_FORMAT_BGRX32_DEPTH30: + return "PIXEL_FORMAT_BGRX32_DEPTH30"; + + case PIXEL_FORMAT_RGBX32_DEPTH30: + return "PIXEL_FORMAT_RGBX32_DEPTH30"; + /* 24bpp formats */ case PIXEL_FORMAT_RGB24: return "PIXEL_FORMAT_RGB24"; @@ -607,6 +615,7 @@ extern "C" UINT32 _g = g; UINT32 _b = b; UINT32 _a = a; + UINT32 t; switch (format) { @@ -635,6 +644,18 @@ extern "C" case PIXEL_FORMAT_BGRX32: return (_b << 24) | (_g << 16) | (_r << 8) | _a; + case PIXEL_FORMAT_RGBX32_DEPTH30: + // TODO: Not tested + t = (_r << 22) | (_g << 12) | (_b << 2); + // NOTE: Swapping byte-order because WriteColor written UINT32 in big-endian + return ((t & 0xff) << 24) | (((t >> 8) & 0xff) << 16) | (((t >> 16) & 0xff) << 8) | (t >> 24); + + case PIXEL_FORMAT_BGRX32_DEPTH30: + // NOTE: Swapping b and r channel (unknown reason) + t = (_r << 22) | (_g << 12) | (_b << 2); + // NOTE: Swapping byte-order because WriteColor written UINT32 in big-endian + return ((t & 0xff) << 24) | (((t >> 8) & 0xff) << 16) | (((t >> 16) & 0xff) << 8) | (t >> 24); + /* 24bpp formats */ case PIXEL_FORMAT_RGB24: return (_r << 16) | (_g << 8) | _b; diff --git a/include/freerdp/codec/nsc.h b/include/freerdp/codec/nsc.h index 6a1c35c40810..44ef209314c2 100644 --- a/include/freerdp/codec/nsc.h +++ b/include/freerdp/codec/nsc.h @@ -52,6 +52,27 @@ extern "C" FREERDP_API BOOL nsc_context_set_parameters(NSC_CONTEXT* context, NSC_PARAMETER what, UINT32 value); + /** @brief decode a NSC message + * + * @param context The context to work on + * @param bpp The bit depth of the data + * @param width The width in pixels of the NSC surface + * @param height The height in pixels of the NSC surface + * @param data The data to decode + * @param length The length of \ref data in bytes + * @param pDstData The destination buffer. Must be at least \n nDstStride * nHeight in size. + * @param DstFormat The color format of the destination + * @param nDstStride The number of bytes per destination buffer line. Must be larger than + * bytes(DstFormat) * nWidth or \0 (in which case it is set to former minimum bound) + * @param nXDst The X offset in the destination buffer in pixels + * @param nYDst The Y offset in the destination buffer in pixels + * @param nWidth The width of the destination buffer in pixels + * @param nHeight The height of the destination buffer in pixels + * @param flip Image flipping flags FREERDP_FLIP_NONE et al passed on to \ref + * freerdp_image_copy + * + * @return \b TRUE in case of success, \b FALSE for any error. Check WLog for details. + */ FREERDP_API BOOL nsc_process_message(NSC_CONTEXT* context, UINT16 bpp, UINT32 width, UINT32 height, const BYTE* data, UINT32 length, BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStride, diff --git a/include/freerdp/constants.h b/include/freerdp/constants.h index d7949efa49d6..44fb1cb54ba1 100644 --- a/include/freerdp/constants.h +++ b/include/freerdp/constants.h @@ -45,6 +45,10 @@ enum RDP_CODEC_ID #define OSMAJORTYPE_OS2 0x0002 #define OSMAJORTYPE_MACINTOSH 0x0003 #define OSMAJORTYPE_UNIX 0x0004 +#define OSMAJORTYPE_IOS 0x0005 +#define OSMAJORTYPE_OSX 0x0006 +#define OSMAJORTYPE_ANDROID 0x0007 +#define OSMAJORTYPE_CHROME_OS 0x0008 /** * OSMinorType @@ -58,6 +62,8 @@ enum RDP_CODEC_ID #define OSMINORTYPE_MACINTOSH 0x0006 #define OSMINORTYPE_NATIVE_XSERVER 0x0007 #define OSMINORTYPE_PSEUDO_XSERVER 0x0008 -#define OSMINORTYPE_NATIVE_WAYLAND 0x0009 +#define OSMINORTYPE_WINDOWS_RT 0x0009 +/* As of 2022-03-29 the following does not exist officially in [MS-RDPBCGR] */ +#define OSMINORTYPE_NATIVE_WAYLAND (0xFFFF - 1) #endif /* FREERDP_CONSTANTS_H */ diff --git a/include/freerdp/crypto/crypto.h b/include/freerdp/crypto/crypto.h index 33f1a1e3ff7b..c029ce7e91a4 100644 --- a/include/freerdp/crypto/crypto.h +++ b/include/freerdp/crypto/crypto.h @@ -22,6 +22,7 @@ /* OpenSSL includes windows.h */ #include +#include #include #include @@ -54,6 +55,7 @@ extern "C" typedef struct crypto_cert_struct* CryptoCert; FREERDP_API CryptoCert crypto_cert_read(BYTE* data, UINT32 length); + FREERDP_API WINPR_MD_TYPE crypto_cert_get_signature_alg(X509* xcert); FREERDP_API BYTE* crypto_cert_hash(X509* xcert, const char* hash, UINT32* length); FREERDP_API char* crypto_cert_fingerprint_by_hash(X509* xcert, const char* hash); FREERDP_API char* crypto_cert_fingerprint(X509* xcert); @@ -88,9 +90,9 @@ extern "C" DWORD* PublicKeyLength); #define TSSK_KEY_LENGTH 64 - extern const BYTE tssk_modulus[]; - extern const BYTE tssk_privateExponent[]; - extern const BYTE tssk_exponent[]; + WINPR_API extern const BYTE tssk_modulus[]; + WINPR_API extern const BYTE tssk_privateExponent[]; + WINPR_API extern const BYTE tssk_exponent[]; FREERDP_API int crypto_rsa_public_encrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* exponent, diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index b5b38c0271ed..235cd04b7c0a 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -70,6 +70,25 @@ extern "C" #define VERIFY_CERT_FLAG_MISMATCH 0x80 #define VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1 0x100 + typedef enum + { + CONNECTION_STATE_INITIAL, + CONNECTION_STATE_NEGO, + CONNECTION_STATE_NLA, + CONNECTION_STATE_MCS_CONNECT, + CONNECTION_STATE_MCS_ERECT_DOMAIN, + CONNECTION_STATE_MCS_ATTACH_USER, + CONNECTION_STATE_MCS_CHANNEL_JOIN, + CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT, + CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE, + CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT, + CONNECTION_STATE_LICENSING, + CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING, + CONNECTION_STATE_CAPABILITIES_EXCHANGE, + CONNECTION_STATE_FINALIZATION, + CONNECTION_STATE_ACTIVE + } CONNECTION_STATE; + /* Message types used by gateway messaging callback */ #define GATEWAY_MESSAGE_CONSENT 1 #define GATEWAY_MESSAGE_SERVICE 2 @@ -436,6 +455,7 @@ extern "C" FREERDP_API BOOL freerdp_connect(freerdp* instance); FREERDP_API BOOL freerdp_abort_connect(freerdp* instance); FREERDP_API BOOL freerdp_shall_disconnect(freerdp* instance); + FREERDP_API BOOL freerdp_shall_disconnect_context(rdpContext* context); FREERDP_API BOOL freerdp_disconnect(freerdp* instance); FREERDP_API BOOL freerdp_disconnect_before_reconnect(freerdp* instance); @@ -527,6 +547,9 @@ extern "C" FREERDP_API const char* freerdp_nego_get_routing_token(rdpContext* context, DWORD* length); + FREERDP_API CONNECTION_STATE freerdp_get_state(rdpContext* context); + FREERDP_API const char* freerdp_state_string(CONNECTION_STATE state); + #ifdef __cplusplus } #endif diff --git a/include/freerdp/locale/keyboard.h b/include/freerdp/locale/keyboard.h index ed2e6be6fcdf..a325afe66f02 100644 --- a/include/freerdp/locale/keyboard.h +++ b/include/freerdp/locale/keyboard.h @@ -171,6 +171,7 @@ typedef struct _RDP_KEYBOARD_LAYOUT RDP_KEYBOARD_LAYOUT; #define KBD_CZECH_PROGRAMMERS 0x00020405 #define KBD_GREEK_319 0x00020408 #define KBD_UNITED_STATES_INTERNATIONAL 0x00020409 +#define KBD_HEBREW_STANDARD 0x0002040D #define KBD_THAI_KEDMANEE_NON_SHIFTLOCK 0x0002041E #define KBD_SAMI_EXTENDED_FINLAND_SWEDEN 0x0002083B #define KBD_GREEK_220_LATIN 0x00030408 diff --git a/include/freerdp/locale/locale.h b/include/freerdp/locale/locale.h index 54ab2c66033b..6647bb22c28f 100644 --- a/include/freerdp/locale/locale.h +++ b/include/freerdp/locale/locale.h @@ -62,6 +62,7 @@ #define BRETON 0x047E #define BULGARIAN 0x0402 #define CATALAN 0x0403 +#define CHEROKEE 0x045C #define CHINESE_TAIWAN 0x0404 #define CHINESE_PRC 0x0804 #define CHINESE_HONG_KONG 0x0C04 @@ -113,12 +114,14 @@ #define GREEK 0x0408 #define GREENLANDIC 0x046F #define GUJARATI 0x0447 +#define HAWAIIAN 0x0475 #define HEBREW 0x040D #define HINDI 0x0439 #define HUNGARIAN 0x040E #define ICELANDIC 0x040F #define IGBO 0x0470 #define INDONESIAN 0x0421 +#define INUKTITUT 0x045D #define IRISH 0x083C #define ITALIAN_STANDARD 0x0410 #define ITALIAN_SWISS 0x0810 @@ -146,6 +149,7 @@ #define MARATHI 0x044E #define MOHAWK 0x047C #define MONGOLIAN 0x0450 +#define MYANMAR 0x0455 #define NEPALI 0x0461 #define NORWEGIAN_BOKMAL 0x0414 #define NORWEGIAN_NYNORSK 0x0814 diff --git a/include/freerdp/peer.h b/include/freerdp/peer.h index a6404444d359..90bbb461fe23 100644 --- a/include/freerdp/peer.h +++ b/include/freerdp/peer.h @@ -48,6 +48,13 @@ typedef BOOL (*psPeerCapabilities)(freerdp_peer* peer); typedef BOOL (*psPeerPostConnect)(freerdp_peer* peer); typedef BOOL (*psPeerActivate)(freerdp_peer* peer); typedef BOOL (*psPeerLogon)(freerdp_peer* peer, SEC_WINNT_AUTH_IDENTITY* identity, BOOL automatic); +typedef BOOL (*psPeerSendServerRedirection)(freerdp_peer* peer, UINT32 sessionId, + const char* targetNetAddress, const char* routingToken, + const char* userName, const char* domain, + const char* password, const char* targetFQDN, + const char* targetNetBiosName, DWORD tsvUrlLength, + const BYTE* tsvUrl, UINT32 targetNetAddressesCount, + const char** targetNetAddresses); typedef BOOL (*psPeerAdjustMonitorsLayout)(freerdp_peer* peer); typedef BOOL (*psPeerClientCapabilities)(freerdp_peer* peer); @@ -133,6 +140,8 @@ struct rdp_freerdp_peer psPeerClientCapabilities ClientCapabilities; psPeerComputeNtlmHash ComputeNtlmHash; psPeerLicenseCallback LicenseCallback; + + psPeerSendServerRedirection SendServerRedirection; }; #ifdef __cplusplus @@ -143,6 +152,9 @@ extern "C" FREERDP_API BOOL freerdp_peer_context_new(freerdp_peer* client); FREERDP_API void freerdp_peer_context_free(freerdp_peer* client); + FREERDP_API const char* freerdp_peer_os_major_type_string(freerdp_peer* client); + FREERDP_API const char* freerdp_peer_os_minor_type_string(freerdp_peer* client); + FREERDP_API freerdp_peer* freerdp_peer_new(int sockfd); FREERDP_API void freerdp_peer_free(freerdp_peer* client); diff --git a/include/freerdp/rail.h b/include/freerdp/rail.h index 019aff89a11a..fefffc1d9e03 100644 --- a/include/freerdp/rail.h +++ b/include/freerdp/rail.h @@ -135,14 +135,19 @@ enum SPI_MASK #endif /* Client Information PDU */ -#define TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE 0x00000001 -#define TS_RAIL_CLIENTSTATUS_AUTORECONNECT 0x00000002 -#define TS_RAIL_CLIENTSTATUS_ZORDER_SYNC 0x00000004 -#define TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED 0x00000010 -#define TS_RAIL_CLIENTSTATUS_HIGH_DPI_ICONS_SUPPORTED 0x00000020 -#define TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED 0x00000040 -#define TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED 0x00000080 -#define TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED 0x00000200 +typedef enum +{ + TS_RAIL_CLIENTSTATUS_ALLOWLOCALMOVESIZE = 0x00000001, + TS_RAIL_CLIENTSTATUS_AUTORECONNECT = 0x00000002, + TS_RAIL_CLIENTSTATUS_ZORDER_SYNC = 0x00000004, + TS_RAIL_CLIENTSTATUS_WINDOW_RESIZE_MARGIN_SUPPORTED = 0x00000010, + TS_RAIL_CLIENTSTATUS_HIGH_DPI_ICONS_SUPPORTED = 0x00000020, + TS_RAIL_CLIENTSTATUS_APPBAR_REMOTING_SUPPORTED = 0x00000040, + TS_RAIL_CLIENTSTATUS_POWER_DISPLAY_REQUEST_SUPPORTED = 0x00000080, + TS_RAIL_CLIENTSTATUS_GET_APPID_RESPONSE_EX_SUPPORTED = 0x00000100, + TS_RAIL_CLIENTSTATUS_BIDIRECTIONAL_CLOAK_SUPPORTED = 0x00000200, + TS_RAIL_CLIENTSTATUS_SUPPRESS_ICON_ORDERS = 0x00000400 +} CLIENT_INFO_PDU; /* Server Move/Size Start PDU */ #define RAIL_WMSZ_LEFT 0x0001 @@ -181,10 +186,15 @@ enum SPI_MASK #endif /* Extended Handshake Flags */ -#define TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF 0x00000001 -#define TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED 0x00000002 -#define TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED 0x00000004 - +typedef enum +{ + TS_RAIL_ORDER_HANDSHAKEEX_FLAGS_HIDEF = 0x00000001, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_SUPPORTED = 0x00000002, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_SNAP_ARRANGE_SUPPORTED = 0x00000004, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_TEXT_SCALE_SUPPORTED = 0x00000008, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_CARET_BLINK_SUPPORTED = 0x00000010, + TS_RAIL_ORDER_HANDSHAKE_EX_FLAGS_EXTENDED_SPI_2_SUPPORTED = 0x00000020 +} EXTENDED_HANDSHAKE_FLAGS; /* Language Profile Information Flags */ #define TF_PROFILETYPE_INPUTPROCESSOR 0x00000001 #define TF_PROFILETYPE_KEYBOARDLAYOUT 0x00000002 @@ -559,30 +569,35 @@ typedef struct _RAIL_GET_APPID_RESP_EX RAIL_GET_APPID_RESP_EX; /* RAIL Constants */ -#define TS_RAIL_ORDER_EXEC 0x0001 -#define TS_RAIL_ORDER_ACTIVATE 0x0002 -#define TS_RAIL_ORDER_SYSPARAM 0x0003 -#define TS_RAIL_ORDER_SYSCOMMAND 0x0004 -#define TS_RAIL_ORDER_HANDSHAKE 0x0005 -#define TS_RAIL_ORDER_NOTIFY_EVENT 0x0006 -#define TS_RAIL_ORDER_WINDOWMOVE 0x0008 -#define TS_RAIL_ORDER_LOCALMOVESIZE 0x0009 -#define TS_RAIL_ORDER_MINMAXINFO 0x000A -#define TS_RAIL_ORDER_CLIENTSTATUS 0x000B -#define TS_RAIL_ORDER_SYSMENU 0x000C -#define TS_RAIL_ORDER_LANGBARINFO 0x000D -#define TS_RAIL_ORDER_GET_APPID_REQ 0x000E -#define TS_RAIL_ORDER_GET_APPID_RESP 0x000F -#define TS_RAIL_ORDER_TASKBARINFO 0x0010 -#define TS_RAIL_ORDER_LANGUAGEIMEINFO 0x0011 -#define TS_RAIL_ORDER_COMPARTMENTINFO 0x0012 -#define TS_RAIL_ORDER_HANDSHAKE_EX 0x0013 -#define TS_RAIL_ORDER_ZORDER_SYNC 0x0014 -#define TS_RAIL_ORDER_CLOAK 0x0015 -#define TS_RAIL_ORDER_POWER_DISPLAY_REQUEST 0x0016 -#define TS_RAIL_ORDER_SNAP_ARRANGE 0x0017 -#define TS_RAIL_ORDER_GET_APPID_RESP_EX 0x0018 -#define TS_RAIL_ORDER_EXEC_RESULT 0x0080 +typedef enum +{ + TS_RAIL_ORDER_EXEC = 0x0001, + TS_RAIL_ORDER_ACTIVATE = 0x0002, + TS_RAIL_ORDER_SYSPARAM = 0x0003, + TS_RAIL_ORDER_SYSCOMMAND = 0x0004, + TS_RAIL_ORDER_HANDSHAKE = 0x0005, + TS_RAIL_ORDER_NOTIFY_EVENT = 0x0006, + TS_RAIL_ORDER_WINDOWMOVE = 0x0008, + TS_RAIL_ORDER_LOCALMOVESIZE = 0x0009, + TS_RAIL_ORDER_MINMAXINFO = 0x000A, + TS_RAIL_ORDER_CLIENTSTATUS = 0x000B, + TS_RAIL_ORDER_SYSMENU = 0x000C, + TS_RAIL_ORDER_LANGBARINFO = 0x000D, + TS_RAIL_ORDER_GET_APPID_REQ = 0x000E, + TS_RAIL_ORDER_GET_APPID_RESP = 0x000F, + TS_RAIL_ORDER_TASKBARINFO = 0x0010, + TS_RAIL_ORDER_LANGUAGEIMEINFO = 0x0011, + TS_RAIL_ORDER_COMPARTMENTINFO = 0x0012, + TS_RAIL_ORDER_HANDSHAKE_EX = 0x0013, + TS_RAIL_ORDER_ZORDER_SYNC = 0x0014, + TS_RAIL_ORDER_CLOAK = 0x0015, + TS_RAIL_ORDER_POWER_DISPLAY_REQUEST = 0x0016, + TS_RAIL_ORDER_SNAP_ARRANGE = 0x0017, + TS_RAIL_ORDER_GET_APPID_RESP_EX = 0x0018, + TS_RAIL_ORDER_TEXTSCALEINFO = 0x0019, + TS_RAIL_ORDER_CARETBLINKINFO = 0x001A, + TS_RAIL_ORDER_EXEC_RESULT = 0x0080 +} ORDER_TYPE; #ifdef __cplusplus extern "C" diff --git a/include/freerdp/server/ainput.h b/include/freerdp/server/ainput.h new file mode 100644 index 000000000000..200de567a99d --- /dev/null +++ b/include/freerdp/server/ainput.h @@ -0,0 +1,122 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * AInput Virtual Channel Extension + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_AINPUT_SERVER_H +#define FREERDP_CHANNEL_AINPUT_SERVER_H + +#include +#include + +typedef enum AINPUT_SERVER_OPEN_RESULT +{ + AINPUT_SERVER_OPEN_RESULT_OK = 0, + AINPUT_SERVER_OPEN_RESULT_CLOSED = 1, + AINPUT_SERVER_OPEN_RESULT_NOTSUPPORTED = 2, + AINPUT_SERVER_OPEN_RESULT_ERROR = 3 +} AINPUT_SERVER_OPEN_RESULT; + +typedef struct _ainput_server_context ainput_server_context; + +typedef BOOL (*psAInputChannelIdAssigned)(ainput_server_context* context, UINT32 channelId); + +typedef UINT (*psAInputServerInitialize)(ainput_server_context* context, BOOL externalThread); +typedef UINT (*psAInputServerPoll)(ainput_server_context* context); +typedef BOOL (*psAInputServerChannelHandle)(ainput_server_context* context, HANDLE* handle); + +typedef UINT (*psAInputServerOpen)(ainput_server_context* context); +typedef UINT (*psAInputServerClose)(ainput_server_context* context); +typedef BOOL (*psAInputServerIsOpen)(ainput_server_context* context); + +typedef UINT (*psAInputServerOpenResult)(ainput_server_context* context, + AINPUT_SERVER_OPEN_RESULT result); +typedef UINT (*psAInputServerMouseEvent)(ainput_server_context* context, UINT64 timestamp, + UINT64 flags, INT32 x, INT32 y); + +struct _ainput_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* data; + + /*** APIs called by the server. ***/ + /** + * Open the ainput channel. + */ + psAInputServerOpen Open; + + /** + * Optional: Set thread handling. + * When externalThread=TRUE the application is responsible to call + * ainput_server_context_poll periodically to process input events. + * + * Defaults to externalThread=FALSE + */ + psAInputServerInitialize Initialize; + + /** + * @brief Poll When externalThread=TRUE call periodically from your main loop. + * if externalThread=FALSE do not call. + */ + psAInputServerPoll Poll; + + /** + * @brief Poll When externalThread=TRUE call to get a handle to wait for events. + * Will return FALSE until the handle is available. + */ + psAInputServerChannelHandle ChannelHandle; + + /** + * Close the ainput channel. + */ + psAInputServerClose Close; + /** + * Status of the ainput channel. + */ + psAInputServerIsOpen IsOpen; + + /*** Callbacks registered by the server. ***/ + + /** + * Receive ainput mouse event PDU. + */ + psAInputServerMouseEvent MouseEvent; + + rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psAInputChannelIdAssigned ChannelIdAssigned; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API ainput_server_context* ainput_server_context_new(HANDLE vcm); + FREERDP_API void ainput_server_context_free(ainput_server_context* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_AINPUT_SERVER_H */ diff --git a/include/freerdp/server/audin.h b/include/freerdp/server/audin.h index a8da17d27056..f9c581f2a83c 100644 --- a/include/freerdp/server/audin.h +++ b/include/freerdp/server/audin.h @@ -28,6 +28,8 @@ typedef struct _audin_server_context audin_server_context; +typedef BOOL (*psAudinServerChannelIdAssigned)(audin_server_context* context, UINT32 channelId); + typedef UINT (*psAudinServerSelectFormat)(audin_server_context* context, size_t client_format_index); typedef BOOL (*psAudinServerOpen)(audin_server_context* context); @@ -99,6 +101,11 @@ struct _audin_server_context psAudinServerReceiveSamples ReceiveSamples; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psAudinServerChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/disp.h b/include/freerdp/server/disp.h index 43aea647a7db..ab7e09b64d39 100644 --- a/include/freerdp/server/disp.h +++ b/include/freerdp/server/disp.h @@ -29,6 +29,8 @@ typedef struct _disp_server_private DispServerPrivate; typedef struct _disp_server_context DispServerContext; +typedef BOOL (*psDispChannelIdAssigned)(DispServerContext* context, UINT32 channelId); + typedef UINT (*psDispMonitorLayout)(DispServerContext* context, const DISPLAY_CONTROL_MONITOR_LAYOUT_PDU* pdu); typedef UINT (*psDispCaps)(DispServerContext* context); @@ -53,6 +55,11 @@ struct _disp_server_context DispServerPrivate* priv; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psDispChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/echo.h b/include/freerdp/server/echo.h index 0643a5932c99..bbaeb581b8d3 100644 --- a/include/freerdp/server/echo.h +++ b/include/freerdp/server/echo.h @@ -34,6 +34,8 @@ typedef enum ECHO_SERVER_OPEN_RESULT typedef struct _echo_server_context echo_server_context; +typedef BOOL (*psEchoServerChannelIdAssigned)(echo_server_context* context, UINT32 channelId); + typedef UINT (*psEchoServerOpen)(echo_server_context* context); typedef UINT (*psEchoServerClose)(echo_server_context* context); typedef BOOL (*psEchoServerRequest)(echo_server_context* context, const BYTE* buffer, @@ -76,6 +78,11 @@ struct _echo_server_context psEchoServerResponse Response; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psEchoServerChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/gfxredir.h b/include/freerdp/server/gfxredir.h new file mode 100644 index 000000000000..559008037589 --- /dev/null +++ b/include/freerdp/server/gfxredir.h @@ -0,0 +1,93 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_GFXREDIR_SERVER_GFXREDIR_H +#define FREERDP_CHANNEL_GFXREDIR_SERVER_GFXREDIR_H + +#include + +#include +#include +#include + +typedef struct _gfxredir_server_private GfxRedirServerPrivate; +typedef struct _gfxredir_server_context GfxRedirServerContext; + +typedef UINT (*psGfxRedirOpen)(GfxRedirServerContext* context); +typedef UINT (*psGfxRedirClose)(GfxRedirServerContext* context); + +typedef UINT (*psGfxRedirError)(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU *error); + +typedef UINT (*psGfxRedirGraphicsRedirectionLegacyCaps)(GfxRedirServerContext* context, const GFXREDIR_LEGACY_CAPS_PDU *graphicsCaps); + +typedef UINT (*psGfxRedirGraphicsRedirectionCapsAdvertise)(GfxRedirServerContext* context, const GFXREDIR_CAPS_ADVERTISE_PDU *graphicsCapsAdvertise); +typedef UINT (*psGfxRedirGraphicsRedirectionCapsConfirm)(GfxRedirServerContext* context, const GFXREDIR_CAPS_CONFIRM_PDU *graphicsCapsConfirm); + +typedef UINT (*psGfxRedirOpenPool)(GfxRedirServerContext* context, const GFXREDIR_OPEN_POOL_PDU *openPool); +typedef UINT (*psGfxRedirClosePool)(GfxRedirServerContext* context, const GFXREDIR_CLOSE_POOL_PDU *closePool); + +typedef UINT (*psGfxRedirCreateBuffer)(GfxRedirServerContext* context, const GFXREDIR_CREATE_BUFFER_PDU *createBuffer); +typedef UINT (*psGfxRedirDestroyBuffer)(GfxRedirServerContext* context, const GFXREDIR_DESTROY_BUFFER_PDU *destroyBuffer); + +typedef UINT (*psGfxRedirPresentBuffer)(GfxRedirServerContext* context, const GFXREDIR_PRESENT_BUFFER_PDU *presentBuffer); +typedef UINT (*psGfxRedirPresentBufferAck)(GfxRedirServerContext* context, const GFXREDIR_PRESENT_BUFFER_ACK_PDU *presentBufferAck); + +struct _gfxredir_server_context +{ + void* custom; + HANDLE vcm; + + psGfxRedirOpen Open; + psGfxRedirClose Close; + + psGfxRedirError Error; + + psGfxRedirGraphicsRedirectionLegacyCaps GraphicsRedirectionLegacyCaps; + + psGfxRedirGraphicsRedirectionCapsAdvertise GraphicsRedirectionCapsAdvertise; + psGfxRedirGraphicsRedirectionCapsConfirm GraphicsRedirectionCapsConfirm; + + psGfxRedirOpenPool OpenPool; + psGfxRedirClosePool ClosePool; + + psGfxRedirCreateBuffer CreateBuffer; + psGfxRedirDestroyBuffer DestroyBuffer; + + psGfxRedirPresentBuffer PresentBuffer; + psGfxRedirPresentBufferAck PresentBufferAck; + + GfxRedirServerPrivate* priv; + rdpContext* rdpcontext; + + UINT32 confirmedCapsVersion; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm); + FREERDP_API void gfxredir_server_context_free(GfxRedirServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_GFXREDIR_SERVER_GFXREDIR_H */ diff --git a/include/freerdp/server/rdpecam-enumerator.h b/include/freerdp/server/rdpecam-enumerator.h new file mode 100644 index 000000000000..3f1b77069bce --- /dev/null +++ b/include/freerdp/server/rdpecam-enumerator.h @@ -0,0 +1,134 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H +#define FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H + +#include +#include + +typedef struct _cam_dev_enum_server_context CamDevEnumServerContext; + +typedef UINT (*psCamDevEnumServerServerOpen)(CamDevEnumServerContext* context); +typedef UINT (*psCamDevEnumServerServerClose)(CamDevEnumServerContext* context); + +typedef BOOL (*psCamDevEnumServerServerChannelIdAssigned)(CamDevEnumServerContext* context, + UINT32 channelId); + +typedef UINT (*psCamDevEnumServerServerInitialize)(CamDevEnumServerContext* context, + BOOL externalThread); +typedef UINT (*psCamDevEnumServerServerPoll)(CamDevEnumServerContext* context); +typedef BOOL (*psCamDevEnumServerServerChannelHandle)(CamDevEnumServerContext* context, + HANDLE* handle); + +typedef UINT (*psCamDevEnumServerServerSelectVersionRequest)( + CamDevEnumServerContext* context, const CAM_SELECT_VERSION_REQUEST* selectVersionRequest); +typedef UINT (*psCamDevEnumServerServerSelectVersionResponse)( + CamDevEnumServerContext* context, const CAM_SELECT_VERSION_RESPONSE* selectVersionResponse); + +typedef UINT (*psCamDevEnumServerServerDeviceAddedNotification)( + CamDevEnumServerContext* context, const CAM_DEVICE_ADDED_NOTIFICATION* deviceAddedNotification); +typedef UINT (*psCamDevEnumServerServerDeviceRemovedNotification)( + CamDevEnumServerContext* context, + const CAM_DEVICE_REMOVED_NOTIFICATION* deviceRemovedNotification); + +struct _cam_dev_enum_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* userdata; + + /*** APIs called by the server. ***/ + + /** + * Optional: Set thread handling. + * When externalThread=TRUE, the application is responsible to call + * Poll() periodically to process channel events. + * + * Defaults to externalThread=FALSE + */ + psCamDevEnumServerServerInitialize Initialize; + + /** + * Open the camera device enumerator channel. + */ + psCamDevEnumServerServerOpen Open; + + /** + * Close the camera device enumerator channel. + */ + psCamDevEnumServerServerClose Close; + + /** + * Poll + * When externalThread=TRUE, call Poll() periodically from your main loop. + * If externalThread=FALSE do not call. + */ + psCamDevEnumServerServerPoll Poll; + + /** + * Retrieve the channel handle for use in conjunction with Poll(). + * If externalThread=FALSE do not call. + */ + psCamDevEnumServerServerChannelHandle ChannelHandle; + + /* + * Send a Select Version Response PDU. + */ + psCamDevEnumServerServerSelectVersionResponse SelectVersionResponse; + + /*** Callbacks registered by the server. ***/ + + /** + * Callback, when the channel got its id assigned. + */ + psCamDevEnumServerServerChannelIdAssigned ChannelIdAssigned; + + /** + * Callback for the Select Version Request PDU. + */ + psCamDevEnumServerServerSelectVersionRequest SelectVersionRequest; + + /** + * Callback for the Device Added Notification PDU. + */ + psCamDevEnumServerServerDeviceAddedNotification DeviceAddedNotification; + + /** + * Callback for the Device Removed Notification PDU. + */ + psCamDevEnumServerServerDeviceRemovedNotification DeviceRemovedNotification; + + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API CamDevEnumServerContext* cam_dev_enum_server_context_new(HANDLE vcm); + FREERDP_API void cam_dev_enum_server_context_free(CamDevEnumServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_CAM_DEV_ENUM_SERVER_CAM_DEV_ENUM_H */ diff --git a/include/freerdp/server/rdpecam.h b/include/freerdp/server/rdpecam.h new file mode 100644 index 000000000000..a28ff87405d0 --- /dev/null +++ b/include/freerdp/server/rdpecam.h @@ -0,0 +1,278 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Video Capture Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H +#define FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H + +#include +#include + +typedef struct camera_device_server_context CameraDeviceServerContext; + +typedef UINT (*psCameraDeviceServerOpen)(CameraDeviceServerContext* context); +typedef UINT (*psCameraDeviceServerClose)(CameraDeviceServerContext* context); + +typedef BOOL (*psCameraDeviceServerChannelIdAssigned)(CameraDeviceServerContext* context, + UINT32 channelId); + +typedef UINT (*psCameraDeviceServerInitialize)(CameraDeviceServerContext* context, + BOOL externalThread); +typedef UINT (*psCameraDeviceServerPoll)(CameraDeviceServerContext* context); +typedef BOOL (*psCameraDeviceServerChannelHandle)(CameraDeviceServerContext* context, + HANDLE* handle); + +typedef UINT (*psCameraDeviceServerSuccessResponse)(CameraDeviceServerContext* context, + const CAM_SUCCESS_RESPONSE* successResponse); +typedef UINT (*psCameraDeviceServerErrorResponse)(CameraDeviceServerContext* context, + const CAM_ERROR_RESPONSE* errorResponse); + +typedef UINT (*psCameraDeviceServerActivateDeviceRequest)( + CameraDeviceServerContext* context, const CAM_ACTIVATE_DEVICE_REQUEST* activateDeviceRequest); +typedef UINT (*psCameraDeviceServerDeactivateDeviceRequest)( + CameraDeviceServerContext* context, + const CAM_DEACTIVATE_DEVICE_REQUEST* deactivateDeviceRequest); + +typedef UINT (*psCameraDeviceServerStreamListRequest)( + CameraDeviceServerContext* context, const CAM_STREAM_LIST_REQUEST* streamListRequest); +typedef UINT (*psCameraDeviceServerStreamListResponse)( + CameraDeviceServerContext* context, const CAM_STREAM_LIST_RESPONSE* streamListResponse); + +typedef UINT (*psCameraDeviceServerMediaTypeListRequest)( + CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_REQUEST* mediaTypeListRequest); +typedef UINT (*psCameraDeviceServerMediaTypeListResponse)( + CameraDeviceServerContext* context, const CAM_MEDIA_TYPE_LIST_RESPONSE* mediaTypeListResponse); + +typedef UINT (*psCameraDeviceServerCurrentMediaTypeRequest)( + CameraDeviceServerContext* context, + const CAM_CURRENT_MEDIA_TYPE_REQUEST* currentMediaTypeRequest); +typedef UINT (*psCameraDeviceServerCurrentMediaTypeResponse)( + CameraDeviceServerContext* context, + const CAM_CURRENT_MEDIA_TYPE_RESPONSE* currentMediaTypeResponse); + +typedef UINT (*psCameraDeviceServerStartStreamsRequest)( + CameraDeviceServerContext* context, const CAM_START_STREAMS_REQUEST* startStreamsRequest); +typedef UINT (*psCameraDeviceServerStopStreamsRequest)( + CameraDeviceServerContext* context, const CAM_STOP_STREAMS_REQUEST* stopStreamsRequest); + +typedef UINT (*psCameraDeviceServerSampleRequest)(CameraDeviceServerContext* context, + const CAM_SAMPLE_REQUEST* sampleRequest); +typedef UINT (*psCameraDeviceServerSampleResponse)(CameraDeviceServerContext* context, + const CAM_SAMPLE_RESPONSE* sampleResponse); +typedef UINT (*psCameraDeviceServerSampleErrorResponse)( + CameraDeviceServerContext* context, const CAM_SAMPLE_ERROR_RESPONSE* sampleErrorResponse); + +typedef UINT (*psCameraDeviceServerPropertyListRequest)( + CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_REQUEST* propertyListRequest); +typedef UINT (*psCameraDeviceServerPropertyListResponse)( + CameraDeviceServerContext* context, const CAM_PROPERTY_LIST_RESPONSE* propertyListResponse); + +typedef UINT (*psCameraDeviceServerPropertyValueRequest)( + CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_REQUEST* propertyValueRequest); +typedef UINT (*psCameraDeviceServerPropertyValueResponse)( + CameraDeviceServerContext* context, const CAM_PROPERTY_VALUE_RESPONSE* propertyValueResponse); + +typedef UINT (*psCameraDeviceServerSetPropertyValueRequest)( + CameraDeviceServerContext* context, + const CAM_SET_PROPERTY_VALUE_REQUEST* setPropertyValueRequest); + +struct camera_device_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* userdata; + + /** + * Name of the virtual channel. Pointer owned by the CameraDeviceServerContext, + * meaning camera_device_server_context_free() takes care of freeing the pointer. + * + * Server implementations should sanitize the virtual channel name for invalid + * names, like names for other known channels + * ("ECHO", "AUDIO_PLAYBACK_DVC", etc.) + */ + char* virtualChannelName; + + /** + * Protocol version to be used. Every sent server to client PDU has the + * version value in the Header set to the following value. + */ + BYTE protocolVersion; + + /*** APIs called by the server. ***/ + + /** + * Optional: Set thread handling. + * When externalThread=TRUE, the application is responsible to call + * Poll() periodically to process channel events. + * + * Defaults to externalThread=FALSE + */ + psCameraDeviceServerInitialize Initialize; + + /** + * Open the camera device channel. + */ + psCameraDeviceServerOpen Open; + + /** + * Close the camera device channel. + */ + psCameraDeviceServerClose Close; + + /** + * Poll + * When externalThread=TRUE, call Poll() periodically from your main loop. + * If externalThread=FALSE do not call. + */ + psCameraDeviceServerPoll Poll; + + /** + * Retrieve the channel handle for use in conjunction with Poll(). + * If externalThread=FALSE do not call. + */ + psCameraDeviceServerChannelHandle ChannelHandle; + + /** + * For the following server to client PDUs, + * the message header does not have to be set. + */ + + /** + * Send a Activate Device Request PDU. + */ + psCameraDeviceServerActivateDeviceRequest ActivateDeviceRequest; + + /** + * Send a Deactivate Device Request PDU. + */ + psCameraDeviceServerDeactivateDeviceRequest DeactivateDeviceRequest; + + /** + * Send a Stream List Request PDU. + */ + psCameraDeviceServerStreamListRequest StreamListRequest; + + /** + * Send a Media Type List Request PDU. + */ + psCameraDeviceServerMediaTypeListRequest MediaTypeListRequest; + + /** + * Send a Current Media Type Request PDU. + */ + psCameraDeviceServerCurrentMediaTypeRequest CurrentMediaTypeRequest; + + /** + * Send a Start Streams Request PDU. + */ + psCameraDeviceServerStartStreamsRequest StartStreamsRequest; + + /** + * Send a Stop Streams Request PDU. + */ + psCameraDeviceServerStopStreamsRequest StopStreamsRequest; + + /** + * Send a Sample Request PDU. + */ + psCameraDeviceServerSampleRequest SampleRequest; + + /** + * Send a Property List Request PDU. + */ + psCameraDeviceServerPropertyListRequest PropertyListRequest; + + /** + * Send a Property Value Request PDU. + */ + psCameraDeviceServerPropertyValueRequest PropertyValueRequest; + + /** + * Send a Set Property Value Request PDU. + */ + psCameraDeviceServerSetPropertyValueRequest SetPropertyValueRequest; + + /*** Callbacks registered by the server. ***/ + + /** + * Callback, when the channel got its id assigned. + */ + psCameraDeviceServerChannelIdAssigned ChannelIdAssigned; + + /** + * Callback for the Success Response PDU. + */ + psCameraDeviceServerSuccessResponse SuccessResponse; + + /** + * Callback for the Error Response PDU. + */ + psCameraDeviceServerErrorResponse ErrorResponse; + + /** + * Callback for the Stream List Response PDU. + */ + psCameraDeviceServerStreamListResponse StreamListResponse; + + /** + * Callback for the Media Type List Response PDU. + */ + psCameraDeviceServerMediaTypeListResponse MediaTypeListResponse; + + /** + * Callback for the Current Media Type Response PDU. + */ + psCameraDeviceServerCurrentMediaTypeResponse CurrentMediaTypeResponse; + + /** + * Callback for the Sample Response PDU. + */ + psCameraDeviceServerSampleResponse SampleResponse; + + /** + * Callback for the Sample Error Response PDU. + */ + psCameraDeviceServerSampleErrorResponse SampleErrorResponse; + + /** + * Callback for the Property List Response PDU. + */ + psCameraDeviceServerPropertyListResponse PropertyListResponse; + + /** + * Callback for the Property Value Response PDU. + */ + psCameraDeviceServerPropertyValueResponse PropertyValueResponse; + + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API CameraDeviceServerContext* camera_device_server_context_new(HANDLE vcm); + FREERDP_API void camera_device_server_context_free(CameraDeviceServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_CAMERA_DEVICE_SERVER_CAMERA_DEVICE_H */ diff --git a/include/freerdp/server/rdpei.h b/include/freerdp/server/rdpei.h index b784e78c2661..01b13937d64f 100644 --- a/include/freerdp/server/rdpei.h +++ b/include/freerdp/server/rdpei.h @@ -47,6 +47,11 @@ struct _rdpei_server_context void* user_data; /* user data, useful for callbacks */ UINT (*onPenEvent)(RdpeiServerContext* context, const RDPINPUT_PEN_EVENT* penEvent); + + /** + * Callback, when the channel got its id assigned. + */ + BOOL (*onChannelIdAssigned)(RdpeiServerContext* context, UINT32 channelId); }; #ifdef __cplusplus diff --git a/include/freerdp/server/rdpgfx.h b/include/freerdp/server/rdpgfx.h index 9e52ca1de7cb..aee9de9379e1 100644 --- a/include/freerdp/server/rdpgfx.h +++ b/include/freerdp/server/rdpgfx.h @@ -29,6 +29,8 @@ typedef struct _rdpgfx_server_private RdpgfxServerPrivate; typedef BOOL (*psRdpgfxServerOpen)(RdpgfxServerContext* context); typedef BOOL (*psRdpgfxServerClose)(RdpgfxServerContext* context); +typedef BOOL (*psRdpgfxServerChannelIdAssigned)(RdpgfxServerContext* context, UINT32 channelId); + typedef UINT (*psRdpgfxResetGraphics)(RdpgfxServerContext* context, const RDPGFX_RESET_GRAPHICS_PDU* resetGraphics); typedef UINT (*psRdpgfxStartFrame)(RdpgfxServerContext* context, @@ -112,6 +114,11 @@ struct _rdpgfx_server_context RdpgfxServerPrivate* priv; rdpContext* rdpcontext; + + /** + * Callback, when the channel got its id assigned. + */ + psRdpgfxServerChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/rdpsnd.h b/include/freerdp/server/rdpsnd.h index fa83473596b4..a8cb510ce2f1 100644 --- a/include/freerdp/server/rdpsnd.h +++ b/include/freerdp/server/rdpsnd.h @@ -32,11 +32,21 @@ typedef struct _rdpsnd_server_private RdpsndServerPrivate; typedef UINT (*psRdpsndStart)(RdpsndServerContext* context); typedef UINT (*psRdpsndStop)(RdpsndServerContext* context); +typedef BOOL (*psRdpsndChannelIdAssigned)(RdpsndServerContext* context, UINT32 channelId); + typedef UINT (*psRdpsndServerInitialize)(RdpsndServerContext* context, BOOL ownThread); +typedef UINT (*psRdpsndServerSendFormats)(RdpsndServerContext* context); typedef UINT (*psRdpsndServerSelectFormat)(RdpsndServerContext* context, UINT16 client_format_index); +typedef UINT (*psRdpsndServerTraining)(RdpsndServerContext* context, UINT16 timestamp, + UINT16 packsize, BYTE* data); +typedef UINT (*psRdpsndServerTrainingConfirm)(RdpsndServerContext* context, UINT16 timestamp, + UINT16 packsize); typedef UINT (*psRdpsndServerSendSamples)(RdpsndServerContext* context, const void* buf, int nframes, UINT16 wTimestamp); +typedef UINT (*psRdpsndServerSendSamples2)(RdpsndServerContext* context, UINT16 formatNo, + const void* buf, size_t size, UINT16 timestamp, + UINT32 audioTimeStamp); typedef UINT (*psRdpsndServerConfirmBlock)(RdpsndServerContext* context, BYTE confirmBlockNum, UINT16 wtimestamp); typedef UINT (*psRdpsndServerSetVolume)(RdpsndServerContext* context, int left, int right); @@ -119,6 +129,46 @@ struct _rdpsnd_server_context UINT16 clientVersion; rdpContext* rdpcontext; + + /* Server to request to use dynamic virtual channel. */ + BOOL use_dynamic_virtual_channel; + + /* dwFlags in CLIENT_AUDIO_VERSION_AND_FORMATS */ + UINT32 capsFlags; + /* dwVolume in CLIENT_AUDIO_VERSION_AND_FORMATS */ + UINT32 initialVolume; + /* dwPitch in CLIENT_AUDIO_VERSION_AND_FORMATS */ + UINT32 initialPitch; + + UINT16 qualityMode; + + /** + * Send server formats and version to the client. Automatically sent, when + * opening the channel. + * Also used to restart the protocol after sending the Close PDU. + */ + psRdpsndServerSendFormats SendFormats; + /** + * Send Training PDU. + */ + psRdpsndServerTraining Training; + + /** + * Send encoded audio samples using a Wave2 PDU. + * When successful, the block_no member is incremented. + */ + psRdpsndServerSendSamples2 SendSamples2; + + /** + * Called when a TrainingConfirm PDU is received from the client. + */ + psRdpsndServerTrainingConfirm TrainingConfirm; + + /** + * Callback, when the channel got its id assigned. + * Only called, when use_dynamic_virtual_channel=TRUE. + */ + psRdpsndChannelIdAssigned ChannelIdAssigned; }; #ifdef __cplusplus diff --git a/include/freerdp/server/telemetry.h b/include/freerdp/server/telemetry.h new file mode 100644 index 000000000000..04f72ad2a6e1 --- /dev/null +++ b/include/freerdp/server/telemetry.h @@ -0,0 +1,108 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Telemetry Virtual Channel Extension + * + * Copyright 2022 Pascal Nowack + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H +#define FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H + +#include +#include + +typedef struct _telemetry_server_context TelemetryServerContext; + +typedef UINT (*psTelemetryServerOpen)(TelemetryServerContext* context); +typedef UINT (*psTelemetryServerClose)(TelemetryServerContext* context); + +typedef BOOL (*psTelemetryServerChannelIdAssigned)(TelemetryServerContext* context, + UINT32 channelId); + +typedef UINT (*psTelemetryServerInitialize)(TelemetryServerContext* context, BOOL externalThread); +typedef UINT (*psTelemetryServerPoll)(TelemetryServerContext* context); +typedef BOOL (*psTelemetryServerChannelHandle)(TelemetryServerContext* context, HANDLE* handle); + +typedef UINT (*psTelemetryServerRdpTelemetry)(TelemetryServerContext* context, + const TELEMETRY_RDP_TELEMETRY_PDU* rdpTelemetry); + +struct _telemetry_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* userdata; + + /*** APIs called by the server. ***/ + + /** + * Optional: Set thread handling. + * When externalThread=TRUE, the application is responsible to call + * Poll() periodically to process channel events. + * + * Defaults to externalThread=FALSE + */ + psTelemetryServerInitialize Initialize; + + /** + * Open the telemetry channel. + */ + psTelemetryServerOpen Open; + + /** + * Close the telemetry channel. + */ + psTelemetryServerClose Close; + + /** + * Poll + * When externalThread=TRUE, call Poll() periodically from your main loop. + * If externalThread=FALSE do not call. + */ + psTelemetryServerPoll Poll; + + /** + * Retrieve the channel handle for use in conjunction with Poll(). + * If externalThread=FALSE do not call. + */ + psTelemetryServerChannelHandle ChannelHandle; + + /*** Callbacks registered by the server. ***/ + + /** + * Callback, when the channel got its id assigned + */ + psTelemetryServerChannelIdAssigned ChannelIdAssigned; + /** + * Callback for the RDP Telemetry PDU. + */ + psTelemetryServerRdpTelemetry RdpTelemetry; + + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API TelemetryServerContext* telemetry_server_context_new(HANDLE vcm); + FREERDP_API void telemetry_server_context_free(TelemetryServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_TELEMETRY_SERVER_TELEMETRY_H */ diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 26adb624e292..76f1951993eb 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -251,6 +251,9 @@ typedef enum #define LB_TARGET_NET_ADDRESSES 0x00000800 #define LB_CLIENT_TSV_URL 0x00001000 #define LB_SERVER_TSV_CAPABLE 0x00002000 +#define LB_PASSWORD_IS_PK_ENCRYPTED 0x00004000 +#define LB_REDIRECTION_GUID 0x00008000 +#define LB_TARGET_CERTIFICATE 0x00010000 #define LB_PASSWORD_MAX_LENGTH 512 @@ -656,6 +659,8 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_NtlmSamFile (1103) #define FreeRDP_FIPSMode (1104) #define FreeRDP_TlsSecLevel (1105) +#define FreeRDP_TLSMinVersion (1106) +#define FreeRDP_TLSMaxVersion (1107) #define FreeRDP_MstscCookieMode (1152) #define FreeRDP_CookieMaxLength (1153) #define FreeRDP_PreconnectionId (1154) @@ -679,6 +684,10 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_RedirectionAcceptedCert (1231) #define FreeRDP_RedirectionAcceptedCertLength (1232) #define FreeRDP_RedirectionPreferType (1233) +#define FreeRDP_RedirectionGuid (1234) +#define FreeRDP_RedirectionGuidLength (1235) +#define FreeRDP_RedirectionTargetCertificate (1236) +#define FreeRDP_RedirectionTargetCertificateLength (1237) #define FreeRDP_Password51 (1280) #define FreeRDP_Password51Length (1281) #define FreeRDP_SmartcardLogon (1282) @@ -721,6 +730,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_PercentScreenUseWidth (1556) #define FreeRDP_PercentScreenUseHeight (1557) #define FreeRDP_DynamicResolutionUpdate (1558) +#define FreeRDP_GrabMouse (1559) #define FreeRDP_SoftwareGdi (1601) #define FreeRDP_LocalConnection (1602) #define FreeRDP_AuthenticationOnly (1603) @@ -894,6 +904,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_TcpKeepAliveDelay (5192) #define FreeRDP_TcpKeepAliveInterval (5193) #define FreeRDP_TcpAckTimeout (5194) +#define FreeRDP_TcpConnectTimeout (5197) /** * FreeRDP Settings Data Structure @@ -1107,7 +1118,9 @@ struct rdp_settings ALIGN64 char* NtlmSamFile; /* 1103 */ ALIGN64 BOOL FIPSMode; /* 1104 */ ALIGN64 UINT32 TlsSecLevel; /* 1105 */ - UINT64 padding1152[1152 - 1106]; /* 1106 */ + ALIGN64 UINT16 TLSMinVersion; /* 1106 */ + ALIGN64 UINT16 TLSMaxVersion; /* 1107 */ + UINT64 padding1152[1152 - 1108]; /* 1108 */ /* Connection Cookie */ ALIGN64 BOOL MstscCookieMode; /* 1152 */ @@ -1136,7 +1149,11 @@ struct rdp_settings ALIGN64 char* RedirectionAcceptedCert; /* 1231 */ ALIGN64 UINT32 RedirectionAcceptedCertLength; /* 1232 */ ALIGN64 UINT32 RedirectionPreferType; /* 1233 */ - UINT64 padding1280[1280 - 1234]; /* 1234 */ + ALIGN64 BYTE* RedirectionGuid; /* 1234 */ + ALIGN64 UINT32 RedirectionGuidLength; /* 1235 */ + ALIGN64 BYTE* RedirectionTargetCertificate; /* 1236 */ + ALIGN64 UINT32 RedirectionTargetCertificateLength; /* 1237 */ + UINT64 padding1280[1280 - 1238]; /* 1238 */ /** * Security @@ -1200,7 +1217,8 @@ struct rdp_settings ALIGN64 BOOL PercentScreenUseWidth; /* 1556 */ ALIGN64 BOOL PercentScreenUseHeight; /* 1557 */ ALIGN64 BOOL DynamicResolutionUpdate; /* 1558 */ - UINT64 padding1601[1601 - 1559]; /* 1559 */ + ALIGN64 BOOL GrabMouse; /* 1559 */ + UINT64 padding1601[1601 - 1560]; /* 1560 */ /* Miscellaneous */ ALIGN64 BOOL SoftwareGdi; /* 1601 */ @@ -1342,20 +1360,20 @@ struct rdp_settings /* Input Capabilities */ ALIGN64 char* KeyboardRemappingList; /* 2622 */ - ALIGN64 UINT32 KeyboardCodePage; /* 2623 */ - ALIGN64 UINT32 KeyboardLayout; /* 2624 */ - ALIGN64 UINT32 KeyboardType; /* 2625 */ - ALIGN64 UINT32 KeyboardSubType; /* 2626 */ - ALIGN64 UINT32 KeyboardFunctionKey; /* 2627 */ - ALIGN64 char* ImeFileName; /* 2628 */ - ALIGN64 BOOL UnicodeInput; /* 2629 */ - ALIGN64 BOOL FastPathInput; /* 2630 */ - ALIGN64 BOOL MultiTouchInput; /* 2631 */ - ALIGN64 BOOL MultiTouchGestures; /* 2632 */ - ALIGN64 UINT32 KeyboardHook; /* 2633 */ - ALIGN64 BOOL HasHorizontalWheel; /* 2634 */ - ALIGN64 BOOL HasExtendedMouseEvent; /* 2635 */ - UINT64 padding2688[2688 - 2636]; /* 2636 */ + ALIGN64 UINT32 KeyboardCodePage; /* 2623 */ + ALIGN64 UINT32 KeyboardLayout; /* 2624 */ + ALIGN64 UINT32 KeyboardType; /* 2625 */ + ALIGN64 UINT32 KeyboardSubType; /* 2626 */ + ALIGN64 UINT32 KeyboardFunctionKey; /* 2627 */ + ALIGN64 char* ImeFileName; /* 2628 */ + ALIGN64 BOOL UnicodeInput; /* 2629 */ + ALIGN64 BOOL FastPathInput; /* 2630 */ + ALIGN64 BOOL MultiTouchInput; /* 2631 */ + ALIGN64 BOOL MultiTouchGestures; /* 2632 */ + ALIGN64 UINT32 KeyboardHook; /* 2633 */ + ALIGN64 BOOL HasHorizontalWheel; /* 2634 */ + ALIGN64 BOOL HasExtendedMouseEvent; /* 2635 */ + UINT64 padding2688[2688 - 2636]; /* 2636 */ /* Brush Capabilities */ ALIGN64 UINT32 BrushSupportLevel; /* 2688 */ @@ -1545,7 +1563,9 @@ struct rdp_settings ALIGN64 UINT32 TcpKeepAliveDelay; /* 5192 */ ALIGN64 UINT32 TcpKeepAliveInterval; /* 5193 */ ALIGN64 UINT32 TcpAckTimeout; /* 5194 */ - UINT64 padding5312[5312 - 5195]; /* 5195 */ + UINT64 padding5197[5197 - 5195]; /* 5195 */ + ALIGN64 UINT32 TcpConnectTimeout; /* 5197 */ + UINT64 padding5312[5312 - 5198]; /* 5198 */ /** * WARNING: End of ABI stable zone! @@ -1628,6 +1648,8 @@ extern "C" FREERDP_API void freerdp_dynamic_channel_collection_free(rdpSettings* settings); FREERDP_API void freerdp_target_net_addresses_free(rdpSettings* settings); + FREERDP_API BOOL freerdp_target_net_addresses_copy(rdpSettings* settings, char** addresses, + UINT32 count); FREERDP_API void freerdp_performance_flags_make(rdpSettings* settings); FREERDP_API void freerdp_performance_flags_split(rdpSettings* settings); @@ -1702,6 +1724,9 @@ extern "C" FREERDP_API SSIZE_T freerdp_settings_get_type_for_key(size_t key); FREERDP_API const char* freerdp_settings_get_name_for_key(size_t key); + FREERDP_API char* freerdp_rail_support_flags_to_string(UINT32 flags, char* buffer, + size_t length); + #ifdef __cplusplus } #endif diff --git a/include/freerdp/types.h b/include/freerdp/types.h index 165c1717e00b..32b4fbfd209f 100644 --- a/include/freerdp/types.h +++ b/include/freerdp/types.h @@ -74,6 +74,15 @@ struct _RECTANGLE_16 }; typedef struct _RECTANGLE_16 RECTANGLE_16; +struct _RECTANGLE_32 +{ + UINT32 left; + UINT32 top; + UINT32 width; + UINT32 height; +}; +typedef struct _RECTANGLE_32 RECTANGLE_32; + /* Plugin events */ #include diff --git a/include/freerdp/update.h b/include/freerdp/update.h index d2797a28e320..71cb15580e72 100644 --- a/include/freerdp/update.h +++ b/include/freerdp/update.h @@ -263,6 +263,7 @@ struct rdp_update * fills BITMAP_DATA struct members: flags, cbCompMainBodySize and cbCompFirstRowSize. */ BOOL autoCalculateBitmapData; + size_t offsetOrders; /* the offset to patch numberOrders in the stream */ }; #endif /* FREERDP_UPDATE_H */ diff --git a/include/freerdp/utils/cliprdr_utils.h b/include/freerdp/utils/cliprdr_utils.h new file mode 100644 index 000000000000..59ecf848fedf --- /dev/null +++ b/include/freerdp/utils/cliprdr_utils.h @@ -0,0 +1,48 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPDR utility functions + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_UTILS_CLIPRDR_H +#define FREERDP_UTILS_CLIPRDR_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, + FILEDESCRIPTORW** file_descriptor_array, + UINT32* file_descriptor_count); + FREERDP_API UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length); + FREERDP_API UINT cliprdr_serialize_file_list_ex(UINT32 flags, + const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, + BYTE** format_data, UINT32* format_data_length); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/freerdp/utils/string.h b/include/freerdp/utils/string.h new file mode 100644 index 000000000000..805930fb222e --- /dev/null +++ b/include/freerdp/utils/string.h @@ -0,0 +1,39 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * String Utils - Helper functions converting something to string + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_UTILS_STRING_H +#define FREERDP_UTILS_STRING_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API char* rdp_redirection_flags_to_string(UINT32 flags, char* buffer, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_UTILS_STRING_H */ diff --git a/libfreerdp/CMakeLists.txt b/libfreerdp/CMakeLists.txt index e3a410316be5..a259b68cc304 100644 --- a/libfreerdp/CMakeLists.txt +++ b/libfreerdp/CMakeLists.txt @@ -211,7 +211,12 @@ if(FAAC_FOUND) endif() if(WITH_NEON) - set_source_files_properties(${CODEC_NEON_SRCS} PROPERTIES COMPILE_FLAGS "-mfpu=neon -Wno-unused-variable" ) + check_symbol_exists("_M_AMD64" "" MSVC_ARM64) + check_symbol_exists("__aarch64__" "" ARCH_ARM64) + + if (NOT MSVC_ARM64 AND NOT ARCH_ARM64) + set_source_files_properties(${CODEC_NEON_SRCS} PROPERTIES COMPILE_FLAGS "-mfpu=neon" ) + endif() set(CODEC_SRCS ${CODEC_SRCS} ${CODEC_NEON_SRCS}) endif() @@ -220,12 +225,6 @@ if(WITH_JPEG) freerdp_library_add(${JPEG_LIBRARIES}) endif() -if(WITH_X264) - set(CODEC_SRCS ${CODEC_SRCS} codec/h264_x264.c) - freerdp_include_directory_add(${X264_INCLUDE_DIR}) - freerdp_library_add(${X264_LIBRARIES}) -endif() - if(WITH_OPENH264) set(CODEC_SRCS ${CODEC_SRCS} codec/h264_openh264.c) freerdp_include_directory_add(${OPENH264_INCLUDE_DIR}) @@ -244,6 +243,10 @@ if(WIN32 AND WITH_MEDIA_FOUNDATION) set(CODEC_SRCS ${CODEC_SRCS} codec/h264_mf.c) endif() +if(ANDROID AND WITH_MEDIACODEC) + list(APPEND CODEC_SRCS codec/h264_mediacodec.c) +endif() + freerdp_module_add(${CODEC_SRCS}) if(BUILD_TESTING) @@ -310,7 +313,7 @@ freerdp_definition_add(-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}) ### IPP Variable debugging if(WITH_IPP) - if(CMAKE_COMPILER_IS_GNUCC) + if(CMAKE_COMPILER_IS_GNUCC OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") foreach(INCLDIR ${IPP_INCLUDE_DIRS}) set(OPTIMIZATION "${OPTIMIZATION} -I${INCLDIR}") endforeach(INCLDIR) @@ -332,9 +335,11 @@ if(WITH_SSE2) PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} /arch:SSE2") endif() elseif(WITH_NEON) - if(CMAKE_COMPILER_IS_GNUCC) - set_source_files_properties(${PRIMITIVES_OPT_SRCS} - PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} -mfpu=neon") + if(CMAKE_COMPILER_IS_GNUCC OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") + if (NOT MSVC_ARM64 AND NOT ARCH_ARM64) + set_source_files_properties(${PRIMITIVES_OPT_SRCS} + PROPERTIES COMPILE_FLAGS "${OPTIMIZATION} -mfpu=neon") + endif() endif() # TODO: Add MSVC equivalent endif() diff --git a/libfreerdp/cache/bitmap.c b/libfreerdp/cache/bitmap.c index 0ce2599b3239..2fd324b6d5d6 100644 --- a/libfreerdp/cache/bitmap.c +++ b/libfreerdp/cache/bitmap.c @@ -281,8 +281,9 @@ rdpBitmapCache* bitmap_cache_new(rdpSettings* settings) bitmapCache->settings = settings; bitmapCache->update = ((freerdp*)settings->instance)->update; bitmapCache->context = bitmapCache->update->context; - bitmapCache->cells = - (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL)); + /* overallocate by 1. older RDP servers do send a off by 1 cache index. */ + bitmapCache->cells = (BITMAP_V2_CELL*)calloc( + (size_t)settings->BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL)); if (!bitmapCache->cells) goto fail; diff --git a/libfreerdp/cache/pointer.c b/libfreerdp/cache/pointer.c index 010df17031e8..42acf65da58b 100644 --- a/libfreerdp/cache/pointer.c +++ b/libfreerdp/cache/pointer.c @@ -68,6 +68,9 @@ static BOOL update_pointer_position(rdpContext* context, !pointer_position) return FALSE; + if (!context->settings->GrabMouse) + return TRUE; + pointer = context->graphics->Pointer_Prototype; return IFCALLRESULT(TRUE, pointer->SetPosition, context, pointer_position->xPos, pointer_position->yPos); diff --git a/libfreerdp/codec/clear.c b/libfreerdp/codec/clear.c index fadd98e6738a..f110fda65461 100644 --- a/libfreerdp/codec/clear.c +++ b/libfreerdp/codec/clear.c @@ -410,7 +410,7 @@ static BOOL clear_decompress_residual_data(CLEAR_CONTEXT* clear, wStream* s, } } - if ((pixelIndex + runLengthFactor) > pixelCount) + if ((pixelIndex >= pixelCount) || (runLengthFactor > (pixelCount - pixelIndex))) { WLog_ERR(TAG, "pixelIndex %" PRIu32 " + runLengthFactor %" PRIu32 " > pixelCount %" PRIu32 @@ -492,6 +492,18 @@ static BOOL clear_decompress_subcodecs_data(CLEAR_CONTEXT* clear, wStream* s, nXDstRel = nXDst + xStart; nYDstRel = nYDst + yStart; + if (1ull * nXDstRel + width > nDstWidth) + { + WLog_ERR(TAG, "nXDstRel %" PRIu32 " + width %" PRIu16 " > nDstWidth %" PRIu32 "", + nXDstRel, width, nDstWidth); + return FALSE; + } + if (1ull * nYDstRel + height > nDstHeight) + { + WLog_ERR(TAG, "nYDstRel %" PRIu32 " + height %" PRIu16 " > nDstHeight %" PRIu32 "", + nYDstRel, height, nDstHeight); + return FALSE; + } if (width > nWidth) { @@ -512,12 +524,12 @@ static BOOL clear_decompress_subcodecs_data(CLEAR_CONTEXT* clear, wStream* s, { case 0: /* Uncompressed */ { - UINT32 nSrcStep = width * GetBytesPerPixel(PIXEL_FORMAT_BGR24); - UINT32 nSrcSize = nSrcStep * height; + const UINT32 nSrcStep = width * GetBytesPerPixel(PIXEL_FORMAT_BGR24); + const size_t nSrcSize = 1ull * nSrcStep * height; if (bitmapDataByteCount != nSrcSize) { - WLog_ERR(TAG, "bitmapDataByteCount %" PRIu32 " != nSrcSize %" PRIu32 "", + WLog_ERR(TAG, "bitmapDataByteCount %" PRIu32 " != nSrcSize %" PRIuz "", bitmapDataByteCount, nSrcSize); return FALSE; } @@ -566,7 +578,7 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry) const UINT32 diffSize = (vBarEntry->count - vBarEntry->size) * bpp; BYTE* tmp; vBarEntry->size = vBarEntry->count; - tmp = (BYTE*)realloc(vBarEntry->pixels, vBarEntry->count * bpp * 1ULL); + tmp = (BYTE*)realloc(vBarEntry->pixels, 1ull * vBarEntry->count * bpp); if (!tmp) { @@ -591,13 +603,9 @@ static BOOL resize_vbar_entry(CLEAR_CONTEXT* clear, CLEAR_VBAR_ENTRY* vBarEntry) static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 bandsByteCount, UINT32 nWidth, UINT32 nHeight, BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep, UINT32 nXDst, - UINT32 nYDst) + UINT32 nYDst, UINT32 nDstWidth, UINT32 nDstHeight) { - UINT32 i, y; - UINT32 count; - UINT32 suboffset; - UINT32 nXDstRel; - UINT32 nYDstRel; + UINT32 suboffset = 0; if (Stream_GetRemainingLength(s) < bandsByteCount) { @@ -605,22 +613,20 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 return FALSE; } - suboffset = 0; - while (suboffset < bandsByteCount) { - BYTE r, g, b; + BYTE cr, cg, cb; UINT16 xStart; UINT16 xEnd; UINT16 yStart; UINT16 yEnd; UINT32 colorBkg; UINT16 vBarHeader; - UINT16 vBarYOn; + UINT16 vBarYOn = 0; UINT16 vBarYOff; UINT32 vBarCount; UINT32 vBarPixelCount; - UINT32 vBarShortPixelCount; + UINT32 vBarShortPixelCount = 0; if (Stream_GetRemainingLength(s) < 11) { @@ -632,11 +638,11 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 Stream_Read_UINT16(s, xEnd); Stream_Read_UINT16(s, yStart); Stream_Read_UINT16(s, yEnd); - Stream_Read_UINT8(s, b); - Stream_Read_UINT8(s, g); - Stream_Read_UINT8(s, r); + Stream_Read_UINT8(s, cb); + Stream_Read_UINT8(s, cg); + Stream_Read_UINT8(s, cr); suboffset += 11; - colorBkg = FreeRDPGetColor(clear->format, r, g, b, 0xFF); + colorBkg = FreeRDPGetColor(clear->format, cr, cg, cb, 0xFF); if (xEnd < xStart) { @@ -652,13 +658,13 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 vBarCount = (xEnd - xStart) + 1; - for (i = 0; i < vBarCount; i++) + for (UINT32 i = 0; i < vBarCount; i++) { UINT32 vBarHeight; CLEAR_VBAR_ENTRY* vBarEntry = NULL; - CLEAR_VBAR_ENTRY* vBarShortEntry; + CLEAR_VBAR_ENTRY* vBarShortEntry = NULL; BOOL vBarUpdate = FALSE; - const BYTE* pSrcPixel; + const BYTE* cpSrcPixel; if (Stream_GetRemainingLength(s) < 2) { @@ -740,11 +746,11 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 if (!resize_vbar_entry(clear, vBarShortEntry)) return FALSE; - for (y = 0; y < vBarShortPixelCount; y++) + for (UINT32 y = 0; y < vBarShortPixelCount; y++) { - BYTE r, g, b; + BYTE r = 0, g = 0, b = 0; BYTE* dstBuffer = &vBarShortEntry->pixels[y * GetBytesPerPixel(clear->format)]; - UINT32 color; + UINT32 color = 0; Stream_Read_UINT8(s, b); Stream_Read_UINT8(s, g); Stream_Read_UINT8(s, r); @@ -804,8 +810,8 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 dstBuffer = vBarEntry->pixels; /* if (y < vBarYOn), use colorBkg */ - y = 0; - count = vBarYOn; + UINT32 y = 0; + UINT32 count = vBarYOn; if ((y + count) > vBarPixelCount) count = (vBarPixelCount > y) ? (vBarPixelCount - y) : 0; @@ -827,8 +833,15 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 count = (vBarPixelCount > y) ? (vBarPixelCount - y) : 0; if (count > 0) - pSrcPixel = - &vBarShortEntry->pixels[(y - vBarYOn) * GetBytesPerPixel(clear->format)]; + { + const size_t offset = (1ull * y - vBarYOn) * GetBytesPerPixel(clear->format); + pSrcPixel = &vBarShortEntry->pixels[offset]; + if (offset + count > vBarShortEntry->count) + { + WLog_ERR(TAG, "offset + count > vBarShortEntry->count"); + return FALSE; + } + } for (x = 0; x < count; x++) { @@ -868,28 +881,31 @@ static BOOL clear_decompress_bands_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 return FALSE; } - nXDstRel = nXDst + xStart; - nYDstRel = nYDst + yStart; - pSrcPixel = vBarEntry->pixels; + const UINT32 nXDstRel = nXDst + xStart; + const UINT32 nYDstRel = nYDst + yStart; + cpSrcPixel = vBarEntry->pixels; if (i < nWidth) { - count = vBarEntry->count; + UINT32 count = vBarEntry->count; if (count > nHeight) count = nHeight; - for (y = 0; y < count; y++) + if (nXDstRel + i > nDstWidth) + return FALSE; + + for (UINT32 y = 0; y < count; y++) { BYTE* pDstPixel8 = &pDstData[((nYDstRel + y) * nDstStep) + ((nXDstRel + i) * GetBytesPerPixel(DstFormat))]; - UINT32 color = ReadColor(pSrcPixel, clear->format); + UINT32 color = ReadColor(cpSrcPixel, clear->format); color = FreeRDPConvertColor(color, clear->format, DstFormat, NULL); if (!WriteColor(pDstPixel8, DstFormat, color)) return FALSE; - pSrcPixel += GetBytesPerPixel(clear->format); + cpSrcPixel += GetBytesPerPixel(clear->format); } } } @@ -975,23 +991,35 @@ static BOOL clear_decompress_glyph_data(CLEAR_CONTEXT* clear, wStream* s, UINT32 { const UINT32 bpp = GetBytesPerPixel(clear->format); CLEAR_GLYPH_ENTRY* glyphEntry = &(clear->GlyphCache[glyphIndex]); - glyphEntry->count = nWidth * nHeight; + const size_t count = 1ull * nWidth * nHeight; + const size_t hlimit = SIZE_MAX / ((nWidth > 0) ? nWidth : 1); + if ((nWidth == 0) || (nHeight == 0) || (hlimit < nHeight)) + { + WLog_ERR(TAG, + "CLEARCODEC_FLAG_GLYPH_INDEX: invalid dimensions nWidth=%" PRIu32 + " nHeight=%" PRIu32, + nWidth, nHeight); + return FALSE; + } - if (glyphEntry->count > glyphEntry->size) + if (count > glyphEntry->size) { - BYTE* tmp; - tmp = realloc(glyphEntry->pixels, glyphEntry->count * bpp * 1ULL); + BYTE* tmp = realloc(glyphEntry->pixels, count * bpp); if (!tmp) { - WLog_ERR(TAG, "glyphEntry->pixels realloc %" PRIu32 " failed!", - glyphEntry->count * bpp); + WLog_ERR(TAG, "glyphEntry->pixels realloc %zu failed!", count * bpp); return FALSE; } + glyphEntry->count = (UINT32)count; glyphEntry->size = glyphEntry->count; glyphEntry->pixels = (UINT32*)tmp; } + else + { + glyphEntry->count = (UINT32)count; + } if (!glyphEntry->pixels) { @@ -1117,7 +1145,7 @@ INT32 clear_decompress(CLEAR_CONTEXT* clear, const BYTE* pSrcData, UINT32 SrcSiz if (bandsByteCount > 0) { if (!clear_decompress_bands_data(clear, s, bandsByteCount, nWidth, nHeight, pDstData, - DstFormat, nDstStep, nXDst, nYDst)) + DstFormat, nDstStep, nXDst, nYDst, nDstWidth, nDstHeight)) { WLog_ERR(TAG, "clear_decompress_bands_data failed!"); goto fail; diff --git a/libfreerdp/codec/color.c b/libfreerdp/codec/color.c index edbfcd4ed677..f8467c295eb7 100644 --- a/libfreerdp/codec/color.c +++ b/libfreerdp/codec/color.c @@ -56,7 +56,7 @@ BYTE* freerdp_glyph_convert(UINT32 width, UINT32 height, const BYTE* data) * means of accessing individual pixels in blitting operations */ scanline = (width + 7) / 8; - dstData = (BYTE*)_aligned_malloc(width * height * 1ULL, 16); + dstData = (BYTE*)_aligned_malloc(1ull * width * height, 16); if (!dstData) return NULL; @@ -545,7 +545,7 @@ BOOL freerdp_image_copy_from_pointer_data(BYTE* pDstData, UINT32 DstFormat, UINT for (y = nYDst; y < nHeight; y++) { BYTE* pDstLine = &pDstData[y * nDstStep + nXDst * dstBytesPerPixel]; - memset(pDstLine, 0, dstBytesPerPixel * (nWidth - nXDst) * 1ULL); + memset(pDstLine, 0, 1ull * dstBytesPerPixel * (nWidth - nXDst)); } switch (xorBpp) @@ -613,6 +613,9 @@ BOOL freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 if (!pDstData || !pSrcData) return FALSE; + if ((nWidth == 0) || (nHeight == 0)) + return TRUE; + if (nDstStep == 0) nDstStep = nWidth * GetBytesPerPixel(DstFormat); @@ -728,21 +731,22 @@ BOOL freerdp_image_copy(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 BOOL freerdp_image_fill(BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, UINT32 color) { - UINT32 x, y; + if ((nWidth == 0) || (nHeight == 0)) + return TRUE; const UINT32 bpp = GetBytesPerPixel(DstFormat); BYTE* pFirstDstLine = &pDstData[nYDst * nDstStep]; BYTE* pFirstDstLineXOffset = &pFirstDstLine[nXDst * bpp]; - for (x = 0; x < nWidth; x++) + for (UINT32 x = 0; x < nWidth; x++) { BYTE* pDst = &pFirstDstLine[(x + nXDst) * bpp]; WriteColor(pDst, DstFormat, color); } - for (y = 1; y < nHeight; y++) + for (UINT32 y = 1; y < nHeight; y++) { BYTE* pDstLine = &pDstData[(y + nYDst) * nDstStep + nXDst * bpp]; - memcpy(pDstLine, pFirstDstLineXOffset, nWidth * bpp * 1ULL); + memcpy(pDstLine, pFirstDstLineXOffset, 1ull * nWidth * bpp); } return TRUE; diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c index a18d044cd2b6..416468f2e076 100644 --- a/libfreerdp/codec/dsp.c +++ b/libfreerdp/codec/dsp.c @@ -292,11 +292,31 @@ static const INT16 ima_step_size_table[] = { 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; +static inline void dsp_ima_clamp_step(ADPCM* adpcm, unsigned int channel) +{ + WINPR_ASSERT(adpcm); + if (adpcm->ima.last_step[channel] < 0) + adpcm->ima.last_step[channel] = 0; + + const size_t size = ARRAYSIZE(ima_step_size_table); + if (adpcm->ima.last_step[channel] > size) + adpcm->ima.last_step[channel] = size; +} + static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BYTE sample) { INT32 ss; INT32 d; - ss = ima_step_size_table[adpcm->ima.last_step[channel]]; + INT16 offset; + + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); + + offset = adpcm->ima.last_step[channel]; + WINPR_ASSERT(offset >= 0); + WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); + + ss = ima_step_size_table[offset]; d = (ss >> 3); if (sample & 1) @@ -319,16 +339,29 @@ static UINT16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, unsigned int channel, BY d = 32767; adpcm->ima.last_sample[channel] = (INT16)d; + WINPR_ASSERT(sample < ARRAYSIZE(ima_step_index_table)); adpcm->ima.last_step[channel] += ima_step_index_table[sample]; - if (adpcm->ima.last_step[channel] < 0) - adpcm->ima.last_step[channel] = 0; - else if (adpcm->ima.last_step[channel] > 88) - adpcm->ima.last_step[channel] = 88; + dsp_ima_clamp_step(adpcm, channel); return (UINT16)d; } +static BOOL valid_ima_adpcm_format(const FREERDP_DSP_CONTEXT* context) +{ + if (!context) + return FALSE; + if (context->format.wFormatTag != WAVE_FORMAT_DVI_ADPCM) + return FALSE; + if (context->format.nBlockAlign <= 4ULL) + return FALSE; + if (context->format.nChannels < 1) + return FALSE; + if (context->format.wBitsPerSample == 0) + return FALSE; + return TRUE; +} + static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size, wStream* out) { @@ -341,6 +374,9 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT const UINT32 channels = context->format.nChannels; size_t i; + if (!valid_ima_adpcm_format(context)) + return FALSE; + if (!Stream_EnsureCapacity(out, out_size)) return FALSE; @@ -350,18 +386,28 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT { if (size % block_size == 0) { + if (size < 4) + return FALSE; + context->adpcm.ima.last_sample[0] = (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); context->adpcm.ima.last_step[0] = (INT16)(*(src + 2)); + + dsp_ima_clamp_step(&context->adpcm, 0); + src += 4; size -= 4; out_size -= 16; if (channels > 1) { + if (size < 4) + return FALSE; context->adpcm.ima.last_sample[1] = (INT16)(((UINT16)(*src)) | (((UINT16)(*(src + 1))) << 8)); context->adpcm.ima.last_step[1] = (INT16)(*(src + 2)); + + dsp_ima_clamp_step(&context->adpcm, 1); src += 4; size -= 4; out_size -= 16; @@ -370,6 +416,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT if (channels > 1) { + if (size < 8) + return FALSE; for (i = 0; i < 8; i++) { channel = (i < 4 ? 0 : 1); @@ -389,6 +437,8 @@ static BOOL freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT } else { + if (size < 1) + return FALSE; sample = ((*src) & 0x0f); decoded = dsp_decode_ima_adpcm_sample(&context->adpcm, 0, sample); *dst++ = (decoded & 0xFF); @@ -464,6 +514,21 @@ static BOOL freerdp_dsp_encode_gsm610(FREERDP_DSP_CONTEXT* context, const BYTE* #endif #if defined(WITH_LAME) +static BOOL valid_mp3_format(const FREERDP_DSP_CONTEXT* context) +{ + if (!context) + return FALSE; + if (context->format.wFormatTag != WAVE_FORMAT_MPEGLAYER3) + return FALSE; + if (context->format.nChannels < 1) + return FALSE; + if (context->format.wBitsPerSample == 0) + return FALSE; + if (context->format.nSamplesPerSec == 0) + return FALSE; + return TRUE; +} + static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size, wStream* out) { @@ -475,6 +540,9 @@ static BOOL freerdp_dsp_decode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src if (!context || !src || !out) return FALSE; + if (!valid_mp3_format(context)) + return FALSE; + buffer_size = 2 * context->format.nChannels * context->format.nSamplesPerSec; if (!Stream_EnsureCapacity(context->buffer, 2 * buffer_size)) @@ -509,6 +577,9 @@ static BOOL freerdp_dsp_encode_mp3(FREERDP_DSP_CONTEXT* context, const BYTE* src if (!context || !src || !out) return FALSE; + if (!valid_mp3_format(context)) + return FALSE; + samples_per_channel = size / context->format.nChannels / context->format.wBitsPerSample / 8; /* Ensure worst case buffer size for mp3 stream taken from LAME header */ @@ -650,7 +721,16 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample) INT32 ss; BYTE enc; INT32 diff; - ss = ima_step_size_table[adpcm->ima.last_step[channel]]; + INT16 offset; + + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_step)); + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ima.last_sample)); + + offset = adpcm->ima.last_step[channel]; + WINPR_ASSERT(offset >= 0); + WINPR_ASSERT(offset < ARRAYSIZE(ima_step_size_table)); + + ss = ima_step_size_table[offset]; d = e = sample - adpcm->ima.last_sample[channel]; diff = ss >> 3; enc = 0; @@ -696,12 +776,10 @@ static BYTE dsp_encode_ima_adpcm_sample(ADPCM* adpcm, int channel, INT16 sample) diff = 32767; adpcm->ima.last_sample[channel] = (INT16)diff; + WINPR_ASSERT(enc < ARRAYSIZE(ima_step_index_table)); adpcm->ima.last_step[channel] += ima_step_index_table[enc]; - if (adpcm->ima.last_step[channel] < 0) - adpcm->ima.last_step[channel] = 0; - else if (adpcm->ima.last_step[channel] > 88) - adpcm->ima.last_step[channel] = 88; + dsp_ima_clamp_step(adpcm, channel); return enc; } @@ -718,6 +796,8 @@ static BOOL freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context, const BYT size_t align; out_size = size / 2; + if (!valid_ima_adpcm_format(context)) + return FALSE; if (!Stream_EnsureRemainingCapacity(out, size)) return FALSE; @@ -792,10 +872,29 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample { INT8 nibble; INT32 presample; + BYTE predictor; + INT32 coeff1; + INT32 coeff2; + INT32 tableval; + + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample1)); + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.sample2)); + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.delta)); + WINPR_ASSERT(channel < ARRAYSIZE(adpcm->ms.predictor)); + nibble = (sample & 0x08 ? (INT8)sample - 16 : (INT8)sample); - presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + - (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / - 256; + predictor = adpcm->ms.predictor[channel]; + + coeff1 = 0; + if (predictor < ARRAYSIZE(ms_adpcm_coeffs1)) + coeff1 = ms_adpcm_coeffs1[predictor]; + + coeff2 = 0; + if (predictor < ARRAYSIZE(ms_adpcm_coeffs2)) + coeff2 = ms_adpcm_coeffs2[predictor]; + + presample = + ((adpcm->ms.sample1[channel] * coeff1) + (adpcm->ms.sample2[channel] * coeff2)) / 256; presample += nibble * adpcm->ms.delta[channel]; if (presample > 32767) @@ -805,7 +904,12 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; adpcm->ms.sample1[channel] = presample; - adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256; + + tableval = 0; + if (sample < ARRAYSIZE(ms_adpcm_adaptation_table)) + tableval = ms_adpcm_adaptation_table[sample]; + + adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * tableval / 256; if (adpcm->ms.delta[channel] < 16) adpcm->ms.delta[channel] = 16; @@ -813,11 +917,28 @@ static INLINE INT16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, BYTE sample return (INT16)presample; } +static BOOL valid_ms_adpcm_format(const FREERDP_DSP_CONTEXT* context) +{ + if (!context) + return FALSE; + if (context->format.wFormatTag != WAVE_FORMAT_ADPCM) + return FALSE; + if (context->format.nBlockAlign <= 4ULL) + return FALSE; + if (context->format.nChannels < 1) + return FALSE; + if (context->format.wBitsPerSample == 0) + return FALSE; + return TRUE; +} + static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE* src, size_t size, wStream* out) { BYTE* dst; BYTE sample; + if (!valid_ms_adpcm_format(context)) + return FALSE; const size_t out_size = size * 4; const UINT32 channels = context->format.nChannels; const UINT32 block_size = context->format.nBlockAlign; @@ -833,6 +954,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE { if (channels > 1) { + if (size < 14) + return FALSE; + context->adpcm.ms.predictor[0] = *src++; context->adpcm.ms.predictor[1] = *src++; context->adpcm.ms.delta[0] = read_int16(src); @@ -859,6 +983,9 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE } else { + if (size < 7) + return FALSE; + context->adpcm.ms.predictor[0] = *src++; context->adpcm.ms.delta[0] = read_int16(src); src += 2; @@ -876,12 +1003,16 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE if (channels > 1) { + if (size < 1) + return FALSE; sample = *src++; size--; write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0)); dst += 2; write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1)); dst += 2; + if (size < 1) + return FALSE; sample = *src++; size--; write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0)); @@ -891,6 +1022,8 @@ static BOOL freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE } else { + if (size < 1) + return FALSE; sample = *src++; size--; write_int16(dst, freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0)); @@ -908,6 +1041,14 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int c { INT32 presample; INT32 errordelta; + size_t offset; + + WINPR_ASSERT(channel >= 0); + WINPR_ASSERT((size_t)channel < ARRAYSIZE(adpcm->ms.sample1)); + WINPR_ASSERT((size_t)channel < ARRAYSIZE(adpcm->ms.sample2)); + WINPR_ASSERT((size_t)channel < ARRAYSIZE(adpcm->ms.delta)); + WINPR_ASSERT((size_t)channel < ARRAYSIZE(adpcm->ms.predictor)); + presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeffs1[adpcm->ms.predictor[channel]]) + (adpcm->ms.sample2[channel] * ms_adpcm_coeffs2[adpcm->ms.predictor[channel]])) / 256; @@ -930,8 +1071,9 @@ static BYTE freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, INT32 sample, int c adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; adpcm->ms.sample1[channel] = presample; - adpcm->ms.delta[channel] = - adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((BYTE)errordelta) & 0x0F)] / 256; + offset = (((BYTE)errordelta) & 0x0F); + WINPR_ASSERT(offset < ARRAYSIZE(ms_adpcm_adaptation_table)); + adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[offset] / 256; if (adpcm->ms.delta[channel] < 16) adpcm->ms.delta[channel] = 16; @@ -949,6 +1091,9 @@ static BOOL freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context, const BYTE const size_t step = 8 + ((context->format.nChannels > 1) ? 4 : 0); out_size = size / 2; + if (!valid_ms_adpcm_format(context)) + return FALSE; + if (!Stream_EnsureRemainingCapacity(out, size)) return FALSE; @@ -1308,6 +1453,28 @@ BOOL freerdp_dsp_context_reset(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* return FALSE; context->format = *targetFormat; + + switch (context->format.wFormatTag) + { +#if defined(WITH_LAME) + case WAVE_FORMAT_MPEGLAYER3: + if (!valid_mp3_format(context)) + return FALSE; + break; +#endif + case WAVE_FORMAT_ADPCM: + if (!valid_ms_adpcm_format(context)) + return FALSE; + break; + case WAVE_FORMAT_DVI_ADPCM: + if (!valid_ima_adpcm_format(context)) + return FALSE; + break; + default: + break; + } + + #if defined(WITH_FAAD2) context->faadSetup = FALSE; #endif diff --git a/libfreerdp/codec/dsp_ffmpeg.c b/libfreerdp/codec/dsp_ffmpeg.c index 0af3cf4fc61a..80df188339ee 100644 --- a/libfreerdp/codec/dsp_ffmpeg.c +++ b/libfreerdp/codec/dsp_ffmpeg.c @@ -418,9 +418,29 @@ static BOOL ffmpeg_resample_frame(AVAudioResampleContext* context, AVFrame* in, static BOOL ffmpeg_encode_frame(AVCodecContext* context, AVFrame* in, AVPacket* packet, wStream* out) { - int ret; + if (in->format == AV_SAMPLE_FMT_FLTP) + { + uint8_t** pp = in->extended_data; + for (int y = 0; y < in->channels; y++) + { + float* data = (float*)pp[y]; + for (int x = 0; x < in->nb_samples; x++) + { + const float val1 = data[x]; + if (isnan(val1)) + data[x] = 0.0f; + else if (isinf(val1)) + { + if (val1 < 0.0f) + data[x] = -1.0f; + else + data[x] = 1.0f; + } + } + } + } /* send the packet with the compressed data to the encoder */ - ret = avcodec_send_frame(context, in); + int ret = avcodec_send_frame(context, in); if (ret < 0) { @@ -571,7 +591,9 @@ BOOL freerdp_dsp_ffmpeg_supports_format(const AUDIO_FORMAT* format, BOOL encode) FREERDP_DSP_CONTEXT* freerdp_dsp_ffmpeg_context_new(BOOL encode) { FREERDP_DSP_CONTEXT* context; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 10, 100) avcodec_register_all(); +#endif context = calloc(1, sizeof(FREERDP_DSP_CONTEXT)); if (!context) @@ -667,7 +689,9 @@ BOOL freerdp_dsp_ffmpeg_decode(FREERDP_DSP_CONTEXT* context, const AUDIO_FORMAT* if (!context || !srcFormat || !data || !out || context->encoder) return FALSE; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 133, 100) av_init_packet(context->packet); +#endif context->packet->data = (uint8_t*)data; context->packet->size = length; return ffmpeg_decode(context->context, context->packet, context->frame, context->rcontext, diff --git a/libfreerdp/codec/h264.c b/libfreerdp/codec/h264.c index 00b812b2921c..7bc4a147cc00 100644 --- a/libfreerdp/codec/h264.c +++ b/libfreerdp/codec/h264.c @@ -63,9 +63,9 @@ BOOL avc420_ensure_buffer(H264_CONTEXT* h264, UINT32 stride, UINT32 width, UINT3 _aligned_free(h264->pYUVData[0]); _aligned_free(h264->pYUVData[1]); _aligned_free(h264->pYUVData[2]); - h264->pYUVData[0] = _aligned_malloc(h264->iStride[0] * height * 1ULL, 16); - h264->pYUVData[1] = _aligned_malloc(h264->iStride[1] * height * 1ULL, 16); - h264->pYUVData[2] = _aligned_malloc(h264->iStride[2] * height * 1ULL, 16); + h264->pYUVData[0] = _aligned_malloc(1ull * h264->iStride[0] * height, 16); + h264->pYUVData[1] = _aligned_malloc(1ull * h264->iStride[1] * height, 16); + h264->pYUVData[2] = _aligned_malloc(1ull * h264->iStride[2] * height, 16); if (!h264->pYUVData[0] || !h264->pYUVData[1] || !h264->pYUVData[2]) return FALSE; @@ -165,6 +165,36 @@ static BOOL avc_yuv_to_rgb(H264_CONTEXT* h264, const RECTANGLE_16* regionRects, return TRUE; } +static BOOL isRectValid(UINT32 width, UINT32 height, const RECTANGLE_16* rect) +{ + WINPR_ASSERT(rect); + if (rect->left > width) + return FALSE; + if (rect->right > width) + return FALSE; + if (rect->left >= rect->right) + return FALSE; + if (rect->top > height) + return FALSE; + if (rect->bottom > height) + return FALSE; + if (rect->top >= rect->bottom) + return FALSE; + return TRUE; +} + +static BOOL areRectsValid(UINT32 width, UINT32 height, const RECTANGLE_16* rects, UINT32 count) +{ + WINPR_ASSERT(rects || (count == 0)); + for (size_t x = 0; x < count; x++) + { + const RECTANGLE_16* rect = &rects[x]; + if (!isRectValid(width, height, rect)) + return FALSE; + } + return TRUE; +} + INT32 avc420_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize, BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight, RECTANGLE_16* regionRects, UINT32 numRegionRects) @@ -174,6 +204,9 @@ INT32 avc420_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize if (!h264) return -1001; + if (!areRectsValid(nDstWidth, nDstHeight, regionRects, numRegionRects)) + return -1013; + status = h264->subsystem->Decompress(h264, pSrcData, SrcSize); if (status == 0) @@ -414,6 +447,11 @@ INT32 avc444_decompress(H264_CONTEXT* h264, BYTE op, RECTANGLE_16* regionRects, if (!h264 || !regionRects || !pSrcData || !pDstData) return -1001; + if (!areRectsValid(nDstWidth, nDstHeight, regionRects, numRegionRects)) + return -1013; + if (!areRectsValid(nDstWidth, nDstHeight, auxRegionRects, numAuxRegionRect)) + return -1014; + switch (op) { case 0: /* YUV420 in stream 1 @@ -484,7 +522,7 @@ INT32 avc444_decompress(H264_CONTEXT* h264, BYTE op, RECTANGLE_16* regionRects, #define MAX_SUBSYSTEMS 10 static INIT_ONCE subsystems_once = INIT_ONCE_STATIC_INIT; -static H264_CONTEXT_SUBSYSTEM* subSystems[MAX_SUBSYSTEMS]; +static H264_CONTEXT_SUBSYSTEM* subSystems[MAX_SUBSYSTEMS] = { 0 }; #if defined(_WIN32) && defined(WITH_MEDIA_FOUNDATION) extern H264_CONTEXT_SUBSYSTEM g_Subsystem_MF; @@ -493,7 +531,14 @@ extern H264_CONTEXT_SUBSYSTEM g_Subsystem_MF; static BOOL CALLBACK h264_register_subsystems(PINIT_ONCE once, PVOID param, PVOID* context) { int i = 0; - ZeroMemory(subSystems, sizeof(subSystems)); + +#ifdef WITH_MEDIACODEC + { + extern H264_CONTEXT_SUBSYSTEM g_Subsystem_mediacodec; + subSystems[i] = &g_Subsystem_mediacodec; + i++; + } +#endif #if defined(_WIN32) && defined(WITH_MEDIA_FOUNDATION) { subSystems[i] = &g_Subsystem_MF; @@ -513,13 +558,6 @@ static BOOL CALLBACK h264_register_subsystems(PINIT_ONCE once, PVOID param, PVOI subSystems[i] = &g_Subsystem_libavcodec; i++; } -#endif -#ifdef WITH_X264 - { - extern H264_CONTEXT_SUBSYSTEM g_Subsystem_x264; - subSystems[i] = &g_Subsystem_x264; - i++; - } #endif return i > 0; } diff --git a/libfreerdp/codec/h264_ffmpeg.c b/libfreerdp/codec/h264_ffmpeg.c index 4e3ba8f9282f..9c445b8253ae 100644 --- a/libfreerdp/codec/h264_ffmpeg.c +++ b/libfreerdp/codec/h264_ffmpeg.c @@ -529,10 +529,12 @@ static BOOL libavcodec_init(H264_CONTEXT* h264) goto EXCEPTION; } +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 18, 100) if (sys->codecDecoder->capabilities & AV_CODEC_CAP_TRUNCATED) { sys->codecDecoderContext->flags |= AV_CODEC_FLAG_TRUNCATED; } +#endif #ifdef WITH_VAAPI diff --git a/libfreerdp/codec/h264_mediacodec.c b/libfreerdp/codec/h264_mediacodec.c new file mode 100644 index 000000000000..9caec04b4a27 --- /dev/null +++ b/libfreerdp/codec/h264_mediacodec.c @@ -0,0 +1,647 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * H.264 Bitmap Compression + * + * Copyright 2022 Ely Ronnen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include + +#include +#include + +#include "h264.h" + +#define RESOLVE_MEDIANDK_FUNC(sys, name) \ + ({ \ + BOOL rc = TRUE; \ + sys->fn##name = GetProcAddress(sys->mediandkLibrary, #name); \ + if (sys->fn##name == NULL) \ + { \ + WLog_Print(h264->log, WLOG_ERROR, \ + "Error resolving function " #name " from libmediandk.so"); \ + rc = FALSE; \ + } \ + rc; \ + }) + +#define RESOLVE_MEDIANDK_VARIABLE(sys, member, exported) \ + ({ \ + BOOL rc = FALSE; \ + const char** temp = GetProcAddress(sys->mediandkLibrary, exported); \ + if (temp == NULL) \ + { \ + WLog_Print(h264->log, WLOG_ERROR, \ + "Error resolving variable " exported " from libmediandk.so"); \ + } \ + else \ + { \ + sys->member = *temp; \ + rc = TRUE; \ + } \ + rc; \ + }) + +typedef AMediaFormat* (*AMediaFormat_new_t)(void); +typedef media_status_t (*AMediaFormat_delete_t)(AMediaFormat*); +typedef const char* (*AMediaFormat_toString_t)(AMediaFormat*); +typedef void (*AMediaFormat_setInt32_t)(AMediaFormat*, const char*, int32_t); +typedef void (*AMediaFormat_setString_t)(AMediaFormat*, const char*, const char*); +typedef AMediaCodec* (*AMediaCodec_createDecoderByType_t)(const char*); +typedef media_status_t (*AMediaCodec_delete_t)(AMediaCodec*); +typedef media_status_t (*AMediaCodec_configure_t)(AMediaCodec*, const AMediaFormat*, ANativeWindow*, + AMediaCrypto*, uint32_t); +typedef media_status_t (*AMediaCodec_start_t)(AMediaCodec*); +typedef media_status_t (*AMediaCodec_stop_t)(AMediaCodec*); +typedef uint8_t* (*AMediaCodec_getInputBuffer_t)(AMediaCodec*, size_t, size_t*); +typedef uint8_t* (*AMediaCodec_getOutputBuffer_t)(AMediaCodec*, size_t, size_t*); +typedef ssize_t (*AMediaCodec_dequeueInputBuffer_t)(AMediaCodec*, int64_t); +typedef media_status_t (*AMediaCodec_queueInputBuffer_t)(AMediaCodec*, size_t, ssize_t, size_t, + uint64_t, uint32_t); +typedef ssize_t (*AMediaCodec_dequeueOutputBuffer_t)(AMediaCodec*, AMediaCodecBufferInfo*, int64_t); +typedef AMediaFormat* (*AMediaCodec_getOutputFormat_t)(AMediaCodec*); +typedef media_status_t (*AMediaCodec_releaseOutputBuffer_t)(AMediaCodec*, size_t, bool); +typedef media_status_t (*AMediaCodec_setParameters_t)(AMediaCodec*, const AMediaFormat*); // 26 +typedef media_status_t (*AMediaCodec_getName_t)(AMediaCodec*, char**); // 28 +typedef void (*AMediaCodec_releaseName_t)(AMediaCodec*, char*); // 28 +typedef AMediaFormat* (*AMediaCodec_getInputFormat_t)(AMediaCodec*); // 28 + +const int COLOR_FormatYUV420Planar = 19; +const int COLOR_FormatYUV420Flexible = 0x7f420888; + +struct _H264_CONTEXT_MEDIACODEC +{ + AMediaCodec* decoder; + AMediaFormat* inputFormat; + AMediaFormat* outputFormat; + int32_t width; + int32_t height; + ssize_t currentOutputBufferIndex; + + // libmediandk.so imports + HMODULE mediandkLibrary; + + AMediaFormat_new_t fnAMediaFormat_new; + AMediaFormat_delete_t fnAMediaFormat_delete; + AMediaFormat_toString_t fnAMediaFormat_toString; + AMediaFormat_setInt32_t fnAMediaFormat_setInt32; + AMediaFormat_setString_t fnAMediaFormat_setString; + AMediaCodec_createDecoderByType_t fnAMediaCodec_createDecoderByType; + AMediaCodec_delete_t fnAMediaCodec_delete; + AMediaCodec_configure_t fnAMediaCodec_configure; + AMediaCodec_start_t fnAMediaCodec_start; + AMediaCodec_stop_t fnAMediaCodec_stop; + AMediaCodec_getInputBuffer_t fnAMediaCodec_getInputBuffer; + AMediaCodec_getOutputBuffer_t fnAMediaCodec_getOutputBuffer; + AMediaCodec_dequeueInputBuffer_t fnAMediaCodec_dequeueInputBuffer; + AMediaCodec_queueInputBuffer_t fnAMediaCodec_queueInputBuffer; + AMediaCodec_dequeueOutputBuffer_t fnAMediaCodec_dequeueOutputBuffer; + AMediaCodec_getOutputFormat_t fnAMediaCodec_getOutputFormat; + AMediaCodec_releaseOutputBuffer_t fnAMediaCodec_releaseOutputBuffer; + AMediaCodec_setParameters_t fnAMediaCodec_setParameters; + AMediaCodec_getName_t fnAMediaCodec_getName; + AMediaCodec_releaseName_t fnAMediaCodec_releaseName; + AMediaCodec_getInputFormat_t fnAMediaCodec_getInputFormat; + + const char* gAMediaFormatKeyMime; + const char* gAMediaFormatKeyWidth; + const char* gAMediaFormatKeyHeight; + const char* gAMediaFormatKeyColorFormat; +}; + +typedef struct _H264_CONTEXT_MEDIACODEC H264_CONTEXT_MEDIACODEC; + +static int load_libmediandk(H264_CONTEXT* h264) +{ + BOOL rc; + H264_CONTEXT_MEDIACODEC* sys; + + WINPR_ASSERT(h264); + + sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData; + WINPR_ASSERT(sys); + + WLog_Print(h264->log, WLOG_DEBUG, "MediaCodec Loading libmediandk.so"); + + sys->mediandkLibrary = LoadLibraryA("libmediandk.so"); + if (sys->mediandkLibrary == NULL) + { + WLog_Print(h264->log, WLOG_WARN, "Error loading libmediandk.so"); + return -1; + } + + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaFormat_new); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaFormat_delete); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaFormat_toString); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaFormat_setInt32); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaFormat_setString); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_createDecoderByType); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_delete); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_configure); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_start); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_stop); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_getInputBuffer); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_getOutputBuffer); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_dequeueInputBuffer); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_queueInputBuffer); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_dequeueOutputBuffer); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_getOutputFormat); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_releaseOutputBuffer); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_setParameters); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_getName); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_releaseName); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_FUNC(sys, AMediaCodec_getInputFormat); + if (!rc) + return -1; + + rc = RESOLVE_MEDIANDK_VARIABLE(sys, gAMediaFormatKeyMime, "AMEDIAFORMAT_KEY_MIME"); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_VARIABLE(sys, gAMediaFormatKeyWidth, "AMEDIAFORMAT_KEY_WIDTH"); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_VARIABLE(sys, gAMediaFormatKeyHeight, "AMEDIAFORMAT_KEY_HEIGHT"); + if (!rc) + return -1; + rc = RESOLVE_MEDIANDK_VARIABLE(sys, gAMediaFormatKeyColorFormat, + "AMEDIAFORMAT_KEY_COLOR_FORMAT"); + if (!rc) + return -1; + + return 0; +} + +static void unload_libmediandk(H264_CONTEXT* h264) +{ + H264_CONTEXT_MEDIACODEC* sys; + + WINPR_ASSERT(h264); + + sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData; + WINPR_ASSERT(sys); + + if (NULL == sys->mediandkLibrary) + { + return; + } + + FreeLibrary(sys->mediandkLibrary); +} + +static void set_mediacodec_format(H264_CONTEXT* h264, AMediaFormat** formatVariable, + AMediaFormat* newFormat) +{ + media_status_t status = AMEDIA_OK; + H264_CONTEXT_MEDIACODEC* sys; + + WINPR_ASSERT(h264); + WINPR_ASSERT(formatVariable); + + sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData; + WINPR_ASSERT(sys); + + if (*formatVariable != NULL) + { + status = sys->fnAMediaFormat_delete(*formatVariable); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "Error AMediaFormat_delete %d", status); + } + } + + *formatVariable = newFormat; +} + +static void release_current_outputbuffer(H264_CONTEXT* h264) +{ + media_status_t status = AMEDIA_OK; + H264_CONTEXT_MEDIACODEC* sys; + + WINPR_ASSERT(h264); + sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData; + WINPR_ASSERT(sys); + + if (sys->currentOutputBufferIndex < 0) + { + return; + } + + status = + sys->fnAMediaCodec_releaseOutputBuffer(sys->decoder, sys->currentOutputBufferIndex, FALSE); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "Error AMediaCodec_releaseOutputBuffer %d", status); + } + + sys->currentOutputBufferIndex = -1; +} + +static int mediacodec_compress(H264_CONTEXT* h264, const BYTE** pSrcYuv, const UINT32* pStride, + BYTE** ppDstData, UINT32* pDstSize) +{ + WINPR_ASSERT(h264); + WINPR_ASSERT(pSrcYuv); + WINPR_ASSERT(pStride); + WINPR_ASSERT(ppDstData); + WINPR_ASSERT(pDstSize); + + WLog_Print(h264->log, WLOG_ERROR, "MediaCodec is not supported as an encoder"); + return -1; +} + +static int mediacodec_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize) +{ + ssize_t inputBufferId = -1; + size_t inputBufferSize, outputBufferSize; + uint8_t* inputBuffer; + media_status_t status; + const char* media_format; + BYTE** pYUVData; + UINT32* iStride; + H264_CONTEXT_MEDIACODEC* sys; + + WINPR_ASSERT(h264); + WINPR_ASSERT(pSrcData); + + sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData; + WINPR_ASSERT(sys); + + pYUVData = h264->pYUVData; + WINPR_ASSERT(pYUVData); + + iStride = h264->iStride; + WINPR_ASSERT(iStride); + + release_current_outputbuffer(h264); + + if (sys->width == 0) + { + int32_t width = h264->width; + int32_t height = h264->height; + if (width % 16 != 0) + width += 16 - width % 16; + if (height % 16 != 0) + height += 16 - height % 16; + + WLog_Print(h264->log, WLOG_DEBUG, "MediaCodec setting width and height [%d,%d]", width, + height); + sys->fnAMediaFormat_setInt32(sys->inputFormat, sys->gAMediaFormatKeyWidth, width); + sys->fnAMediaFormat_setInt32(sys->inputFormat, sys->gAMediaFormatKeyHeight, height); + + status = sys->fnAMediaCodec_setParameters(sys->decoder, sys->inputFormat); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_setParameters failed: %d", status); + return -1; + } + + sys->width = width; + sys->height = height; + } + + while (true) + { + UINT32 inputBufferCurrnetOffset = 0; + while (inputBufferCurrnetOffset < SrcSize) + { + UINT32 numberOfBytesToCopy = SrcSize - inputBufferCurrnetOffset; + inputBufferId = sys->fnAMediaCodec_dequeueInputBuffer(sys->decoder, -1); + if (inputBufferId < 0) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_dequeueInputBuffer failed [%d]", + inputBufferId); + // TODO: sleep? + continue; + } + + inputBuffer = + sys->fnAMediaCodec_getInputBuffer(sys->decoder, inputBufferId, &inputBufferSize); + if (inputBuffer == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_getInputBuffer failed"); + return -1; + } + + if (numberOfBytesToCopy > inputBufferSize) + { + WLog_Print(h264->log, WLOG_WARN, + "MediaCodec inputBufferSize: got [%d] but wanted [%d]", inputBufferSize, + numberOfBytesToCopy); + numberOfBytesToCopy = inputBufferSize; + } + + memcpy(inputBuffer, pSrcData + inputBufferCurrnetOffset, numberOfBytesToCopy); + inputBufferCurrnetOffset += numberOfBytesToCopy; + + status = sys->fnAMediaCodec_queueInputBuffer(sys->decoder, inputBufferId, 0, + numberOfBytesToCopy, 0, 0); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "Error AMediaCodec_queueInputBuffer %d", status); + return -1; + } + } + + while (true) + { + AMediaCodecBufferInfo bufferInfo; + ssize_t outputBufferId = + sys->fnAMediaCodec_dequeueOutputBuffer(sys->decoder, &bufferInfo, -1); + if (outputBufferId >= 0) + { + sys->currentOutputBufferIndex = outputBufferId; + + uint8_t* outputBuffer; + outputBuffer = sys->fnAMediaCodec_getOutputBuffer(sys->decoder, outputBufferId, + &outputBufferSize); + sys->currentOutputBufferIndex = outputBufferId; + + if (outputBufferSize != (sys->width * sys->height + + ((sys->width + 1) / 2) * ((sys->height + 1) / 2) * 2)) + { + WLog_Print(h264->log, WLOG_ERROR, "Error unexpected output buffer size %d", + outputBufferSize); + return -1; + } + + // TODO: work with AImageReader and get AImage object instead of + // COLOR_FormatYUV420Planar buffer. + iStride[0] = sys->width; + iStride[1] = (sys->width + 1) / 2; + iStride[2] = (sys->width + 1) / 2; + pYUVData[0] = outputBuffer; + pYUVData[1] = outputBuffer + iStride[0] * sys->height; + pYUVData[2] = + outputBuffer + iStride[0] * sys->height + iStride[1] * ((sys->height + 1) / 2); + break; + } + else if (outputBufferId == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) + { + AMediaFormat* outputFormat; + outputFormat = sys->fnAMediaCodec_getOutputFormat(sys->decoder); + if (outputFormat == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_getOutputFormat failed"); + return -1; + } + set_mediacodec_format(h264, &sys->outputFormat, outputFormat); + + media_format = sys->fnAMediaFormat_toString(sys->outputFormat); + if (media_format == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaFormat_toString failed"); + return -1; + } + } + else if (outputBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) + { + WLog_Print(h264->log, WLOG_WARN, + "AMediaCodec_dequeueOutputBuffer need to try again later"); + // TODO: sleep? + } + else if (outputBufferId == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) + { + WLog_Print(h264->log, WLOG_WARN, + "AMediaCodec_dequeueOutputBuffer returned deprecated value " + "AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED, ignoring"); + } + else + { + WLog_Print(h264->log, WLOG_ERROR, + "AMediaCodec_dequeueOutputBuffer returned unknown value [%d]", + outputBufferId); + return -1; + } + } + + break; + } + + return 1; +} + +static void mediacodec_uninit(H264_CONTEXT* h264) +{ + media_status_t status = AMEDIA_OK; + H264_CONTEXT_MEDIACODEC* sys; + + WINPR_ASSERT(h264); + + sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData; + + WLog_Print(h264->log, WLOG_DEBUG, "Uninitializing MediaCodec"); + + if (!sys) + return; + + if (sys->decoder != NULL) + { + release_current_outputbuffer(h264); + status = sys->fnAMediaCodec_stop(sys->decoder); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "Error AMediaCodec_stop %d", status); + } + + status = sys->fnAMediaCodec_delete(sys->decoder); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "Error AMediaCodec_delete %d", status); + } + + sys->decoder = NULL; + } + + set_mediacodec_format(h264, &sys->inputFormat, NULL); + set_mediacodec_format(h264, &sys->outputFormat, NULL); + + unload_libmediandk(h264); + + free(sys); + h264->pSystemData = NULL; +} + +static BOOL mediacodec_init(H264_CONTEXT* h264) +{ + H264_CONTEXT_MEDIACODEC* sys; + media_status_t status; + const char* media_format; + char* codec_name; + AMediaFormat *inputFormat, *outputFormat; + + WINPR_ASSERT(h264); + + if (h264->Compressor) + { + WLog_Print(h264->log, WLOG_ERROR, "MediaCodec is not supported as an encoder"); + goto EXCEPTION; + } + + WLog_Print(h264->log, WLOG_DEBUG, "Initializing MediaCodec"); + + sys = (H264_CONTEXT_MEDIACODEC*)calloc(1, sizeof(H264_CONTEXT_MEDIACODEC)); + + if (!sys) + { + goto EXCEPTION; + } + + h264->pSystemData = (void*)sys; + + if (load_libmediandk(h264) < 0) + { + goto EXCEPTION; + } + + sys->currentOutputBufferIndex = -1; + sys->width = sys->height = 0; // update when we're given the height and width + sys->decoder = sys->fnAMediaCodec_createDecoderByType("video/avc"); + if (sys->decoder == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_createCodecByName failed"); + goto EXCEPTION; + } + + status = sys->fnAMediaCodec_getName(sys->decoder, &codec_name); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_getName failed: %d", status); + goto EXCEPTION; + } + + WLog_Print(h264->log, WLOG_DEBUG, "MediaCodec using video/avc codec [%s]", codec_name); + sys->fnAMediaCodec_releaseName(sys->decoder, codec_name); + + sys->inputFormat = sys->fnAMediaFormat_new(); + if (sys->inputFormat == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaFormat_new failed"); + goto EXCEPTION; + } + + sys->fnAMediaFormat_setString(sys->inputFormat, sys->gAMediaFormatKeyMime, "video/avc"); + sys->fnAMediaFormat_setInt32(sys->inputFormat, sys->gAMediaFormatKeyWidth, sys->width); + sys->fnAMediaFormat_setInt32(sys->inputFormat, sys->gAMediaFormatKeyHeight, sys->height); + sys->fnAMediaFormat_setInt32(sys->inputFormat, sys->gAMediaFormatKeyColorFormat, + COLOR_FormatYUV420Planar); + + media_format = sys->fnAMediaFormat_toString(sys->inputFormat); + if (media_format == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaFormat_toString failed"); + goto EXCEPTION; + } + + status = sys->fnAMediaCodec_configure(sys->decoder, sys->inputFormat, NULL, NULL, 0); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_configure failed: %d", status); + goto EXCEPTION; + } + + inputFormat = sys->fnAMediaCodec_getInputFormat(sys->decoder); + if (inputFormat == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_getInputFormat failed"); + goto EXCEPTION; + } + set_mediacodec_format(h264, &sys->inputFormat, inputFormat); + + media_format = sys->fnAMediaFormat_toString(sys->inputFormat); + if (media_format == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaFormat_toString failed"); + goto EXCEPTION; + } + WLog_Print(h264->log, WLOG_DEBUG, "Using MediaCodec with input MediaFormat [%s]", media_format); + + outputFormat = sys->fnAMediaCodec_getOutputFormat(sys->decoder); + if (outputFormat == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_getOutputFormat failed"); + goto EXCEPTION; + } + set_mediacodec_format(h264, &sys->outputFormat, outputFormat); + + media_format = sys->fnAMediaFormat_toString(sys->outputFormat); + if (media_format == NULL) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaFormat_toString failed"); + goto EXCEPTION; + } + WLog_Print(h264->log, WLOG_DEBUG, "Using MediaCodec with output MediaFormat [%s]", + media_format); + + WLog_Print(h264->log, WLOG_DEBUG, "Starting MediaCodec"); + status = sys->fnAMediaCodec_start(sys->decoder); + if (status != AMEDIA_OK) + { + WLog_Print(h264->log, WLOG_ERROR, "AMediaCodec_start failed %d", status); + goto EXCEPTION; + } + + return TRUE; +EXCEPTION: + mediacodec_uninit(h264); + return FALSE; +} + +H264_CONTEXT_SUBSYSTEM g_Subsystem_mediacodec = { "MediaCodec", mediacodec_init, mediacodec_uninit, + mediacodec_decompress, mediacodec_compress }; diff --git a/libfreerdp/codec/h264_openh264.c b/libfreerdp/codec/h264_openh264.c index 5321f30ec14f..9efce29a7b5d 100644 --- a/libfreerdp/codec/h264_openh264.c +++ b/libfreerdp/codec/h264_openh264.c @@ -115,6 +115,21 @@ static int openh264_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 } } + if (state != dsErrorFree) + { + WLog_Print(h264->log, WLOG_WARN, "DecodeFrame2 state: 0x%02X", state); + return -2003; + } + +#if OPENH264_MAJOR >= 2 + state = (*sys->pDecoder)->FlushFrame(sys->pDecoder, pYUVData, &sBufferInfo); + if (state != dsErrorFree) + { + WLog_Print(h264->log, WLOG_WARN, "FlushFrame state: 0x%02X", state); + return -2003; + } +#endif + pSystemBuffer = &sBufferInfo.UsrData.sSystemBuffer; iStride[0] = pSystemBuffer->iStride[0]; iStride[1] = pSystemBuffer->iStride[1]; diff --git a/libfreerdp/codec/h264_x264.c b/libfreerdp/codec/h264_x264.c deleted file mode 100644 index c3600946c9b1..000000000000 --- a/libfreerdp/codec/h264_x264.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * H.264 Bitmap Compression - * - * Copyright 2015 Marc-André Moreau - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define NAL_UNKNOWN X264_NAL_UNKNOWN -#define NAL_SLICE X264_NAL_SLICE -#define NAL_SLICE_DPA X264_NAL_SLICE_DPA -#define NAL_SLICE_DPB X264_NAL_SLICE_DPB -#define NAL_SLICE_DPC X264_NAL_SLICE_DPC -#define NAL_SLICE_IDR X264_NAL_SLICE_IDR -#define NAL_SEI X264_NAL_SEI -#define NAL_SPS X264_NAL_SPS -#define NAL_PPS X264_NAL_PPS -#define NAL_AUD X264_NAL_AUD -#define NAL_FILLER X264_NAL_FILLER - -#define NAL_PRIORITY_DISPOSABLE X264_NAL_PRIORITY_DISPOSABLE -#define NAL_PRIORITY_LOW X264_NAL_PRIORITY_LOW -#define NAL_PRIORITY_HIGH X264_NAL_PRIORITY_HIGH -#define NAL_PRIORITY_HIGHEST X264_NAL_PRIORITY_HIGHEST - -#include -#include -#include - -#error "X264 backend not implemented, please review your configuration!" - -struct _H264_CONTEXT_X264 -{ - void* dummy; -}; -typedef struct _H264_CONTEXT_X264 H264_CONTEXT_X264; - -static int x264_decompress(H264_CONTEXT* h264, BYTE* pSrcData, UINT32 SrcSize) -{ - // H264_CONTEXT_X264* sys = (H264_CONTEXT_X264*) h264->pSystemData; - return -1; -} - -static int x264_compress(H264_CONTEXT* h264, const BYTE** ppSrcYuv, const UINT32* pStride, - BYTE** ppDstData, UINT32* pDstSize) -{ - // H264_CONTEXT_X264* sys = (H264_CONTEXT_X264*) h264->pSystemData; - return -1; -} - -static void x264_uninit(H264_CONTEXT* h264) -{ - H264_CONTEXT_X264* sys = (H264_CONTEXT_X264*)h264->pSystemData; - - if (sys) - { - free(sys); - h264->pSystemData = NULL; - } -} - -static BOOL x264_init(H264_CONTEXT* h264) -{ - H264_CONTEXT_X264* sys; - h264->numSystemData = 1; - sys = (H264_CONTEXT_X264*)calloc(h264->numSystemData, sizeof(H264_CONTEXT_X264)); - - if (!sys) - { - goto EXCEPTION; - } - - h264->pSystemData = (void*)sys; - - if (h264->Compressor) - { - } - else - { - } - - return TRUE; -EXCEPTION: - x264_uninit(h264); - return FALSE; -} - -H264_CONTEXT_SUBSYSTEM g_Subsystem_x264 = { "x264", x264_init, x264_uninit, x264_decompress, - x264_compress }; - -#undef NAL_UNKNOWN -#undef NAL_SLICE -#undef NAL_SLICE_DPA -#undef NAL_SLICE_DPB -#undef NAL_SLICE_DPC -#undef NAL_SLICE_IDR -#undef NAL_SEI -#undef NAL_SPS -#undef NAL_PPS -#undef NAL_AUD -#undef NAL_FILLER - -#undef NAL_PRIORITY_DISPOSABLE -#undef NAL_PRIORITY_LOW -#undef NAL_PRIORITY_HIGH -#undef NAL_PRIORITY_HIGHEST diff --git a/libfreerdp/codec/include/bitmap.c b/libfreerdp/codec/include/bitmap.c index 38bcaa859f88..af8338711e38 100644 --- a/libfreerdp/codec/include/bitmap.c +++ b/libfreerdp/codec/include/bitmap.c @@ -31,7 +31,10 @@ static INLINE BYTE* WRITEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd, UINT32 r BYTE mask = 0x01; if (cBits > 8) + { + WLog_ERR(TAG, "cBits %d > 8", cBits); return NULL; + } if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits)) return NULL; @@ -46,7 +49,6 @@ static INLINE BYTE* WRITEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd, UINT32 r data = xorPixel; DESTWRITEPIXEL(pbDest, data); - DESTNEXTPIXEL(pbDest); mask = mask << 1; }); return pbDest; @@ -62,7 +64,10 @@ static INLINE BYTE* WRITEFIRSTLINEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd, BYTE mask = 0x01; if (cBits > 8) + { + WLog_ERR(TAG, "cBits %d > 8", cBits); return NULL; + } if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits)) return NULL; @@ -76,7 +81,6 @@ static INLINE BYTE* WRITEFIRSTLINEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd, data = BLACK_PIXEL; DESTWRITEPIXEL(pbDest, data); - DESTNEXTPIXEL(pbDest); mask = mask << 1; }); return pbDest; @@ -88,6 +92,9 @@ static INLINE BYTE* WRITEFIRSTLINEFGBGIMAGE(BYTE* pbDest, const BYTE* pbDestEnd, static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BYTE* pbDestBuffer, UINT32 rowDelta, UINT32 width, UINT32 height) { +#if defined(WITH_DEBUG_CODECS) + char sbuffer[128] = { 0 }; +#endif const BYTE* pbSrc = pbSrcBuffer; const BYTE* pbEnd; const BYTE* pbDestEnd; @@ -100,14 +107,22 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY PIXEL pixelA, pixelB; UINT32 runLength; UINT32 code; - UINT32 advance; + UINT32 advance = 0; RLEEXTRA if ((rowDelta == 0) || (rowDelta < width)) + { + WLog_ERR(TAG, "Invalid arguments: rowDelta=%" PRIu32 " == 0 || < width=%" PRIu32, rowDelta, + width); return FALSE; + } if (!pbSrcBuffer || !pbDestBuffer) + { + WLog_ERR(TAG, "Invalid arguments: pbSrcBuffer=%p, pbDestBuffer=%p", pbSrcBuffer, + pbDestBuffer); return FALSE; + } pbEnd = pbSrcBuffer + cbSrcBuffer; pbDestEnd = pbDestBuffer + rowDelta * height; @@ -130,10 +145,17 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY */ code = ExtractCodeId(*pbSrc); +#if defined(WITH_DEBUG_CODECS) + WLog_VRB(TAG, "pbSrc=%p code=%s, rem=%" PRIuz, pbSrc, + rle_code_str_buffer(code, sbuffer, sizeof(sbuffer)), pbEnd - pbSrc); +#endif + /* Handle Background Run Orders. */ - if (code == REGULAR_BG_RUN || code == MEGA_MEGA_BG_RUN) + if ((code == REGULAR_BG_RUN) || (code == MEGA_MEGA_BG_RUN)) { - runLength = ExtractRunLength(code, pbSrc, &advance); + runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance); + if (advance == 0) + return FALSE; pbSrc = pbSrc + advance; if (fFirstLine) @@ -144,17 +166,13 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY return FALSE; DESTWRITEPIXEL(pbDest, fgPel); - DESTNEXTPIXEL(pbDest); runLength = runLength - 1; } if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength)) return FALSE; - UNROLL(runLength, { - DESTWRITEPIXEL(pbDest, BLACK_PIXEL); - DESTNEXTPIXEL(pbDest); - }); + UNROLL(runLength, { DESTWRITEPIXEL(pbDest, BLACK_PIXEL); }); } else { @@ -166,7 +184,6 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY return FALSE; DESTWRITEPIXEL(pbDest, temp ^ fgPel); - DESTNEXTPIXEL(pbDest); runLength--; } @@ -176,7 +193,6 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY UNROLL(runLength, { DESTREADPIXEL(temp, pbDest - rowDelta); DESTWRITEPIXEL(pbDest, temp); - DESTNEXTPIXEL(pbDest); }); } @@ -196,15 +212,16 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY case MEGA_MEGA_FG_RUN: case LITE_SET_FG_FG_RUN: case MEGA_MEGA_SET_FG_RUN: - runLength = ExtractRunLength(code, pbSrc, &advance); + runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance); + if (advance == 0) + return FALSE; pbSrc = pbSrc + advance; if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN) { - if (pbSrc >= pbEnd) + if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd)) return FALSE; SRCREADPIXEL(fgPel, pbSrc); - SRCNEXTPIXEL(pbSrc); } if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength)) @@ -212,17 +229,13 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY if (fFirstLine) { - UNROLL(runLength, { - DESTWRITEPIXEL(pbDest, fgPel); - DESTNEXTPIXEL(pbDest); - }); + UNROLL(runLength, { DESTWRITEPIXEL(pbDest, fgPel); }); } else { UNROLL(runLength, { DESTREADPIXEL(temp, pbDest - rowDelta); DESTWRITEPIXEL(pbDest, temp ^ fgPel); - DESTNEXTPIXEL(pbDest); }); } @@ -231,45 +244,41 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY /* Handle Dithered Run Orders. */ case LITE_DITHERED_RUN: case MEGA_MEGA_DITHERED_RUN: - runLength = ExtractRunLength(code, pbSrc, &advance); + runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance); + if (advance == 0) + return FALSE; pbSrc = pbSrc + advance; - if (pbSrc >= pbEnd) + if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd)) return FALSE; SRCREADPIXEL(pixelA, pbSrc); - SRCNEXTPIXEL(pbSrc); - if (pbSrc >= pbEnd) + if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd)) return FALSE; SRCREADPIXEL(pixelB, pbSrc); - SRCNEXTPIXEL(pbSrc); if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength * 2)) return FALSE; UNROLL(runLength, { DESTWRITEPIXEL(pbDest, pixelA); - DESTNEXTPIXEL(pbDest); DESTWRITEPIXEL(pbDest, pixelB); - DESTNEXTPIXEL(pbDest); }); break; /* Handle Color Run Orders. */ case REGULAR_COLOR_RUN: case MEGA_MEGA_COLOR_RUN: - runLength = ExtractRunLength(code, pbSrc, &advance); + runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance); + if (advance == 0) + return FALSE; pbSrc = pbSrc + advance; - if (pbSrc >= pbEnd) + if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd)) return FALSE; SRCREADPIXEL(pixelA, pbSrc); - SRCNEXTPIXEL(pbSrc); if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength)) return FALSE; - UNROLL(runLength, { - DESTWRITEPIXEL(pbDest, pixelA); - DESTNEXTPIXEL(pbDest); - }); + UNROLL(runLength, { DESTWRITEPIXEL(pbDest, pixelA); }); break; /* Handle Foreground/Background Image Orders. */ @@ -277,17 +286,20 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY case MEGA_MEGA_FGBG_IMAGE: case LITE_SET_FG_FGBG_IMAGE: case MEGA_MEGA_SET_FGBG_IMAGE: - runLength = ExtractRunLength(code, pbSrc, &advance); + runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance); + if (advance == 0) + return FALSE; pbSrc = pbSrc + advance; - if (pbSrc >= pbEnd) - return FALSE; if (code == LITE_SET_FG_FGBG_IMAGE || code == MEGA_MEGA_SET_FGBG_IMAGE) { + if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd)) + return FALSE; SRCREADPIXEL(fgPel, pbSrc); - SRCNEXTPIXEL(pbSrc); } + if (!buffer_within_range(pbSrc, runLength / 8, pbEnd)) + return FALSE; if (fFirstLine) { while (runLength > 8) @@ -306,8 +318,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY { while (runLength > 8) { - bitmask = *pbSrc; - pbSrc = pbSrc + 1; + bitmask = *pbSrc++; + pbDest = WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, bitmask, fgPel, 8); if (!pbDest) @@ -319,8 +331,9 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY if (runLength > 0) { - bitmask = *pbSrc; - pbSrc = pbSrc + 1; + if (!buffer_within_range(pbSrc, 1, pbEnd)) + return FALSE; + bitmask = *pbSrc++; if (fFirstLine) { @@ -342,23 +355,25 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY /* Handle Color Image Orders. */ case REGULAR_COLOR_IMAGE: case MEGA_MEGA_COLOR_IMAGE: - runLength = ExtractRunLength(code, pbSrc, &advance); + runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance); + if (advance == 0) + return FALSE; pbSrc = pbSrc + advance; if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength)) return FALSE; + if (!ENSURE_CAPACITY(pbSrc, pbEnd, runLength)) + return FALSE; UNROLL(runLength, { - if (pbSrc >= pbEnd) - return FALSE; SRCREADPIXEL(temp, pbSrc); - SRCNEXTPIXEL(pbSrc); DESTWRITEPIXEL(pbDest, temp); - DESTNEXTPIXEL(pbDest); }); break; /* Handle Special Order 1. */ case SPECIAL_FGBG_1: + if (!buffer_within_range(pbSrc, 1, pbEnd)) + return FALSE; pbSrc = pbSrc + 1; if (fFirstLine) @@ -379,6 +394,8 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY /* Handle Special Order 2. */ case SPECIAL_FGBG_2: + if (!buffer_within_range(pbSrc, 1, pbEnd)) + return FALSE; pbSrc = pbSrc + 1; if (fFirstLine) @@ -399,27 +416,31 @@ static INLINE BOOL RLEDECOMPRESS(const BYTE* pbSrcBuffer, UINT32 cbSrcBuffer, BY /* Handle White Order. */ case SPECIAL_WHITE: + if (!buffer_within_range(pbSrc, 1, pbEnd)) + return FALSE; pbSrc = pbSrc + 1; if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1)) return FALSE; DESTWRITEPIXEL(pbDest, WHITE_PIXEL); - DESTNEXTPIXEL(pbDest); break; /* Handle Black Order. */ case SPECIAL_BLACK: + if (!buffer_within_range(pbSrc, 1, pbEnd)) + return FALSE; pbSrc = pbSrc + 1; if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1)) return FALSE; DESTWRITEPIXEL(pbDest, BLACK_PIXEL); - DESTNEXTPIXEL(pbDest); break; default: + WLog_ERR(TAG, "invalid code 0x%08" PRIx32 ", pbSrcBuffer=%p, pbSrc=%p, pbEnd=%p", + code, pbSrcBuffer, pbSrc, pbEnd); return FALSE; } } diff --git a/libfreerdp/codec/interleaved.c b/libfreerdp/codec/interleaved.c index b76fe1ca3c1b..e419876e6628 100644 --- a/libfreerdp/codec/interleaved.c +++ b/libfreerdp/codec/interleaved.c @@ -21,6 +21,7 @@ * limitations under the License. */ +#include #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -30,15 +31,16 @@ #define TAG FREERDP_TAG("codec") -#define UNROLL_BODY(_exp, _count) \ - do \ - { \ - size_t x; \ - for (x = 0; x < (_count); x++) \ - { \ - do \ - _exp while (FALSE); \ - } \ +#define UNROLL_BODY(_exp, _count) \ + do \ + { \ + for (size_t x = 0; x < (_count); x++) \ + { \ + do \ + { \ + _exp \ + } while (FALSE); \ + } \ } while (FALSE) #define UNROLL_MULTIPLE(_condition, _exp, _count) \ @@ -97,6 +99,80 @@ static const BYTE g_MaskSpecialFgBg2 = 0x05; static const BYTE g_MaskRegularRunLength = 0x1F; static const BYTE g_MaskLiteRunLength = 0x0F; +static const char* rle_code_str(UINT32 code) +{ + switch (code) + { + case REGULAR_BG_RUN: + return "REGULAR_BG_RUN"; + case MEGA_MEGA_BG_RUN: + return "MEGA_MEGA_BG_RUN"; + case REGULAR_FG_RUN: + return "REGULAR_FG_RUN"; + case MEGA_MEGA_FG_RUN: + return "MEGA_MEGA_FG_RUN"; + case LITE_SET_FG_FG_RUN: + return "LITE_SET_FG_FG_RUN"; + case MEGA_MEGA_SET_FG_RUN: + return "MEGA_MEGA_SET_FG_RUN"; + case LITE_DITHERED_RUN: + return "LITE_DITHERED_RUN"; + case MEGA_MEGA_DITHERED_RUN: + return "MEGA_MEGA_DITHERED_RUN"; + case REGULAR_COLOR_RUN: + return "REGULAR_COLOR_RUN"; + case MEGA_MEGA_COLOR_RUN: + return "MEGA_MEGA_COLOR_RUN"; + case REGULAR_FGBG_IMAGE: + return "REGULAR_FGBG_IMAGE"; + case MEGA_MEGA_FGBG_IMAGE: + return "MEGA_MEGA_FGBG_IMAGE"; + case LITE_SET_FG_FGBG_IMAGE: + return "LITE_SET_FG_FGBG_IMAGE"; + case MEGA_MEGA_SET_FGBG_IMAGE: + return "MEGA_MEGA_SET_FGBG_IMAGE"; + case REGULAR_COLOR_IMAGE: + return "REGULAR_COLOR_IMAGE"; + case MEGA_MEGA_COLOR_IMAGE: + return "MEGA_MEGA_COLOR_IMAGE"; + case SPECIAL_FGBG_1: + return "SPECIAL_FGBG_1"; + case SPECIAL_FGBG_2: + return "SPECIAL_FGBG_2"; + case SPECIAL_WHITE: + return "SPECIAL_WHITE"; + case SPECIAL_BLACK: + return "SPECIAL_BLACK"; + default: + return "UNKNOWN"; + } +} + +static const char* rle_code_str_buffer(UINT32 code, char* buffer, size_t size) +{ + const char* str = rle_code_str(code); + _snprintf(buffer, size, "%s [0x%08" PRIx32 "]", str, code); + return buffer; +} + +#define buffer_within_range(pbSrc, size, pbEnd) \ + buffer_within_range_((pbSrc), (size), (pbEnd), __func__, __FILE__, __LINE__) +static INLINE BOOL buffer_within_range_(const void* pbSrc, size_t size, const void* pbEnd, + const char* fkt, const char* file, size_t line) +{ + WINPR_UNUSED(file); + WINPR_ASSERT(pbSrc); + WINPR_ASSERT(pbEnd); + + if ((const char*)pbSrc + size > (const char*)pbEnd) + { + WLog_ERR(TAG, "[%s:%" PRIuz "] pbSrc=%p + %" PRIuz " > pbEnd=%p", fkt, line, pbSrc, size, + pbEnd); + return FALSE; + } + return TRUE; +} + /** * Reads the supplied order header and extracts the compression * order code ID. @@ -127,71 +203,155 @@ static INLINE UINT32 ExtractCodeId(BYTE bOrderHdr) /** * Extract the run length of a compression order. */ -static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, UINT32* advance) +static UINT ExtractRunLengthRegularFgBg(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) { - UINT32 runLength; - UINT32 ladvance; - ladvance = 1; - runLength = 0; + UINT runLength = 0; - switch (code) + WINPR_ASSERT(pbOrderHdr); + WINPR_ASSERT(pbEnd); + WINPR_ASSERT(advance); + + runLength = (*pbOrderHdr) & g_MaskRegularRunLength; + if (runLength == 0) { - case REGULAR_FGBG_IMAGE: - runLength = (*pbOrderHdr) & g_MaskRegularRunLength; + if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) + { + *advance = 0; + return 0; + } + runLength = *(pbOrderHdr + 1) + 1; + (*advance)++; + } + else + runLength = runLength * 8; - if (runLength == 0) - { - runLength = (*(pbOrderHdr + 1)) + 1; - ladvance += 1; - } - else - { - runLength = runLength * 8; - } + return runLength; +} - break; +static UINT ExtractRunLengthLiteFgBg(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) +{ + UINT runLength = 0; - case LITE_SET_FG_FGBG_IMAGE: - runLength = (*pbOrderHdr) & g_MaskLiteRunLength; + WINPR_ASSERT(pbOrderHdr); + WINPR_ASSERT(pbEnd); + WINPR_ASSERT(advance); - if (runLength == 0) - { - runLength = (*(pbOrderHdr + 1)) + 1; - ladvance += 1; - } - else - { - runLength = runLength * 8; - } + runLength = *pbOrderHdr & g_MaskLiteRunLength; + if (runLength == 0) + { + if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) + { + *advance = 0; + return 0; + } + runLength = *(pbOrderHdr + 1) + 1; + (*advance)++; + } + else + runLength = runLength * 8; + + return runLength; +} + +static UINT ExtractRunLengthRegular(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) +{ + UINT runLength = 0; + + WINPR_ASSERT(pbOrderHdr); + WINPR_ASSERT(pbEnd); + WINPR_ASSERT(advance); + + runLength = *pbOrderHdr & g_MaskRegularRunLength; + if (runLength == 0) + { + if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) + { + *advance = 0; + return 0; + } + runLength = *(pbOrderHdr + 1) + 32; + (*advance)++; + } + + return runLength; +} + +static UINT ExtractRunLengthMegaMega(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) +{ + UINT runLength = 0; + + WINPR_ASSERT(pbOrderHdr); + WINPR_ASSERT(pbEnd); + WINPR_ASSERT(advance); + + if (!buffer_within_range(pbOrderHdr, 3, pbEnd)) + { + *advance = 0; + return 0; + } + + runLength = ((UINT16)pbOrderHdr[1]) | (((UINT16)pbOrderHdr[2]) << 8); + (*advance) += 2; + + return runLength; +} + +static UINT ExtractRunLengthLite(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) +{ + UINT runLength = 0; + + WINPR_ASSERT(pbOrderHdr); + WINPR_ASSERT(pbEnd); + WINPR_ASSERT(advance); + + runLength = *pbOrderHdr & g_MaskLiteRunLength; + if (runLength == 0) + { + if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) + { + *advance = 0; + return 0; + } + runLength = *(pbOrderHdr + 1) + 16; + (*advance)++; + } + return runLength; +} + +static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, const BYTE* pbEnd, + UINT32* advance) +{ + UINT32 runLength = 0; + UINT32 ladvance = 1; + + WINPR_ASSERT(pbOrderHdr); + WINPR_ASSERT(pbEnd); + WINPR_ASSERT(advance); + + *advance = 0; + if (!buffer_within_range(pbOrderHdr, 0, pbEnd)) + return 0; + + switch (code) + { + case REGULAR_FGBG_IMAGE: + runLength = ExtractRunLengthRegularFgBg(pbOrderHdr, pbEnd, &ladvance); + break; + case LITE_SET_FG_FGBG_IMAGE: + runLength = ExtractRunLengthLiteFgBg(pbOrderHdr, pbEnd, &ladvance); break; case REGULAR_BG_RUN: case REGULAR_FG_RUN: case REGULAR_COLOR_RUN: case REGULAR_COLOR_IMAGE: - runLength = (*pbOrderHdr) & g_MaskRegularRunLength; - - if (runLength == 0) - { - /* An extended (MEGA) run. */ - runLength = (*(pbOrderHdr + 1)) + 32; - ladvance += 1; - } - + runLength = ExtractRunLengthRegular(pbOrderHdr, pbEnd, &ladvance); break; case LITE_SET_FG_FG_RUN: case LITE_DITHERED_RUN: - runLength = (*pbOrderHdr) & g_MaskLiteRunLength; - - if (runLength == 0) - { - /* An extended (MEGA) run. */ - runLength = (*(pbOrderHdr + 1)) + 16; - ladvance += 1; - } - + runLength = ExtractRunLengthLite(pbOrderHdr, pbEnd, &ladvance); break; case MEGA_MEGA_BG_RUN: @@ -202,8 +362,12 @@ static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, UINT3 case MEGA_MEGA_FGBG_IMAGE: case MEGA_MEGA_SET_FGBG_IMAGE: case MEGA_MEGA_COLOR_IMAGE: - runLength = ((UINT16)pbOrderHdr[1]) | ((UINT16)(pbOrderHdr[2] << 8)); - ladvance += 2; + runLength = ExtractRunLengthMegaMega(pbOrderHdr, pbEnd, &ladvance); + break; + + default: + runLength = 0; + ladvance = 0; break; } @@ -211,20 +375,32 @@ static INLINE UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, UINT3 return runLength; } -static INLINE BOOL ensure_capacity(const BYTE* start, const BYTE* end, size_t size, size_t base) +#define ensure_capacity(start, end, size, base) \ + ensure_capacity_((start), (end), (size), (base), __func__, __FILE__, __LINE__) +static INLINE BOOL ensure_capacity_(const BYTE* start, const BYTE* end, size_t size, size_t base, + const char* fkt, const char* file, size_t line) { const size_t available = (uintptr_t)end - (uintptr_t)start; const BOOL rc = available >= size * base; - return rc && (start <= end); + const BOOL res = rc && (start <= end); + + if (!res) + WLog_ERR(TAG, + "[%s:%" PRIuz "] failed: start=%p <= end=%p, available=%" PRIuz " >= size=%" PRIuz + " * base=%" PRIuz, + fkt, line, start, end, available, size, base); + return res; } static INLINE void write_pixel_8(BYTE* _buf, BYTE _pix) { + WINPR_ASSERT(_buf); *_buf = _pix; } static INLINE void write_pixel_24(BYTE* _buf, UINT32 _pix) { + WINPR_ASSERT(_buf); (_buf)[0] = (BYTE)(_pix); (_buf)[1] = (BYTE)((_pix) >> 8); (_buf)[2] = (BYTE)((_pix) >> 16); @@ -232,6 +408,7 @@ static INLINE void write_pixel_24(BYTE* _buf, UINT32 _pix) static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix) { + WINPR_ASSERT(_buf); _buf[0] = _pix & 0xFF; _buf[1] = (_pix >> 8) & 0xFF; } @@ -239,19 +416,30 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix) #undef DESTWRITEPIXEL #undef DESTREADPIXEL #undef SRCREADPIXEL -#undef DESTNEXTPIXEL -#undef SRCNEXTPIXEL #undef WRITEFGBGIMAGE #undef WRITEFIRSTLINEFGBGIMAGE #undef RLEDECOMPRESS #undef RLEEXTRA #undef WHITE_PIXEL +#undef PIXEL_SIZE +#undef PIXEL +#define PIXEL_SIZE 1 +#define PIXEL BYTE #define WHITE_PIXEL 0xFF -#define DESTWRITEPIXEL(_buf, _pix) write_pixel_8(_buf, _pix) +#define DESTWRITEPIXEL(_buf, _pix) \ + do \ + { \ + write_pixel_8(_buf, _pix); \ + _buf += 1; \ + } while (0) #define DESTREADPIXEL(_pix, _buf) _pix = (_buf)[0] -#define SRCREADPIXEL(_pix, _buf) _pix = (_buf)[0] -#define DESTNEXTPIXEL(_buf) _buf += 1 -#define SRCNEXTPIXEL(_buf) _buf += 1 +#define SRCREADPIXEL(_pix, _buf) \ + do \ + { \ + _pix = (_buf)[0]; \ + _buf += 1; \ + } while (0) + #define WRITEFGBGIMAGE WriteFgBgImage8to8 #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage8to8 #define RLEDECOMPRESS RleDecompress8to8 @@ -263,19 +451,29 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix) #undef DESTWRITEPIXEL #undef DESTREADPIXEL #undef SRCREADPIXEL -#undef DESTNEXTPIXEL -#undef SRCNEXTPIXEL #undef WRITEFGBGIMAGE #undef WRITEFIRSTLINEFGBGIMAGE #undef RLEDECOMPRESS #undef RLEEXTRA #undef WHITE_PIXEL +#undef PIXEL_SIZE +#undef PIXEL +#define PIXEL_SIZE 2 +#define PIXEL UINT16 #define WHITE_PIXEL 0xFFFF -#define DESTWRITEPIXEL(_buf, _pix) write_pixel_16(_buf, _pix) +#define DESTWRITEPIXEL(_buf, _pix) \ + do \ + { \ + write_pixel_16(_buf, _pix); \ + _buf += 2; \ + } while (0) #define DESTREADPIXEL(_pix, _buf) _pix = ((UINT16*)(_buf))[0] -#define SRCREADPIXEL(_pix, _buf) _pix = (_buf)[0] | ((_buf)[1] << 8) -#define DESTNEXTPIXEL(_buf) _buf += 2 -#define SRCNEXTPIXEL(_buf) _buf += 2 +#define SRCREADPIXEL(_pix, _buf) \ + do \ + { \ + _pix = (_buf)[0] | ((_buf)[1] << 8); \ + _buf += 2; \ + } while (0) #define WRITEFGBGIMAGE WriteFgBgImage16to16 #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage16to16 #define RLEDECOMPRESS RleDecompress16to16 @@ -287,19 +485,30 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix) #undef DESTWRITEPIXEL #undef DESTREADPIXEL #undef SRCREADPIXEL -#undef DESTNEXTPIXEL -#undef SRCNEXTPIXEL #undef WRITEFGBGIMAGE #undef WRITEFIRSTLINEFGBGIMAGE #undef RLEDECOMPRESS #undef RLEEXTRA #undef WHITE_PIXEL -#define WHITE_PIXEL 0xFFFFFF -#define DESTWRITEPIXEL(_buf, _pix) write_pixel_24(_buf, _pix) +#undef PIXEL_SIZE +#undef PIXEL +#define PIXEL_SIZE 3 +#define PIXEL UINT32 +#define WHITE_PIXEL 0xffffff +#define DESTWRITEPIXEL(_buf, _pix) \ + do \ + { \ + write_pixel_24(_buf, _pix); \ + _buf += 3; \ + } while (0) #define DESTREADPIXEL(_pix, _buf) _pix = (_buf)[0] | ((_buf)[1] << 8) | ((_buf)[2] << 16) -#define SRCREADPIXEL(_pix, _buf) _pix = (_buf)[0] | ((_buf)[1] << 8) | ((_buf)[2] << 16) -#define DESTNEXTPIXEL(_buf) _buf += 3 -#define SRCNEXTPIXEL(_buf) _buf += 3 +#define SRCREADPIXEL(_pix, _buf) \ + do \ + { \ + _pix = (_buf)[0] | ((_buf)[1] << 8) | ((_buf)[2] << 16); \ + _buf += 3; \ + } while (0) + #define WRITEFGBGIMAGE WriteFgBgImage24to24 #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage24to24 #define RLEDECOMPRESS RleDecompress24to24 @@ -308,18 +517,32 @@ static INLINE void write_pixel_16(BYTE* _buf, UINT16 _pix) #define ENSURE_CAPACITY(_start, _end, _size) ensure_capacity(_start, _end, _size, 3) #include "include/bitmap.c" +struct S_BITMAP_INTERLEAVED_CONTEXT +{ + BOOL Compressor; + + UINT32 TempSize; + BYTE* TempBuffer; + + wStream* bts; +}; + BOOL interleaved_decompress(BITMAP_INTERLEAVED_CONTEXT* interleaved, const BYTE* pSrcData, UINT32 SrcSize, UINT32 nSrcWidth, UINT32 nSrcHeight, UINT32 bpp, BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nDstWidth, UINT32 nDstHeight, const gdiPalette* palette) { - UINT32 scanline; - UINT32 SrcFormat; - UINT32 BufferSize; + UINT32 scanline = 0; + UINT32 SrcFormat = 0; + UINT32 BufferSize = 0; if (!interleaved || !pSrcData || !pDstData) + { + WLog_ERR(TAG, "invalid arguments: interleaved=%p, pSrcData=%p, pDstData=%p", interleaved, + pSrcData, pDstData); return FALSE; + } switch (bpp) { @@ -352,19 +575,26 @@ BOOL interleaved_decompress(BITMAP_INTERLEAVED_CONTEXT* interleaved, const BYTE* if (BufferSize > interleaved->TempSize) { - interleaved->TempBuffer = _aligned_realloc(interleaved->TempBuffer, BufferSize, 16); + interleaved->TempBuffer = + _aligned_recalloc(interleaved->TempBuffer, BufferSize, sizeof(BYTE), 16); interleaved->TempSize = BufferSize; } if (!interleaved->TempBuffer) + { + WLog_ERR(TAG, "interleaved->TempBuffer=%p", interleaved->TempBuffer); return FALSE; + } switch (bpp) { case 24: if (!RleDecompress24to24(pSrcData, SrcSize, interleaved->TempBuffer, scanline, nSrcWidth, nSrcHeight)) + { + WLog_ERR(TAG, "RleDecompress24to24 failed"); return FALSE; + } break; @@ -372,24 +602,36 @@ BOOL interleaved_decompress(BITMAP_INTERLEAVED_CONTEXT* interleaved, const BYTE* case 15: if (!RleDecompress16to16(pSrcData, SrcSize, interleaved->TempBuffer, scanline, nSrcWidth, nSrcHeight)) + { + WLog_ERR(TAG, "RleDecompress16to16 failed"); return FALSE; + } break; case 8: if (!RleDecompress8to8(pSrcData, SrcSize, interleaved->TempBuffer, scanline, nSrcWidth, nSrcHeight)) + { + WLog_ERR(TAG, "RleDecompress8to8 failed"); return FALSE; + } break; default: + WLog_ERR(TAG, "Invalid color depth %" PRIu32 "", bpp); return FALSE; } - return freerdp_image_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth, nDstHeight, - interleaved->TempBuffer, SrcFormat, scanline, 0, 0, palette, - FREERDP_FLIP_VERTICAL); + if (!freerdp_image_copy(pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth, nDstHeight, + interleaved->TempBuffer, SrcFormat, scanline, 0, 0, palette, + FREERDP_FLIP_VERTICAL)) + { + WLog_ERR(TAG, "freerdp_image_copy failed"); + return FALSE; + } + return TRUE; } BOOL interleaved_compress(BITMAP_INTERLEAVED_CONTEXT* interleaved, BYTE* pDstData, UINT32* pDstSize, @@ -397,10 +639,10 @@ BOOL interleaved_compress(BITMAP_INTERLEAVED_CONTEXT* interleaved, BYTE* pDstDat UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, const gdiPalette* palette, UINT32 bpp) { - BOOL status; - wStream* s; + BOOL status = 0; + wStream* s = NULL; UINT32 DstFormat = 0; - const size_t maxSize = 64 * 64 * 4; + const UINT32 maxSize = 64 * 64 * 4; if (!interleaved || !pDstData || !pSrcData) return FALSE; @@ -442,7 +684,7 @@ BOOL interleaved_compress(BITMAP_INTERLEAVED_CONTEXT* interleaved, BYTE* pDstDat } if (!freerdp_image_copy(interleaved->TempBuffer, DstFormat, 0, 0, 0, nWidth, nHeight, pSrcData, - SrcFormat, nSrcStep, nXSrc, nYSrc, palette, FREERDP_FLIP_NONE)) + SrcFormat, nSrcStep, nXSrc, nYSrc, palette, 0)) return FALSE; s = Stream_New(pDstData, *pDstSize); @@ -474,33 +716,29 @@ BOOL bitmap_interleaved_context_reset(BITMAP_INTERLEAVED_CONTEXT* interleaved) BITMAP_INTERLEAVED_CONTEXT* bitmap_interleaved_context_new(BOOL Compressor) { - BITMAP_INTERLEAVED_CONTEXT* interleaved; - interleaved = (BITMAP_INTERLEAVED_CONTEXT*)calloc(1, sizeof(BITMAP_INTERLEAVED_CONTEXT)); + BITMAP_INTERLEAVED_CONTEXT* interleaved = NULL; + interleaved = (BITMAP_INTERLEAVED_CONTEXT*)_aligned_recalloc( + NULL, 1, sizeof(BITMAP_INTERLEAVED_CONTEXT), 32); if (interleaved) { interleaved->TempSize = 64 * 64 * 4; - interleaved->TempBuffer = _aligned_malloc(interleaved->TempSize, 16); + interleaved->TempBuffer = _aligned_malloc(interleaved->TempSize * sizeof(BYTE), 16); if (!interleaved->TempBuffer) - { - free(interleaved); - WLog_ERR(TAG, "_aligned_malloc failed!"); - return NULL; - } + goto fail; interleaved->bts = Stream_New(NULL, interleaved->TempSize); if (!interleaved->bts) - { - _aligned_free(interleaved->TempBuffer); - free(interleaved); - WLog_ERR(TAG, "Stream_New failed!"); - return NULL; - } + goto fail; } return interleaved; + +fail: + bitmap_interleaved_context_free(interleaved); + return NULL; } void bitmap_interleaved_context_free(BITMAP_INTERLEAVED_CONTEXT* interleaved) @@ -510,5 +748,5 @@ void bitmap_interleaved_context_free(BITMAP_INTERLEAVED_CONTEXT* interleaved) _aligned_free(interleaved->TempBuffer); Stream_Free(interleaved->bts, TRUE); - free(interleaved); + _aligned_free(interleaved); } diff --git a/libfreerdp/codec/ncrush.c b/libfreerdp/codec/ncrush.c index 3d6a216d32f4..199f1ed7c488 100644 --- a/libfreerdp/codec/ncrush.c +++ b/libfreerdp/codec/ncrush.c @@ -1994,15 +1994,9 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY UINT32* pDstSize, UINT32 flags) { UINT32 index; - UINT32 bits; - INT32 nbits; - const BYTE* SrcPtr; - const BYTE* SrcEnd; - UINT16 Mask; BYTE Literal; UINT32 IndexLEC; UINT32 BitLength; - UINT32 MaskedBits; UINT32 CopyOffset; UINT32 CopyLength; UINT32 OldCopyOffset; @@ -2010,9 +2004,6 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY UINT32 LengthOfMatch; UINT32 CopyOffsetIndex; UINT32 OffsetCacheIndex; - BYTE* HistoryPtr; - BYTE* HistoryBuffer; - BYTE* HistoryBufferEnd; UINT32 CopyOffsetBits; UINT32 CopyOffsetBase; UINT32 LengthOfMatchBits; @@ -2021,8 +2012,8 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY if (ncrush->HistoryEndOffset != 65535) return -1001; - HistoryBuffer = ncrush->HistoryBuffer; - HistoryBufferEnd = &HistoryBuffer[ncrush->HistoryEndOffset]; + BYTE* HistoryBuffer = ncrush->HistoryBuffer; + const BYTE* HistoryBufferEnd = &HistoryBuffer[ncrush->HistoryEndOffset]; if (flags & PACKET_AT_FRONT) { @@ -2041,7 +2032,7 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY ZeroMemory(&(ncrush->OffsetCache), sizeof(ncrush->OffsetCache)); } - HistoryPtr = ncrush->HistoryPtr; + BYTE* HistoryPtr = ncrush->HistoryPtr; if (!(flags & PACKET_COMPRESSED)) { @@ -2050,17 +2041,25 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY return 1; } - SrcEnd = &pSrcData[SrcSize]; - nbits = 32; - bits = get_dword(pSrcData); - SrcPtr = pSrcData + 4; + if (SrcSize < 4) + { + WLog_ERR(TAG, "Input size short: SrcSize %" PRIu32 " < 4", SrcSize); + return -1; + } + + const BYTE* SrcEnd = &pSrcData[SrcSize]; + const BYTE* SrcPtr = pSrcData + 4; + INT32 nbits = 32; + UINT32 bits = get_dword(pSrcData); while (1) { while (1) { - Mask = get_word(&HuffTableMask[29]); - MaskedBits = bits & Mask; + const UINT16 Mask = get_word(&HuffTableMask[29]); + const UINT32 MaskedBits = bits & Mask; + if (MaskedBits >= ARRAYSIZE(HuffTableLEC)) + return -1; IndexLEC = HuffTableLEC[MaskedBits] & 0xFFF; BitLength = HuffTableLEC[MaskedBits] >> 12; bits >>= BitLength; @@ -2096,8 +2095,10 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY return -1004; CopyOffset = ncrush->OffsetCache[OffsetCacheIndex]; - Mask = get_word(&HuffTableMask[21]); - MaskedBits = bits & Mask; + const UINT16 Mask = get_word(&HuffTableMask[21]); + const UINT32 MaskedBits = bits & Mask; + if (MaskedBits > ARRAYSIZE(HuffTableLOM)) + return -1; LengthOfMatch = HuffTableLOM[MaskedBits] & 0xFFF; BitLength = HuffTableLOM[MaskedBits] >> 12; bits >>= BitLength; @@ -2106,13 +2107,23 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY if (!NCrushFetchBits(&SrcPtr, &SrcEnd, &nbits, &bits)) return -1; + if (LengthOfMatch >= ARRAYSIZE(LOMBitsLUT)) + return -1; + LengthOfMatchBits = LOMBitsLUT[LengthOfMatch]; + + if (LengthOfMatch >= ARRAYSIZE(LOMBaseLUT)) + return -1; LengthOfMatchBase = LOMBaseLUT[LengthOfMatch]; if (LengthOfMatchBits) { - Mask = get_word(&HuffTableMask[(2 * LengthOfMatchBits) + 3]); - MaskedBits = bits & Mask; + const size_t idx = (2ull * LengthOfMatchBits) + 3ull; + if (idx >= ARRAYSIZE(HuffTableMask)) + return -1; + + const UINT16 Mask = get_word(&HuffTableMask[idx]); + const UINT32 MaskedBits = bits & Mask; bits >>= LengthOfMatchBits; nbits -= LengthOfMatchBits; LengthOfMatchBase += MaskedBits; @@ -2127,15 +2138,28 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY } else { + if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBitsLUT)) + return -1; + CopyOffsetBits = CopyOffsetBitsLUT[CopyOffsetIndex]; + + if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBaseLUT)) + return -1; CopyOffsetBase = CopyOffsetBaseLUT[CopyOffsetIndex]; CopyOffset = CopyOffsetBase - 1; if (CopyOffsetBits) { - Mask = get_word(&HuffTableMask[(2 * CopyOffsetBits) + 3]); - MaskedBits = bits & Mask; - CopyOffset = CopyOffsetBase + MaskedBits - 1; + const size_t idx = (2ull * CopyOffsetBits) + 3ull; + if (idx >= ARRAYSIZE(HuffTableMask)) + return -1; + + const UINT16 Mask = get_word(&HuffTableMask[idx]); + const UINT32 MaskedBits = bits & Mask; + const UINT32 tmp = CopyOffsetBase + MaskedBits; + if (tmp < 1) + return -1; + CopyOffset = tmp - 1; bits >>= CopyOffsetBits; nbits -= CopyOffsetBits; @@ -2143,8 +2167,11 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY return -1; } - Mask = get_word(&HuffTableMask[21]); - MaskedBits = bits & Mask; + const UINT16 Mask = get_word(&HuffTableMask[21]); + const UINT32 MaskedBits = bits & Mask; + if (MaskedBits >= ARRAYSIZE(HuffTableLOM)) + return -1; + LengthOfMatch = HuffTableLOM[MaskedBits] & 0xFFF; BitLength = HuffTableLOM[MaskedBits] >> 12; bits >>= BitLength; @@ -2153,13 +2180,23 @@ int ncrush_decompress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BY if (!NCrushFetchBits(&SrcPtr, &SrcEnd, &nbits, &bits)) return -1; + if (LengthOfMatch >= ARRAYSIZE(LOMBitsLUT)) + return -1; + LengthOfMatchBits = LOMBitsLUT[LengthOfMatch]; + + if (LengthOfMatch >= ARRAYSIZE(LOMBaseLUT)) + return -1; LengthOfMatchBase = LOMBaseLUT[LengthOfMatch]; if (LengthOfMatchBits) { - Mask = get_word(&HuffTableMask[(2 * LengthOfMatchBits) + 3]); - MaskedBits = bits & Mask; + const size_t idx = (2ull * LengthOfMatchBits) + 3ull; + if (idx >= ARRAYSIZE(HuffTableMask)) + return -1; + + const UINT16 Mask = get_word(&HuffTableMask[idx]); + const UINT32 MaskedBits = bits & Mask; bits >>= LengthOfMatchBits; nbits -= LengthOfMatchBits; LengthOfMatchBase += MaskedBits; @@ -2583,7 +2620,12 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE } IndexLEC = Literal; + if (IndexLEC >= ARRAYSIZE(HuffLengthLEC)) + return -1; BitLength = HuffLengthLEC[IndexLEC]; + + if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC)) + return -1; CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]); if (BitLength > 15) @@ -2666,9 +2708,18 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE bits = CopyOffset; CopyOffsetIndex = ncrush->HuffTableCopyOffset[bits + 2]; + + if (CopyOffsetIndex >= ARRAYSIZE(CopyOffsetBitsLUT)) + return -1; + CopyOffsetBits = CopyOffsetBitsLUT[CopyOffsetIndex]; IndexLEC = 257 + CopyOffsetIndex; + if (IndexLEC >= ARRAYSIZE(HuffLengthLEC)) + return -1; BitLength = HuffLengthLEC[IndexLEC]; + + if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC)) + return -1; CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]); if (BitLength > 15) @@ -2687,13 +2738,23 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE else IndexCO = ncrush->HuffTableLOM[MatchLength]; + if (IndexCO >= ARRAYSIZE(HuffLengthLOM)) + return -1; BitLength = HuffLengthLOM[IndexCO]; + + if (IndexCO >= ARRAYSIZE(LOMBitsLUT)) + return -1; IndexLOM = LOMBitsLUT[IndexCO]; + + if (IndexCO >= ARRAYSIZE(HuffCodeLOM)) + return -1; NCrushWriteBits(&DstPtr, &accumulator, &offset, HuffCodeLOM[IndexCO], BitLength); Mask = ((1 << IndexLOM) - 1); MaskedBits = (MatchLength - 2) & Mask; NCrushWriteBits(&DstPtr, &accumulator, &offset, MaskedBits, IndexLOM); + if (IndexCO >= ARRAYSIZE(LOMBaseLUT)) + return -1; if ((MaskedBits + LOMBaseLUT[IndexCO]) != MatchLength) return -1010; } @@ -2701,7 +2762,11 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE { /* CopyOffset in OffsetCache */ IndexLEC = 289 + OffsetCacheIndex; + if (IndexLEC >= ARRAYSIZE(HuffLengthLEC)) + return -1; BitLength = HuffLengthLEC[IndexLEC]; + if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC)) + return -1; CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]); if (BitLength >= 15) @@ -2714,13 +2779,24 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE else IndexCO = ncrush->HuffTableLOM[MatchLength]; + if (IndexCO >= ARRAYSIZE(HuffLengthLOM)) + return -1; + BitLength = HuffLengthLOM[IndexCO]; + + if (IndexCO >= ARRAYSIZE(LOMBitsLUT)) + return -1; IndexLOM = LOMBitsLUT[IndexCO]; + + if (IndexCO >= ARRAYSIZE(HuffCodeLOM)) + return -1; NCrushWriteBits(&DstPtr, &accumulator, &offset, HuffCodeLOM[IndexCO], BitLength); Mask = ((1 << IndexLOM) - 1); MaskedBits = (MatchLength - 2) & Mask; NCrushWriteBits(&DstPtr, &accumulator, &offset, MaskedBits, IndexLOM); + if (IndexCO >= ARRAYSIZE(LOMBaseLUT)) + return -1; if ((MaskedBits + LOMBaseLUT[IndexCO]) != MatchLength) return -1012; } @@ -2745,6 +2821,10 @@ int ncrush_compress(NCRUSH_CONTEXT* ncrush, BYTE* pSrcData, UINT32 SrcSize, BYTE Literal = *SrcPtr++; HistoryPtr++; IndexLEC = Literal; + if (IndexLEC >= ARRAYSIZE(HuffLengthLEC)) + return -1; + if (IndexLEC * 2ull >= ARRAYSIZE(HuffCodeLEC)) + return -1; BitLength = HuffLengthLEC[IndexLEC]; CodeLEC = get_word(&HuffCodeLEC[IndexLEC * 2]); @@ -2817,6 +2897,11 @@ static int ncrush_generate_tables(NCRUSH_CONTEXT* context) else i = context->HuffTableLOM[k]; + if (i >= ARRAYSIZE(LOMBitsLUT)) + return -1; + if (i >= ARRAYSIZE(LOMBaseLUT)) + return -1; + if (((((1 << LOMBitsLUT[i]) - 1) & (k - 2)) + LOMBaseLUT[i]) != k) return -1; } diff --git a/libfreerdp/codec/nsc.c b/libfreerdp/codec/nsc.c index dc3a978ed243..ed7ef083dae1 100644 --- a/libfreerdp/codec/nsc.c +++ b/libfreerdp/codec/nsc.c @@ -29,6 +29,8 @@ #include #include +#include +#include #include #include @@ -38,6 +40,12 @@ #include "nsc_sse2.h" +#if !defined(Stream_CheckAndLogRequiredLengthWLog) +#define Stream_CheckAndLogRequiredLengthWLog(log, s, len) \ + Stream_CheckAndLogRequiredLengthWLogEx(log, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \ + __FILE__, __LINE__) +#endif + #ifndef NSC_INIT_SIMD #define NSC_INIT_SIMD(_nsc_context) \ do \ @@ -87,8 +95,8 @@ static BOOL nsc_decode(NSC_CONTEXT* context) for (x = 0; x < context->width; x++) { INT16 y_val = (INT16)*yplane; - INT16 co_val = (INT16)(INT8)(*coplane << shift); - INT16 cg_val = (INT16)(INT8)(*cgplane << shift); + INT16 co_val = (INT16)(INT8)(((INT16)*coplane) << shift); + INT16 cg_val = (INT16)(INT8)(((INT16)*cgplane) << shift); INT16 r_val = y_val + co_val - cg_val; INT16 g_val = y_val + cg_val; INT16 b_val = y_val - co_val - cg_val; @@ -111,12 +119,17 @@ static BOOL nsc_decode(NSC_CONTEXT* context) return TRUE; } -static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalSize) +static BOOL nsc_rle_decode(const BYTE* in, size_t inSize, BYTE* out, UINT32 outSize, + UINT32 originalSize) { UINT32 left = originalSize; while (left > 4) { + if (inSize < 1) + return FALSE; + inSize--; + const BYTE value = *in++; UINT32 len = 0; @@ -129,17 +142,26 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS *out++ = value; left--; } + else if (inSize < 1) + return FALSE; else if (value == *in) { + inSize--; in++; - if (*in < 0xFF) + if (inSize < 1) + return FALSE; + else if (*in < 0xFF) { + inSize--; len = (UINT32)*in++; len += 2; } else { + if (inSize < 5) + return FALSE; + inSize -= 5; in++; len = ((UINT32)(*in++)); len |= ((UINT32)(*in++)) << 8U; @@ -147,7 +169,7 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS len |= ((UINT32)(*in++)) << 24U; } - if (outSize < len) + if ((outSize < len) || (left < len)) return FALSE; outSize -= len; @@ -169,26 +191,28 @@ static BOOL nsc_rle_decode(BYTE* in, BYTE* out, UINT32 outSize, UINT32 originalS if ((outSize < 4) || (left < 4)) return FALSE; + if (inSize < 4) + return FALSE; memcpy(out, in, 4); return TRUE; } static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context) { - UINT16 i; - BYTE* rle; - UINT32 planeSize; - UINT32 originalSize; - if (!context) return FALSE; - rle = context->Planes; + const BYTE* rle = context->Planes; + size_t rleSize = context->PlanesSize; + WINPR_ASSERT(rle); - for (i = 0; i < 4; i++) + for (size_t i = 0; i < 4; i++) { - originalSize = context->OrgByteCount[i]; - planeSize = context->PlaneByteCount[i]; + const UINT32 originalSize = context->OrgByteCount[i]; + const UINT32 planeSize = context->PlaneByteCount[i]; + + if (rleSize < planeSize) + return FALSE; if (planeSize == 0) { @@ -199,7 +223,7 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context) } else if (planeSize < originalSize) { - if (!nsc_rle_decode(rle, context->priv->PlaneBuffers[i], + if (!nsc_rle_decode(rle, rleSize, context->priv->PlaneBuffers[i], context->priv->PlaneBuffersLength, originalSize)) return FALSE; } @@ -208,6 +232,9 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context) if (context->priv->PlaneBuffersLength < originalSize) return FALSE; + if (rleSize < originalSize) + return FALSE; + CopyMemory(context->priv->PlaneBuffers[i], rle, originalSize); } @@ -219,64 +246,71 @@ static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context) static BOOL nsc_stream_initialize(NSC_CONTEXT* context, wStream* s) { - int i; - - if (Stream_GetRemainingLength(s) < 20) + WINPR_ASSERT(context); + WINPR_ASSERT(context->priv); + if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 20)) return FALSE; - for (i = 0; i < 4; i++) + size_t total = 0; + for (size_t i = 0; i < 4; i++) + { Stream_Read_UINT32(s, context->PlaneByteCount[i]); + total += context->PlaneByteCount[i]; + } Stream_Read_UINT8(s, context->ColorLossLevel); /* ColorLossLevel (1 byte) */ + if ((context->ColorLossLevel < 1) || (context->ColorLossLevel > 7)) + { + WLog_Print(context->priv->log, WLOG_ERROR, + "ColorLossLevel=%" PRIu8 " out of range, must be [1,7] inclusive", + context->ColorLossLevel); + return FALSE; + } Stream_Read_UINT8(s, context->ChromaSubsamplingLevel); /* ChromaSubsamplingLevel (1 byte) */ Stream_Seek(s, 2); /* Reserved (2 bytes) */ context->Planes = Stream_Pointer(s); - return TRUE; + context->PlanesSize = total; + return Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, total); } static BOOL nsc_context_initialize(NSC_CONTEXT* context, wStream* s) { - int i; - UINT32 length; - UINT32 tempWidth; - UINT32 tempHeight; - if (!nsc_stream_initialize(context, s)) return FALSE; - length = context->width * context->height * 4; + const size_t blength = context->width * context->height * 4ull; if (!context->BitmapData) { - context->BitmapData = calloc(1, length + 16); + context->BitmapData = calloc(1, blength + 16); if (!context->BitmapData) return FALSE; - context->BitmapDataLength = length; + context->BitmapDataLength = blength; } - else if (length > context->BitmapDataLength) + else if (blength > context->BitmapDataLength) { void* tmp; - tmp = realloc(context->BitmapData, length + 16); + tmp = realloc(context->BitmapData, blength + 16); if (!tmp) return FALSE; context->BitmapData = tmp; - context->BitmapDataLength = length; + context->BitmapDataLength = blength; } - tempWidth = ROUND_UP_TO(context->width, 8); - tempHeight = ROUND_UP_TO(context->height, 2); + const UINT32 tempWidth = ROUND_UP_TO(context->width, 8); + const UINT32 tempHeight = ROUND_UP_TO(context->height, 2); /* The maximum length a decoded plane can reach in all cases */ - length = tempWidth * tempHeight; + const size_t plength = 1ull * tempWidth * tempHeight; - if (length > context->priv->PlaneBuffersLength) + if (plength > context->priv->PlaneBuffersLength) { - for (i = 0; i < 4; i++) + for (size_t i = 0; i < 4; i++) { - void* tmp = (BYTE*)realloc(context->priv->PlaneBuffers[i], length); + void* tmp = (BYTE*)realloc(context->priv->PlaneBuffers[i], plength); if (!tmp) return FALSE; @@ -284,13 +318,11 @@ static BOOL nsc_context_initialize(NSC_CONTEXT* context, wStream* s) context->priv->PlaneBuffers[i] = tmp; } - context->priv->PlaneBuffersLength = length; + context->priv->PlaneBuffersLength = plength; } - for (i = 0; i < 4; i++) - { + for (size_t i = 0; i < 4; i++) context->OrgByteCount[i] = context->width * context->height; - } if (context->ChromaSubsamplingLevel) { @@ -419,13 +451,20 @@ BOOL nsc_process_message(NSC_CONTEXT* context, UINT16 bpp, UINT32 width, UINT32 if (!context || !data || !pDstData) return FALSE; - s = Stream_New((BYTE*)data, length); + if (nXDst > nWidth) + return FALSE; + if (nYDst > nHeight) + return FALSE; + s = Stream_New((BYTE*)data, length); if (!s) return FALSE; + const UINT32 minStride = nWidth * GetBytesPerPixel(DstFormat); if (nDstStride == 0) - nDstStride = nWidth * GetBytesPerPixel(DstFormat); + nDstStride = minStride; + if (nDstStride < minStride) + return FALSE; switch (bpp) { @@ -483,7 +522,15 @@ BOOL nsc_process_message(NSC_CONTEXT* context, UINT16 bpp, UINT32 width, UINT32 return FALSE; } - if (!freerdp_image_copy(pDstData, DstFormat, nDstStride, nXDst, nYDst, width, height, + UINT32 cwidth = width; + if (1ull * nXDst + width > nWidth) + cwidth = nWidth - nXDst; + + UINT32 cheight = height; + if (1ull * nYDst + height > nHeight) + cheight = nHeight - nYDst; + + if (!freerdp_image_copy(pDstData, DstFormat, nDstStride, nXDst, nYDst, cwidth, cheight, context->BitmapData, PIXEL_FORMAT_BGRA32, 0, 0, 0, NULL, flip)) return FALSE; diff --git a/libfreerdp/codec/nsc_encode.c b/libfreerdp/codec/nsc_encode.c index fd9626a6d2ca..477318cc191c 100644 --- a/libfreerdp/codec/nsc_encode.c +++ b/libfreerdp/codec/nsc_encode.c @@ -119,6 +119,8 @@ static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data, UI UINT16 rw; BYTE ccl; const BYTE* src; + const UINT32* src_32; + const UINT16* src_16; BYTE* yplane = NULL; BYTE* coplane = NULL; BYTE* cgplane = NULL; @@ -140,69 +142,85 @@ static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data, UI coplane = context->priv->PlaneBuffers[1] + y * rw; cgplane = context->priv->PlaneBuffers[2] + y * rw; aplane = context->priv->PlaneBuffers[3] + y * context->width; + src_32 = (UINT32*)src; + src_16 = (UINT16*)src; for (x = 0; x < context->width; x++) { switch (context->format) { case PIXEL_FORMAT_BGRX32: - b_val = *src++; - g_val = *src++; - r_val = *src++; - src++; + b_val = (INT16)(*src_32 & 0xFF); + g_val = (INT16)((*src_32 >> 8) & 0xFF); + r_val = (INT16)((*src_32 >> 16) & 0xFF); a_val = 0xFF; + src_32++; break; case PIXEL_FORMAT_BGRA32: - b_val = *src++; - g_val = *src++; - r_val = *src++; - a_val = *src++; + b_val = (INT16)(*src_32 & 0xFF); + g_val = (INT16)((*src_32 >> 8) & 0xFF); + r_val = (INT16)((*src_32 >> 16) & 0xFF); + a_val = (INT16)((*src_32 >> 24) & 0xFF); + src_32++; break; case PIXEL_FORMAT_RGBX32: - r_val = *src++; - g_val = *src++; - b_val = *src++; - src++; + r_val = (INT16)(*src_32 & 0xFF); + g_val = (INT16)((*src_32 >> 8) & 0xFF); + b_val = (INT16)((*src_32 >> 16) & 0xFF); a_val = 0xFF; + src_32++; break; case PIXEL_FORMAT_RGBA32: - r_val = *src++; - g_val = *src++; - b_val = *src++; - a_val = *src++; + r_val = (INT16)(*src_32 & 0xFF); + g_val = (INT16)((*src_32 >> 8) & 0xFF); + b_val = (INT16)((*src_32 >> 16) & 0xFF); + a_val = (INT16)((*src_32 >> 24) & 0xFF); + src_32++; break; case PIXEL_FORMAT_BGR24: +#ifdef __LITTLE_ENDIAN__ b_val = *src++; g_val = *src++; r_val = *src++; +#else + r_val = *src++; + g_val = *src++; + b_val = *src++; +#endif a_val = 0xFF; break; case PIXEL_FORMAT_RGB24: +#ifdef __LITTLE_ENDIAN__ r_val = *src++; g_val = *src++; b_val = *src++; +#else + b_val = *src++; + g_val = *src++; + r_val = *src++; +#endif a_val = 0xFF; break; case PIXEL_FORMAT_BGR16: - b_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5)); - g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3)); - r_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07)); + b_val = (INT16)((*src_16) & 0x1F); + g_val = (INT16)((*src_16 >> 5) & 0x3F); + r_val = (INT16)((*src_16 >> 11) & 0x1F); a_val = 0xFF; - src += 2; + src_16++; break; case PIXEL_FORMAT_RGB16: - r_val = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5)); - g_val = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3)); - b_val = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07)); + r_val = (INT16)((*src_16) & 0x1F); + g_val = (INT16)((*src_16 >> 5) & 0x3F); + b_val = (INT16)((*src_16 >> 11) & 0x1F); a_val = 0xFF; - src += 2; + src_16++; break; case PIXEL_FORMAT_A4: @@ -210,17 +228,17 @@ static BOOL nsc_encode_argb_to_aycocg(NSC_CONTEXT* context, const BYTE* data, UI int shift; BYTE idx; shift = (7 - (x % 8)); - idx = ((*src) >> shift) & 1; - idx |= (((*(src + 1)) >> shift) & 1) << 1; - idx |= (((*(src + 2)) >> shift) & 1) << 2; - idx |= (((*(src + 3)) >> shift) & 1) << 3; + idx = (BYTE)(((*src_32 & 0xFF) >> shift) & 1); + idx |= (BYTE)(((((*src_32 >> 8) & 0xFF) >> shift) & 1) << 1); + idx |= (BYTE)(((((*src_32 >> 16) & 0xFF) >> shift) & 1) << 2); + idx |= (BYTE)(((((*src_32 >> 24) & 0xFF) >> shift) & 1) << 3); idx *= 3; r_val = (INT16)context->palette[idx]; g_val = (INT16)context->palette[idx + 1]; b_val = (INT16)context->palette[idx + 2]; if (shift == 0) - src += 4; + src_32++; } a_val = 0xFF; diff --git a/libfreerdp/codec/nsc_types.h b/libfreerdp/codec/nsc_types.h index 14c1a946bcd1..5a6506baea5a 100644 --- a/libfreerdp/codec/nsc_types.h +++ b/libfreerdp/codec/nsc_types.h @@ -61,6 +61,7 @@ struct _NSC_CONTEXT UINT32 BitmapDataLength; BYTE* Planes; + size_t PlanesSize; UINT32 PlaneByteCount[4]; UINT32 ColorLossLevel; UINT32 ChromaSubsamplingLevel; diff --git a/libfreerdp/codec/planar.c b/libfreerdp/codec/planar.c index 12b9f6daf15c..c728dd8ea507 100644 --- a/libfreerdp/codec/planar.c +++ b/libfreerdp/codec/planar.c @@ -33,7 +33,8 @@ #define TAG FREERDP_TAG("codec") -#define ALIGN(val, align) ((val) % (align) == 0) ? (val) : ((val) + (align) - (val) % (align)) +#define PLANAR_ALIGN(val, align) \ + ((val) % (align) == 0) ? (val) : ((val) + (align) - (val) % (align)) static INLINE UINT32 planar_invert_format(BITMAP_PLANAR_CONTEXT* planar, BOOL alpha, UINT32 DstFormat) @@ -612,11 +613,25 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT const UINT32 h = MIN(nSrcHeight, nDstHeight); const primitives_t* prims = primitives_get(); + WINPR_ASSERT(planar); + WINPR_ASSERT(prims); + + if (planar->maxWidth < nSrcWidth) + return FALSE; + if (planar->maxHeight < nSrcHeight) + return FALSE; + if (nDstStep <= 0) nDstStep = nDstWidth * GetBytesPerPixel(DstFormat); srcp = pSrcData; + if (!pSrcData || (SrcSize < 1)) + { + WLog_ERR(TAG, "Invalid argument pSrcData=%p [size=%" PRIu32 "]", pSrcData, SrcSize); + return FALSE; + } + if (!pDstData) { WLog_ERR(TAG, "Invalid argument pDstData=NULL"); @@ -679,6 +694,13 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT rawHeights[3] = nSrcHeight; } + const size_t diff = srcp - pSrcData; + if (SrcSize < diff) + { + WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff); + return FALSE; + } + if (!rle) /* RAW */ { UINT32 base = planeSize * 3; @@ -687,8 +709,12 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT if (alpha) { - if ((SrcSize - (srcp - pSrcData)) < (planeSize + base)) + if ((SrcSize - diff) < (planeSize + base)) + { + WLog_ERR(TAG, "Alpha plane size mismatch %" PRIuz " < %" PRIu32, SrcSize - diff, + (planeSize + base)); return FALSE; + } planes[3] = srcp; /* AlphaPlane */ planes[0] = planes[3] + rawSizes[3]; /* LumaOrRedPlane */ @@ -700,8 +726,11 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT } else { - if ((SrcSize - (srcp - pSrcData)) < base) + if ((SrcSize - diff) < base) + { + WLog_ERR(TAG, "plane size mismatch %" PRIu32 " < %" PRIu32, SrcSize - diff, base); return FALSE; + } planes[0] = srcp; /* LumaOrRedPlane */ planes[1] = planes[0] + rawSizes[0]; /* OrangeChromaOrGreenPlane */ @@ -716,8 +745,8 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT if (alpha) { planes[3] = srcp; - rleSizes[3] = planar_skip_plane_rle(planes[3], SrcSize - (planes[3] - pSrcData), - rawWidths[3], rawHeights[3]); /* AlphaPlane */ + rleSizes[3] = planar_skip_plane_rle(planes[3], SrcSize - diff, rawWidths[3], + rawHeights[3]); /* AlphaPlane */ if (rleSizes[3] < 0) return FALSE; @@ -727,22 +756,41 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT else planes[0] = srcp; - rleSizes[0] = planar_skip_plane_rle(planes[0], SrcSize - (planes[0] - pSrcData), - rawWidths[0], rawHeights[0]); /* RedPlane */ + const size_t diff0 = (planes[0] - pSrcData); + if (SrcSize < diff0) + { + WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff0); + return FALSE; + } + rleSizes[0] = planar_skip_plane_rle(planes[0], SrcSize - diff0, rawWidths[0], + rawHeights[0]); /* RedPlane */ if (rleSizes[0] < 0) return FALSE; planes[1] = planes[0] + rleSizes[0]; - rleSizes[1] = planar_skip_plane_rle(planes[1], SrcSize - (planes[1] - pSrcData), - rawWidths[1], rawHeights[1]); /* GreenPlane */ + + const size_t diff1 = (planes[1] - pSrcData); + if (SrcSize < diff1) + { + WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff1); + return FALSE; + } + rleSizes[1] = planar_skip_plane_rle(planes[1], SrcSize - diff1, rawWidths[1], + rawHeights[1]); /* GreenPlane */ if (rleSizes[1] < 1) return FALSE; planes[2] = planes[1] + rleSizes[1]; - rleSizes[2] = planar_skip_plane_rle(planes[2], SrcSize - (planes[2] - pSrcData), - rawWidths[2], rawHeights[2]); /* BluePlane */ + const size_t diff2 = (planes[2] - pSrcData); + if (SrcSize < diff2) + { + WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff); + return FALSE; + } + rleSizes[2] = planar_skip_plane_rle(planes[2], SrcSize - diff2, rawWidths[2], + rawHeights[2]); /* BluePlane */ if (rleSizes[2] < 1) return FALSE; @@ -783,6 +831,24 @@ BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* planar, const BYTE* pSrcData, UINT } else /* RLE */ { + if (nYDst + nSrcHeight > nTotalHeight) + { + WLog_ERR(TAG, + "planar plane destination Y %" PRIu32 " + height %" PRIu32 + " exceeds totalHeight %" PRIu32, + nYDst, nSrcHeight, nTotalHeight); + return FALSE; + } + + if ((nXDst + nSrcWidth) * GetBytesPerPixel(DstFormat) > nDstStep) + { + WLog_ERR(TAG, + "planar plane destination (X %" PRIu32 " + width %" PRIu32 + ") * bpp %" PRIu32 " exceeds stride %" PRIu32, + nXDst, nSrcWidth, GetBytesPerPixel(DstFormat), nDstStep); + return FALSE; + } + status = planar_decompress_plane_rle(planes[0], rleSizes[0], pTempData, nTempStep, nXDst, nYDst, nSrcWidth, nSrcHeight, 2, vFlip); /* RedPlane */ @@ -1484,9 +1550,15 @@ BOOL freerdp_bitmap_planar_context_reset(BITMAP_PLANAR_CONTEXT* context, UINT32 return FALSE; context->bgr = FALSE; - context->maxWidth = ALIGN(width, 4); - context->maxHeight = ALIGN(height, 4); - context->maxPlaneSize = context->maxWidth * context->maxHeight; + context->maxWidth = PLANAR_ALIGN(width, 4); + context->maxHeight = PLANAR_ALIGN(height, 4); + const UINT64 tmp = (UINT64)context->maxWidth * context->maxHeight; + if (tmp > UINT32_MAX) + return FALSE; + context->maxPlaneSize = tmp; + + if (context->maxWidth > UINT32_MAX / 4) + return FALSE; context->nTempStep = context->maxWidth * 4; free(context->planesBuffer); free(context->pTempData); diff --git a/libfreerdp/codec/progressive.c b/libfreerdp/codec/progressive.c index 08b8199e724f..41e658c8fd66 100644 --- a/libfreerdp/codec/progressive.c +++ b/libfreerdp/codec/progressive.c @@ -23,6 +23,7 @@ #include "config.h" #endif +#include #include #include #include @@ -154,20 +155,51 @@ static INLINE void progressive_rfx_quant_lsub(RFX_COMPONENT_CODEC_QUANT* q, int q->LL3 -= val; /* LL3 */ } -static INLINE void progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1, +static INLINE BOOL progressive_rfx_quant_sub(const RFX_COMPONENT_CODEC_QUANT* q1, const RFX_COMPONENT_CODEC_QUANT* q2, RFX_COMPONENT_CODEC_QUANT* dst) { + if (q1->HH1 < q2->HL1) + return FALSE; dst->HL1 = q1->HL1 - q2->HL1; /* HL1 */ + + if (q1->LH1 < q2->LH1) + return FALSE; dst->LH1 = q1->LH1 - q2->LH1; /* LH1 */ + + if (q1->HH1 < q2->HH1) + return FALSE; dst->HH1 = q1->HH1 - q2->HH1; /* HH1 */ + + if (q1->HL2 < q2->HL2) + return FALSE; dst->HL2 = q1->HL2 - q2->HL2; /* HL2 */ + + if (q1->LH2 < q2->LH2) + return FALSE; dst->LH2 = q1->LH2 - q2->LH2; /* LH2 */ + + if (q1->HH2 < q2->HH2) + return FALSE; dst->HH2 = q1->HH2 - q2->HH2; /* HH2 */ + + if (q1->HL3 < q2->HL3) + return FALSE; dst->HL3 = q1->HL3 - q2->HL3; /* HL3 */ + + if (q1->LH3 < q2->LH3) + return FALSE; dst->LH3 = q1->LH3 - q2->LH3; /* LH3 */ + + if (q1->HH3 < q2->HH3) + return FALSE; dst->HH3 = q1->HH3 - q2->HH3; /* HH3 */ + + if (q1->LL3 < q2->LL3) + return FALSE; dst->LL3 = q1->LL3 - q2->LL3; /* LL3 */ + + return TRUE; } static INLINE BOOL progressive_rfx_quant_lcmp_less_equal(const RFX_COMPONENT_CODEC_QUANT* q, @@ -367,13 +399,11 @@ static INLINE PROGRESSIVE_SURFACE_CONTEXT* progressive_get_surface_data(PROGRESSIVE_CONTEXT* progressive, UINT16 surfaceId) { void* key = (void*)(((ULONG_PTR)surfaceId) + 1); - void* pData = NULL; if (!progressive) return NULL; - pData = HashTable_GetItemValue(progressive->SurfaceContexts, key); - return pData; + return HashTable_GetItemValue(progressive->SurfaceContexts, key); } static void progressive_tile_free(RFX_PROGRESSIVE_TILE* tile) @@ -386,10 +416,14 @@ static void progressive_tile_free(RFX_PROGRESSIVE_TILE* tile) } } -static void progressive_surface_context_free(PROGRESSIVE_SURFACE_CONTEXT* surface) +static void progressive_surface_context_free(void* ptr) { + PROGRESSIVE_SURFACE_CONTEXT* surface = ptr; UINT32 index; + if (!surface) + return; + for (index = 0; index < surface->gridSize; index++) { RFX_PROGRESSIVE_TILE* tile = &(surface->tiles[index]); @@ -412,7 +446,7 @@ static INLINE BOOL progressive_tile_allocate(RFX_PROGRESSIVE_TILE* tile) tile->stride = 4 * tile->width; { - size_t dataLen = tile->stride * tile->height * 1ULL; + size_t dataLen = 1ull * tile->stride * tile->height; tile->data = (BYTE*)_aligned_malloc(dataLen, 16); } @@ -434,6 +468,36 @@ static INLINE BOOL progressive_tile_allocate(RFX_PROGRESSIVE_TILE* tile) return rc; } +static BOOL progressive_allocate_tile_cache(PROGRESSIVE_SURFACE_CONTEXT* surface) +{ + size_t oldIndex; + + WINPR_ASSERT(surface); + WINPR_ASSERT(surface->gridSize > 0); + + oldIndex = surface->gridSize; + if (surface->tiles) + surface->gridSize *= 2; + + { + void* tmp = realloc(surface->tiles, surface->gridSize * sizeof(RFX_PROGRESSIVE_TILE)); + if (!tmp) + return FALSE; + surface->tiles = tmp; + memset(&surface->tiles[oldIndex], 0, + (surface->gridSize - oldIndex) * sizeof(RFX_PROGRESSIVE_TILE)); + } + { + void* tmp = realloc(surface->updatedTileIndices, surface->gridSize * sizeof(UINT32)); + if (!tmp) + return FALSE; + surface->updatedTileIndices = tmp; + memset(&surface->updatedTileIndices[oldIndex], 0, + (surface->gridSize - oldIndex) * sizeof(UINT32)); + } + return TRUE; +} + static PROGRESSIVE_SURFACE_CONTEXT* progressive_surface_context_new(UINT16 surfaceId, UINT32 width, UINT32 height) { @@ -450,14 +514,10 @@ static PROGRESSIVE_SURFACE_CONTEXT* progressive_surface_context_new(UINT16 surfa surface->gridWidth = (width + (64 - width % 64)) / 64; surface->gridHeight = (height + (64 - height % 64)) / 64; surface->gridSize = surface->gridWidth * surface->gridHeight; - surface->tiles = (RFX_PROGRESSIVE_TILE*)calloc(surface->gridSize, sizeof(RFX_PROGRESSIVE_TILE)); - surface->updatedTileIndices = (UINT32*)calloc(surface->gridSize, sizeof(UINT32)); - if (!surface->tiles || !surface->updatedTileIndices) + if (!progressive_allocate_tile_cache(surface)) { - free(surface->updatedTileIndices); - free(surface->tiles); - free(surface); + progressive_surface_context_free(surface); return NULL; } for (x = 0; x < surface->gridSize; x++) @@ -549,8 +609,8 @@ static BOOL progressive_surface_tile_replace(PROGRESSIVE_SURFACE_CONTEXT* surfac } if (surface->numUpdatedTiles >= surface->gridSize) { - WLog_ERR(TAG, "Invalid total tile count, maximum %" PRIu32, surface->gridSize); - return FALSE; + if (!progressive_allocate_tile_cache(surface)) + return FALSE; } region->tiles[region->usedTiles++] = t; @@ -583,13 +643,7 @@ INT32 progressive_create_surface_context(PROGRESSIVE_CONTEXT* progressive, UINT1 int progressive_delete_surface_context(PROGRESSIVE_CONTEXT* progressive, UINT16 surfaceId) { - PROGRESSIVE_SURFACE_CONTEXT* surface = progressive_get_surface_data(progressive, surfaceId); - - if (surface) - { - progressive_set_surface_data(progressive, surfaceId, NULL); - progressive_surface_context_free(surface); - } + progressive_set_surface_data(progressive, surfaceId, NULL); return 1; } @@ -873,7 +927,7 @@ static INLINE int progressive_rfx_decode_component(PROGRESSIVE_CONTEXT* progress return status; CopyMemory(sign, buffer, 4096 * 2); - if (!subbandDiff) + if (!extrapolate) { rfx_differential_decode(buffer + 4032, 64); progressive_rfx_decode_block(prims, &buffer[0], 1024, shift->HL1); /* HL1 */ @@ -1084,7 +1138,7 @@ static INLINE INT16 progressive_rfx_srl_read(RFX_PROGRESSIVE_UPGRADE_STATE* stat if (k) { bs->mask = ((1 << k) - 1); - state->nz = ((bs->accumulator >> (32 - k)) & bs->mask); + state->nz = ((bs->accumulator >> (32u - k)) & bs->mask); BitStream_Shift(bs, k); } @@ -1410,9 +1464,12 @@ static INLINE int progressive_decompress_tile_upgrade(PROGRESSIVE_CONTEXT* progr progressive_rfx_quant_add(quantY, quantProgY, &yBitPos); progressive_rfx_quant_add(quantCb, quantProgCb, &cbBitPos); progressive_rfx_quant_add(quantCr, quantProgCr, &crBitPos); - progressive_rfx_quant_sub(&(tile->yBitPos), &yBitPos, &yNumBits); - progressive_rfx_quant_sub(&(tile->cbBitPos), &cbBitPos, &cbNumBits); - progressive_rfx_quant_sub(&(tile->crBitPos), &crBitPos, &crNumBits); + if (!progressive_rfx_quant_sub(&(tile->yBitPos), &yBitPos, &yNumBits)) + goto fail; + if (!progressive_rfx_quant_sub(&(tile->cbBitPos), &cbBitPos, &cbNumBits)) + goto fail; + if (!progressive_rfx_quant_sub(&(tile->crBitPos), &crBitPos, &crNumBits)) + goto fail; progressive_rfx_quant_add(quantY, quantProgY, &shiftY); progressive_rfx_quant_lsub(&shiftY, 1); /* -6 + 5 = -1 */ progressive_rfx_quant_add(quantCb, quantProgCb, &shiftCb); @@ -1636,6 +1693,9 @@ static void CALLBACK progressive_process_tiles_tile_work_callback(PTP_CALLBACK_I { PROGRESSIVE_TILE_PROCESS_WORK_PARAM* param = (PROGRESSIVE_TILE_PROCESS_WORK_PARAM*)context; + WINPR_UNUSED(instance); + WINPR_UNUSED(work); + switch (param->tile->blockType) { case PROGRESSIVE_WBT_TILE_SIMPLE: @@ -1770,13 +1830,13 @@ static INLINE int progressive_process_tiles(PROGRESSIVE_CONTEXT* progressive, wS for (index = 0; index < region->numTiles; index++) { RFX_PROGRESSIVE_TILE* tile = region->tiles[index]; - params[index].progressive = progressive; - params[index].region = region; - params[index].context = context; - params[index].tile = tile; if (progressive->rfx_context->priv->UseThreads) { + params[index].progressive = progressive; + params[index].region = region; + params[index].context = context; + params[index].tile = tile; if (!(work_objects[index] = CreateThreadpoolWork( progressive_process_tiles_tile_work_callback, (void*)¶ms[index], &progressive->rfx_context->priv->ThreadPoolEnv))) @@ -1791,7 +1851,10 @@ static INLINE int progressive_process_tiles(PROGRESSIVE_CONTEXT* progressive, wS } else { - progressive_process_tiles_tile_work_callback(0, ¶ms[index], 0); + PROGRESSIVE_TILE_PROCESS_WORK_PARAM param = { + .progressive = progressive, .region = region, .context = context, .tile = tile + }; + progressive_process_tiles_tile_work_callback(0, ¶m, 0); } if (status < 0) @@ -2371,8 +2434,8 @@ INT32 progressive_decompress_ex(PROGRESSIVE_CONTEXT* progressive, const BYTE* pS { RECTANGLE_16 clippingRect; const RFX_RECT* rect = &(region->rects[i]); - clippingRect.left = nXDst + rect->x; - clippingRect.top = nYDst + rect->y; + clippingRect.left = (UINT16)nXDst + rect->x; + clippingRect.top = (UINT16)nYDst + rect->y; clippingRect.right = clippingRect.left + rect->width; clippingRect.bottom = clippingRect.top + rect->height; region16_union_rect(&clippingRects, &clippingRects, &clippingRect); @@ -2396,11 +2459,17 @@ INT32 progressive_decompress_ex(PROGRESSIVE_CONTEXT* progressive, const BYTE* pS for (j = 0; j < nbUpdateRects; j++) { const RECTANGLE_16* rect = &updateRects[j]; - const UINT32 nXSrc = rect->left - (nXDst + tile->x); - const UINT32 nYSrc = rect->top - (nYDst + tile->y); + if (rect->left < updateRect.left) + goto fail; + const UINT32 nXSrc = rect->left - updateRect.left; + const UINT32 nYSrc = rect->top - updateRect.top; const UINT32 width = rect->right - rect->left; const UINT32 height = rect->bottom - rect->top; + if (rect->left + width > surface->width) + goto fail; + if (rect->top + height > surface->height) + goto fail; if (!freerdp_image_copy(pDstData, DstFormat, nDstStep, rect->left, rect->top, width, height, tile->data, progressive->format, tile->stride, nXSrc, nYSrc, NULL, FREERDP_FLIP_NONE)) @@ -2715,6 +2784,8 @@ PROGRESSIVE_CONTEXT* progressive_context_new(BOOL Compressor) if (!progressive_context_reset(progressive)) goto fail; + progressive->SurfaceContexts->valueFree = progressive_surface_context_free; + return progressive; fail: progressive_context_free(progressive); @@ -2723,11 +2794,6 @@ PROGRESSIVE_CONTEXT* progressive_context_new(BOOL Compressor) void progressive_context_free(PROGRESSIVE_CONTEXT* progressive) { - int count; - int index; - ULONG_PTR* pKeys = NULL; - PROGRESSIVE_SURFACE_CONTEXT* surface; - if (!progressive) return; @@ -2736,21 +2802,7 @@ void progressive_context_free(PROGRESSIVE_CONTEXT* progressive) rfx_context_free(progressive->rfx_context); BufferPool_Free(progressive->bufferPool); - - if (progressive->SurfaceContexts) - { - count = HashTable_GetKeys(progressive->SurfaceContexts, &pKeys); - - for (index = 0; index < count; index++) - { - surface = (PROGRESSIVE_SURFACE_CONTEXT*)HashTable_GetItemValue( - progressive->SurfaceContexts, (void*)pKeys[index]); - progressive_surface_context_free(surface); - } - - free(pKeys); - HashTable_Free(progressive->SurfaceContexts); - } + HashTable_Free(progressive->SurfaceContexts); free(progressive); } diff --git a/libfreerdp/codec/region.c b/libfreerdp/codec/region.c index 1009b852a903..dc4377fcbdc6 100644 --- a/libfreerdp/codec/region.c +++ b/libfreerdp/codec/region.c @@ -496,7 +496,6 @@ BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* UINT32 usedRects, srcNbRects; UINT16 topInterBand; assert(src); - assert(src->data); assert(dst); srcExtents = region16_extents(src); dstExtents = region16_extents_noconst(dst); @@ -658,8 +657,8 @@ BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* dstRect++; } - if ((src == dst) && (src->data->size > 0) && (src->data != &empty_region)) - free(src->data); + if ((src == dst) && (dst->data != &empty_region)) + free(dst->data); dstExtents->top = MIN(rect->top, srcExtents->top); dstExtents->left = MIN(rect->left, srcExtents->left); @@ -673,10 +672,7 @@ BOOL region16_union_rect(REGION16* dst, const REGION16* src, const RECTANGLE_16* dst->data = newItems; if (!dst->data) - { - free(newItems); return FALSE; - } dst->data->nbRects = usedRects; return region16_simplify_bands(dst); diff --git a/libfreerdp/codec/rfx.c b/libfreerdp/codec/rfx.c index 8c65e7508370..ccbc5afe44fa 100644 --- a/libfreerdp/codec/rfx.c +++ b/libfreerdp/codec/rfx.c @@ -936,6 +936,27 @@ static BOOL rfx_process_message_tileset(RFX_CONTEXT* context, RFX_MESSAGE* messa Stream_Read_UINT8(&sub, tile->quantIdxY); /* quantIdxY (1 byte) */ Stream_Read_UINT8(&sub, tile->quantIdxCb); /* quantIdxCb (1 byte) */ Stream_Read_UINT8(&sub, tile->quantIdxCr); /* quantIdxCr (1 byte) */ + if (tile->quantIdxY >= context->numQuant) + { + WLog_Print(context->priv->log, WLOG_ERROR, "quantIdxY %" PRIu8 " >= numQuant %" PRIu8, + tile->quantIdxY, context->numQuant); + rc = FALSE; + break; + } + else if (tile->quantIdxCb >= context->numQuant) + { + WLog_Print(context->priv->log, WLOG_ERROR, "quantIdxCb %" PRIu8 " >= numQuant %" PRIu8, + tile->quantIdxCb, context->numQuant); + rc = FALSE; + break; + } + else if (tile->quantIdxCr >= context->numQuant) + { + WLog_Print(context->priv->log, WLOG_ERROR, "quantIdxCr %" PRIu8 " >= numQuant %" PRIu8, + tile->quantIdxCr, context->numQuant); + rc = FALSE; + break; + } Stream_Read_UINT16(&sub, tile->xIdx); /* xIdx (2 bytes) */ Stream_Read_UINT16(&sub, tile->yIdx); /* yIdx (2 bytes) */ Stream_Read_UINT16(&sub, tile->YLen); /* YLen (2 bytes) */ @@ -1071,7 +1092,10 @@ BOOL rfx_process_message(RFX_CONTEXT* context, const BYTE* data, UINT32 length, UINT8 channelId; if (Stream_GetRemainingLength(s) < 2) + { + WLog_ERR(TAG, "extraBlockLen too small(%" PRIuz ")", Stream_GetRemainingLength(s)); return FALSE; + } extraBlockLen = 2; Stream_Read_UINT8(s, codecId); /* codecId (1 byte) must be set to 0x01 */ @@ -1106,8 +1130,18 @@ BOOL rfx_process_message(RFX_CONTEXT* context, const BYTE* data, UINT32 length, } } - Stream_StaticInit(&subStream, Stream_Pointer(s), blockLen - (6 + extraBlockLen)); - Stream_Seek(s, blockLen - (6 + extraBlockLen)); + const size_t blockLenNoHeader = blockLen - 6; + if (blockLenNoHeader < extraBlockLen) + { + WLog_Print(context->priv->log, WLOG_ERROR, + "blockLen too small(%" PRIu32 "), must be >= 6 + %" PRIu16, blockLen, + extraBlockLen); + return FALSE; + } + + const size_t subStreamLen = blockLenNoHeader - extraBlockLen; + Stream_StaticInit(&subStream, Stream_Pointer(s), subStreamLen); + Stream_Seek(s, subStreamLen); switch (blockType) { @@ -1212,6 +1246,9 @@ BOOL rfx_process_message(RFX_CONTEXT* context, const BYTE* data, UINT32 length, NULL, FREERDP_FLIP_NONE)) { region16_uninit(&updateRegion); + WLog_ERR(TAG, + "nbUpdateRectx[% " PRIu32 " (%" PRIu32 ")] freerdp_image_copy failed", + j, nbUpdateRects); return FALSE; } @@ -1225,7 +1262,13 @@ BOOL rfx_process_message(RFX_CONTEXT* context, const BYTE* data, UINT32 length, region16_uninit(&clippingRects); return TRUE; } + else + { + rfx_message_free(context, message); + context->currentMessage.freeArray = TRUE; + } + WLog_ERR(TAG, "%s failed", __FUNCTION__); return FALSE; } diff --git a/libfreerdp/codec/rfx_encode.c b/libfreerdp/codec/rfx_encode.c index b4f393605205..355f74340242 100644 --- a/libfreerdp/codec/rfx_encode.c +++ b/libfreerdp/codec/rfx_encode.c @@ -49,6 +49,8 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i int x_exceed; int y_exceed; const BYTE* src; + const UINT32* src_32; + const UINT16* src_16; INT16 r, g, b; INT16 *r_last, *g_last, *b_last; x_exceed = 64 - width; @@ -57,6 +59,8 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i for (y = 0; y < height; y++) { src = rgb_data + y * rowstride; + src_32 = (UINT32*)src; + src_16 = (UINT16*)src; switch (pixel_format) { @@ -64,10 +68,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_BGRA32: for (x = 0; x < width; x++) { - *b_buf++ = (INT16)(*src++); - *g_buf++ = (INT16)(*src++); - *r_buf++ = (INT16)(*src++); - src++; + *b_buf++ = (INT16)(*src_32 & 0xFF); + *g_buf++ = (INT16)((*src_32 >> 8) & 0xFF); + *r_buf++ = (INT16)((*src_32 >> 16) & 0xFF); + src_32++; } break; @@ -76,10 +80,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_ABGR32: for (x = 0; x < width; x++) { - src++; - *b_buf++ = (INT16)(*src++); - *g_buf++ = (INT16)(*src++); - *r_buf++ = (INT16)(*src++); + *b_buf++ = (INT16)((*src_32 >> 8) & 0xFF); + *g_buf++ = (INT16)((*src_32 >> 16) & 0xFF); + *r_buf++ = (INT16)((*src_32 >> 24) & 0xFF); + src_32++; } break; @@ -88,10 +92,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_RGBA32: for (x = 0; x < width; x++) { - *r_buf++ = (INT16)(*src++); - *g_buf++ = (INT16)(*src++); - *b_buf++ = (INT16)(*src++); - src++; + *r_buf++ = (INT16)(*src_32 & 0xFF); + *g_buf++ = (INT16)((*src_32 >> 8) & 0xFF); + *b_buf++ = (INT16)((*src_32 >> 16) & 0xFF); + src_32++; } break; @@ -100,10 +104,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_ARGB32: for (x = 0; x < width; x++) { - src++; - *r_buf++ = (INT16)(*src++); - *g_buf++ = (INT16)(*src++); - *b_buf++ = (INT16)(*src++); + *r_buf++ = (INT16)((*src_32 >> 8) & 0xFF); + *g_buf++ = (INT16)((*src_32 >> 16) & 0xFF); + *b_buf++ = (INT16)((*src_32 >> 24) & 0xFF); + src_32++; } break; @@ -111,9 +115,15 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_BGR24: for (x = 0; x < width; x++) { +#ifdef __LITTLE_ENDIAN__ *b_buf++ = (INT16)(*src++); *g_buf++ = (INT16)(*src++); *r_buf++ = (INT16)(*src++); +#else + *r_buf++ = (INT16)(*src++); + *g_buf++ = (INT16)(*src++); + *b_buf++ = (INT16)(*src++); +#endif } break; @@ -121,9 +131,15 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_RGB24: for (x = 0; x < width; x++) { +#ifdef __LITTLE_ENDIAN__ *r_buf++ = (INT16)(*src++); *g_buf++ = (INT16)(*src++); *b_buf++ = (INT16)(*src++); +#else + *b_buf++ = (INT16)(*src++); + *g_buf++ = (INT16)(*src++); + *r_buf++ = (INT16)(*src++); +#endif } break; @@ -131,10 +147,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_BGR16: for (x = 0; x < width; x++) { - *b_buf++ = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5)); - *g_buf++ = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3)); - *r_buf++ = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07)); - src += 2; + *b_buf++ = (INT16)((*src_16) & 0x1F); + *g_buf++ = (INT16)((*src_16 >> 5) & 0x3F); + *r_buf++ = (INT16)((*src_16 >> 11) & 0x1F); + src_16++; } break; @@ -142,10 +158,10 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i case PIXEL_FORMAT_RGB16: for (x = 0; x < width; x++) { - *r_buf++ = (INT16)(((*(src + 1)) & 0xF8) | ((*(src + 1)) >> 5)); - *g_buf++ = (INT16)((((*(src + 1)) & 0x07) << 5) | (((*src) & 0xE0) >> 3)); - *b_buf++ = (INT16)((((*src) & 0x1F) << 3) | (((*src) >> 2) & 0x07)); - src += 2; + *r_buf++ = (INT16)((*src_16 & 0x1F)); + *g_buf++ = (INT16)((*src_16 >> 5) & 0x3F); + *b_buf++ = (INT16)((*src_16 >> 11) & 0x1F); + src_16++; } break; @@ -159,17 +175,17 @@ static void rfx_encode_format_rgb(const BYTE* rgb_data, int width, int height, i int shift; BYTE idx; shift = (7 - (x % 8)); - idx = ((*src) >> shift) & 1; - idx |= (((*(src + 1)) >> shift) & 1) << 1; - idx |= (((*(src + 2)) >> shift) & 1) << 2; - idx |= (((*(src + 3)) >> shift) & 1) << 3; + idx = (BYTE)(((*src_32 & 0xFF) >> shift) & 1); + idx |= (BYTE)(((((*src_32 >> 8) & 0xFF) >> shift) & 1) << 1); + idx |= (BYTE)(((((*src_32 >> 16) & 0xFF) >> shift) & 1) << 2); + idx |= (BYTE)(((((*src_32 >> 24) & 0xFF) >> shift) & 1) << 3); idx *= 3; *r_buf++ = (INT16)palette[idx]; *g_buf++ = (INT16)palette[idx + 1]; *b_buf++ = (INT16)palette[idx + 2]; if (shift == 0) - src += 4; + src_32++; } break; diff --git a/libfreerdp/codec/rfx_neon.c b/libfreerdp/codec/rfx_neon.c index c98955adeedf..c41ebb404d66 100644 --- a/libfreerdp/codec/rfx_neon.c +++ b/libfreerdp/codec/rfx_neon.c @@ -21,7 +21,7 @@ #include "config.h" #endif -#if defined(__ARM_NEON__) +#if defined(WITH_NEON) #include #include @@ -241,4 +241,4 @@ void rfx_init_neon(RFX_CONTEXT* context) } } -#endif // __ARM_NEON__ +#endif // WITH_NEON diff --git a/libfreerdp/codec/xcrush.c b/libfreerdp/codec/xcrush.c index 350801df338e..56bf649f5560 100644 --- a/libfreerdp/codec/xcrush.c +++ b/libfreerdp/codec/xcrush.c @@ -674,7 +674,7 @@ static int xcrush_generate_output(XCRUSH_CONTEXT* xcrush, BYTE* OutputBuffer, UI if (&OutputBuffer[2] >= &OutputBuffer[OutputSize]) return -6001; /* error */ - *((UINT16*)OutputBuffer) = MatchCount; + Data_Write_UINT16(OutputBuffer, MatchCount); MatchDetails = (RDP61_MATCH_DETAILS*)&OutputBuffer[2]; Literals = (BYTE*)&MatchDetails[MatchCount]; @@ -683,12 +683,12 @@ static int xcrush_generate_output(XCRUSH_CONTEXT* xcrush, BYTE* OutputBuffer, UI for (MatchIndex = 0; MatchIndex < MatchCount; MatchIndex++) { - MatchDetails[MatchIndex].MatchLength = - (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchLength); - MatchDetails[MatchIndex].MatchOutputOffset = - (UINT16)(xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset); - MatchDetails[MatchIndex].MatchHistoryOffset = - xcrush->OptimizedMatches[MatchIndex].ChunkOffset; + Data_Write_UINT16(&MatchDetails[MatchIndex].MatchLength, + xcrush->OptimizedMatches[MatchIndex].MatchLength); + Data_Write_UINT16(&MatchDetails[MatchIndex].MatchOutputOffset, + xcrush->OptimizedMatches[MatchIndex].MatchOffset - HistoryOffset); + Data_Write_UINT32(&MatchDetails[MatchIndex].MatchHistoryOffset, + xcrush->OptimizedMatches[MatchIndex].ChunkOffset); } CurrentOffset = HistoryOffset; diff --git a/libfreerdp/codec/zgfx.c b/libfreerdp/codec/zgfx.c index 1a2878bd9a6a..c63229480499 100644 --- a/libfreerdp/codec/zgfx.c +++ b/libfreerdp/codec/zgfx.c @@ -230,19 +230,22 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t BYTE* pbSegment; size_t cbSegment; - if (!zgfx || !stream) + WINPR_ASSERT(zgfx); + WINPR_ASSERT(stream); + + if (segmentSize < 2) return FALSE; cbSegment = segmentSize - 1; - if ((Stream_GetRemainingLength(stream) < segmentSize) || (segmentSize < 1) || - (segmentSize > UINT32_MAX)) + if ((Stream_GetRemainingLength(stream) < segmentSize) || (segmentSize > UINT32_MAX)) return FALSE; Stream_Read_UINT8(stream, flags); /* header (1 byte) */ zgfx->OutputCount = 0; pbSegment = Stream_Pointer(stream); - Stream_Seek(stream, cbSegment); + if (!Stream_SafeSeek(stream, cbSegment)) + return FALSE; if (!(flags & PACKET_COMPRESSED)) { @@ -259,7 +262,11 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t zgfx->pbInputCurrent = pbSegment; zgfx->pbInputEnd = &pbSegment[cbSegment - 1]; /* NumberOfBitsToDecode = ((NumberOfBytesToDecode - 1) * 8) - ValueOfLastByte */ - zgfx->cBitsRemaining = 8 * (cbSegment - 1) - *zgfx->pbInputEnd; + const UINT32 bits = 8u * (cbSegment - 1u); + if (bits < *zgfx->pbInputEnd) + return FALSE; + + zgfx->cBitsRemaining = bits - *zgfx->pbInputEnd; zgfx->cBitsCurrent = 0; zgfx->BitsCurrent = 0; @@ -345,6 +352,10 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t if (count > sizeof(zgfx->OutputBuffer) - zgfx->OutputCount) return FALSE; + else if (count > zgfx->cBitsRemaining / 8) + return FALSE; + else if (zgfx->pbInputCurrent + count > zgfx->pbInputEnd) + return FALSE; CopyMemory(&(zgfx->OutputBuffer[zgfx->OutputCount]), zgfx->pbInputCurrent, count); @@ -363,15 +374,56 @@ static BOOL zgfx_decompress_segment(ZGFX_CONTEXT* zgfx, wStream* stream, size_t return TRUE; } +/* Allocate the buffers a bit larger. + * + * Due to optimizations some h264 decoders will read data beyond + * the actual available data, so ensure that it will never be a + * out of bounds read. + */ +static BYTE* aligned_zgfx_malloc(size_t size) +{ + return malloc(size + 64); +} + +static BOOL zgfx_append(ZGFX_CONTEXT* zgfx, BYTE** ppConcatenated, size_t uncompressedSize, + size_t* pUsed) +{ + WINPR_ASSERT(zgfx); + WINPR_ASSERT(ppConcatenated); + WINPR_ASSERT(pUsed); + + const size_t used = *pUsed; + if (zgfx->OutputCount > UINT32_MAX - used) + return FALSE; + + if (used + zgfx->OutputCount > uncompressedSize) + return FALSE; + + BYTE* tmp = realloc(*ppConcatenated, used + zgfx->OutputCount + 64ull); + if (!tmp) + return FALSE; + *ppConcatenated = tmp; + CopyMemory(&tmp[used], zgfx->OutputBuffer, zgfx->OutputCount); + *pUsed = used + zgfx->OutputCount; + return TRUE; +} + int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BYTE** ppDstData, UINT32* pDstSize, UINT32 flags) { int status = -1; - BYTE descriptor; + BYTE descriptor = 0; + size_t used = 0; + BYTE* pConcatenated = NULL; wStream* stream = Stream_New((BYTE*)pSrcData, SrcSize); - if (!stream) - return -1; + WINPR_ASSERT(zgfx); + WINPR_ASSERT(stream); + WINPR_ASSERT(ppDstData); + WINPR_ASSERT(pDstSize); + + *ppDstData = NULL; + *pDstSize = 0; if (Stream_GetRemainingLength(stream) < 1) goto fail; @@ -383,16 +435,15 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY if (!zgfx_decompress_segment(zgfx, stream, Stream_GetRemainingLength(stream))) goto fail; - *ppDstData = NULL; - if (zgfx->OutputCount > 0) - *ppDstData = (BYTE*)malloc(zgfx->OutputCount); - - if (!*ppDstData) - goto fail; - - *pDstSize = zgfx->OutputCount; - CopyMemory(*ppDstData, zgfx->OutputBuffer, zgfx->OutputCount); + { + if (!zgfx_append(zgfx, &pConcatenated, zgfx->OutputCount, &used)) + goto fail; + if (used != zgfx->OutputCount) + goto fail; + *ppDstData = pConcatenated; + *pDstSize = zgfx->OutputCount; + } } else if (descriptor == ZGFX_SEGMENTED_MULTIPART) { @@ -400,8 +451,6 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY UINT16 segmentNumber; UINT16 segmentCount; UINT32 uncompressedSize; - BYTE* pConcatenated; - size_t used = 0; if (Stream_GetRemainingLength(stream) < 6) goto fail; @@ -409,17 +458,6 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY Stream_Read_UINT16(stream, segmentCount); /* segmentCount (2 bytes) */ Stream_Read_UINT32(stream, uncompressedSize); /* uncompressedSize (4 bytes) */ - if (Stream_GetRemainingLength(stream) < segmentCount * sizeof(UINT32)) - goto fail; - - pConcatenated = (BYTE*)malloc(uncompressedSize); - - if (!pConcatenated) - goto fail; - - *ppDstData = pConcatenated; - *pDstSize = uncompressedSize; - for (segmentNumber = 0; segmentNumber < segmentCount; segmentNumber++) { if (Stream_GetRemainingLength(stream) < sizeof(UINT32)) @@ -430,16 +468,15 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY if (!zgfx_decompress_segment(zgfx, stream, segmentSize)) goto fail; - if (zgfx->OutputCount > UINT32_MAX - used) + if (!zgfx_append(zgfx, &pConcatenated, uncompressedSize, &used)) goto fail; + } - if (used + zgfx->OutputCount > uncompressedSize) - goto fail; + if (used != uncompressedSize) + goto fail; - CopyMemory(pConcatenated, zgfx->OutputBuffer, zgfx->OutputCount); - pConcatenated += zgfx->OutputCount; - used += zgfx->OutputCount; - } + *ppDstData = pConcatenated; + *pDstSize = uncompressedSize; } else { @@ -448,6 +485,8 @@ int zgfx_decompress(ZGFX_CONTEXT* zgfx, const BYTE* pSrcData, UINT32 SrcSize, BY status = 1; fail: + if (status < 0) + free(pConcatenated); Stream_Free(stream, FALSE); return status; } diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index b639778b0462..2be4fcd0d5e3 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -925,3 +925,58 @@ BOOL freerdp_settings_set_value_for_name(rdpSettings* settings, const char* name } return FALSE; } + +char* freerdp_rail_support_flags_to_string(UINT32 flags, char* buffer, size_t length) +{ + if (flags & RAIL_LEVEL_SUPPORTED) + winpr_str_append("RAIL_LEVEL_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED) + winpr_str_append("RAIL_LEVEL_DOCKED_LANGBAR_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED) + winpr_str_append("RAIL_LEVEL_SHELL_INTEGRATION_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED) + winpr_str_append("RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED) + winpr_str_append("RAIL_LEVEL_SERVER_TO_CLIENT_IME_SYNC_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED) + winpr_str_append("RAIL_LEVEL_HIDE_MINIMIZED_APPS_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED) + winpr_str_append("RAIL_LEVEL_WINDOW_CLOAKING_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED) + winpr_str_append("RAIL_LEVEL_HANDSHAKE_EX_SUPPORTED", buffer, length, "|"); + if (flags & RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED) + winpr_str_append("RAIL_LEVEL_LANGUAGE_IME_SYNC_SUPPORTED", buffer, length, "|"); + return buffer; +} + +BOOL freerdp_target_net_addresses_copy(rdpSettings* settings, char** addresses, UINT32 count) +{ + UINT32 i; + + WINPR_ASSERT(settings); + WINPR_ASSERT(addresses); + + freerdp_target_net_addresses_free(settings); + + settings->TargetNetAddressCount = count; + settings->TargetNetAddresses = (char**)calloc(settings->TargetNetAddressCount, sizeof(char*)); + + if (!settings->TargetNetAddresses) + { + freerdp_target_net_addresses_free(settings); + return FALSE; + } + + for (i = 0; i < settings->TargetNetAddressCount; i++) + { + settings->TargetNetAddresses[i] = _strdup(addresses[i]); + + if (!settings->TargetNetAddresses[i]) + { + freerdp_target_net_addresses_free(settings); + return FALSE; + } + } + + return TRUE; +} diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 2a34bb80fca9..8df8957c66b1 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -231,6 +231,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id) case FreeRDP_GrabKeyboard: return settings->GrabKeyboard; + case FreeRDP_GrabMouse: + return settings->GrabMouse; + case FreeRDP_HasExtendedMouseEvent: return settings->HasExtendedMouseEvent; @@ -809,6 +812,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val) settings->GrabKeyboard = val; break; + case FreeRDP_GrabMouse: + settings->GrabMouse = val; + break; + case FreeRDP_HasExtendedMouseEvent: settings->HasExtendedMouseEvent = val; break; @@ -1193,6 +1200,12 @@ UINT16 freerdp_settings_get_uint16(const rdpSettings* settings, size_t id) case FreeRDP_ProxyPort: return settings->ProxyPort; + case FreeRDP_TLSMaxVersion: + return settings->TLSMaxVersion; + + case FreeRDP_TLSMinVersion: + return settings->TLSMinVersion; + default: WLog_ERR(TAG, "[%s] Invalid key index %" PRIuz, __FUNCTION__, id); return FALSE; @@ -1214,6 +1227,14 @@ BOOL freerdp_settings_set_uint16(rdpSettings* settings, size_t id, UINT16 val) settings->ProxyPort = val; break; + case FreeRDP_TLSMaxVersion: + settings->TLSMaxVersion = val; + break; + + case FreeRDP_TLSMinVersion: + settings->TLSMinVersion = val; + break; + default: WLog_ERR(TAG, "[%s] Invalid key index %" PRIuz, __FUNCTION__, id); return FALSE; @@ -1495,12 +1516,18 @@ UINT32 freerdp_settings_get_uint32(const rdpSettings* settings, size_t id) case FreeRDP_RedirectionFlags: return settings->RedirectionFlags; + case FreeRDP_RedirectionGuidLength: + return settings->RedirectionGuidLength; + case FreeRDP_RedirectionPasswordLength: return settings->RedirectionPasswordLength; case FreeRDP_RedirectionPreferType: return settings->RedirectionPreferType; + case FreeRDP_RedirectionTargetCertificateLength: + return settings->RedirectionTargetCertificateLength; + case FreeRDP_RedirectionTsvUrlLength: return settings->RedirectionTsvUrlLength; @@ -1570,6 +1597,9 @@ UINT32 freerdp_settings_get_uint32(const rdpSettings* settings, size_t id) case FreeRDP_TcpAckTimeout: return settings->TcpAckTimeout; + case FreeRDP_TcpConnectTimeout: + return settings->TcpConnectTimeout; + case FreeRDP_TcpKeepAliveDelay: return settings->TcpKeepAliveDelay; @@ -1921,6 +1951,10 @@ BOOL freerdp_settings_set_uint32(rdpSettings* settings, size_t id, UINT32 val) settings->RedirectionFlags = val; break; + case FreeRDP_RedirectionGuidLength: + settings->RedirectionGuidLength = val; + break; + case FreeRDP_RedirectionPasswordLength: settings->RedirectionPasswordLength = val; break; @@ -1929,6 +1963,10 @@ BOOL freerdp_settings_set_uint32(rdpSettings* settings, size_t id, UINT32 val) settings->RedirectionPreferType = val; break; + case FreeRDP_RedirectionTargetCertificateLength: + settings->RedirectionTargetCertificateLength = val; + break; + case FreeRDP_RedirectionTsvUrlLength: settings->RedirectionTsvUrlLength = val; break; @@ -2021,6 +2059,10 @@ BOOL freerdp_settings_set_uint32(rdpSettings* settings, size_t id, UINT32 val) settings->TcpAckTimeout = val; break; + case FreeRDP_TcpConnectTimeout: + settings->TcpConnectTimeout = val; + break; + case FreeRDP_TcpKeepAliveDelay: settings->TcpKeepAliveDelay = val; break; @@ -2847,9 +2889,15 @@ const void* freerdp_settings_get_pointer(const rdpSettings* settings, size_t id) case FreeRDP_ReceivedCapabilities: return settings->ReceivedCapabilities; + case FreeRDP_RedirectionGuid: + return settings->RedirectionGuid; + case FreeRDP_RedirectionPassword: return settings->RedirectionPassword; + case FreeRDP_RedirectionTargetCertificate: + return settings->RedirectionTargetCertificate; + case FreeRDP_RedirectionTsvUrl: return settings->RedirectionTsvUrl; @@ -2955,10 +3003,18 @@ BOOL freerdp_settings_set_pointer(rdpSettings* settings, size_t id, const void* settings->ReceivedCapabilities = (void*)val; break; + case FreeRDP_RedirectionGuid: + settings->RedirectionGuid = (void*)val; + break; + case FreeRDP_RedirectionPassword: settings->RedirectionPassword = (void*)val; break; + case FreeRDP_RedirectionTargetCertificate: + settings->RedirectionTargetCertificate = (void*)val; + break; + case FreeRDP_RedirectionTsvUrl: settings->RedirectionTsvUrl = (void*)val; break; diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index b7b8def722e9..6fa11c05b79a 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -87,6 +87,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_GfxSmallCache, 0, "FreeRDP_GfxSmallCache" }, { FreeRDP_GfxThinClient, 0, "FreeRDP_GfxThinClient" }, { FreeRDP_GrabKeyboard, 0, "FreeRDP_GrabKeyboard" }, + { FreeRDP_GrabMouse, 0, "FreeRDP_GrabMouse" }, { FreeRDP_HasExtendedMouseEvent, 0, "FreeRDP_HasExtendedMouseEvent" }, { FreeRDP_HasHorizontalWheel, 0, "FreeRDP_HasHorizontalWheel" }, { FreeRDP_HasMonitorAttributes, 0, "FreeRDP_HasMonitorAttributes" }, @@ -180,6 +181,8 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_Workarea, 0, "FreeRDP_Workarea" }, { FreeRDP_DesktopOrientation, 1, "FreeRDP_DesktopOrientation" }, { FreeRDP_ProxyPort, 1, "FreeRDP_ProxyPort" }, + { FreeRDP_TLSMaxVersion, 1, "FreeRDP_TLSMaxVersion" }, + { FreeRDP_TLSMinVersion, 1, "FreeRDP_TLSMinVersion" }, { FreeRDP_AcceptedCertLength, 3, "FreeRDP_AcceptedCertLength" }, { FreeRDP_AuthenticationLevel, 3, "FreeRDP_AuthenticationLevel" }, { FreeRDP_AutoReconnectMaxRetries, 3, "FreeRDP_AutoReconnectMaxRetries" }, @@ -260,8 +263,10 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_RedirectedSessionId, 3, "FreeRDP_RedirectedSessionId" }, { FreeRDP_RedirectionAcceptedCertLength, 3, "FreeRDP_RedirectionAcceptedCertLength" }, { FreeRDP_RedirectionFlags, 3, "FreeRDP_RedirectionFlags" }, + { FreeRDP_RedirectionGuidLength, 3, "FreeRDP_RedirectionGuidLength" }, { FreeRDP_RedirectionPasswordLength, 3, "FreeRDP_RedirectionPasswordLength" }, { FreeRDP_RedirectionPreferType, 3, "FreeRDP_RedirectionPreferType" }, + { FreeRDP_RedirectionTargetCertificateLength, 3, "FreeRDP_RedirectionTargetCertificateLength" }, { FreeRDP_RedirectionTsvUrlLength, 3, "FreeRDP_RedirectionTsvUrlLength" }, { FreeRDP_RemoteAppNumIconCacheEntries, 3, "FreeRDP_RemoteAppNumIconCacheEntries" }, { FreeRDP_RemoteAppNumIconCaches, 3, "FreeRDP_RemoteAppNumIconCaches" }, @@ -285,6 +290,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_StaticChannelCount, 3, "FreeRDP_StaticChannelCount" }, { FreeRDP_TargetNetAddressCount, 3, "FreeRDP_TargetNetAddressCount" }, { FreeRDP_TcpAckTimeout, 3, "FreeRDP_TcpAckTimeout" }, + { FreeRDP_TcpConnectTimeout, 3, "FreeRDP_TcpConnectTimeout" }, { FreeRDP_TcpKeepAliveDelay, 3, "FreeRDP_TcpKeepAliveDelay" }, { FreeRDP_TcpKeepAliveInterval, 3, "FreeRDP_TcpKeepAliveInterval" }, { FreeRDP_TcpKeepAliveRetries, 3, "FreeRDP_TcpKeepAliveRetries" }, @@ -378,7 +384,9 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_RdpServerCertificate, 8, "FreeRDP_RdpServerCertificate" }, { FreeRDP_RdpServerRsaKey, 8, "FreeRDP_RdpServerRsaKey" }, { FreeRDP_ReceivedCapabilities, 8, "FreeRDP_ReceivedCapabilities" }, + { FreeRDP_RedirectionGuid, 8, "FreeRDP_RedirectionGuid" }, { FreeRDP_RedirectionPassword, 8, "FreeRDP_RedirectionPassword" }, + { FreeRDP_RedirectionTargetCertificate, 8, "FreeRDP_RedirectionTargetCertificate" }, { FreeRDP_RedirectionTsvUrl, 8, "FreeRDP_RedirectionTsvUrl" }, { FreeRDP_ServerAutoReconnectCookie, 8, "FreeRDP_ServerAutoReconnectCookie" }, { FreeRDP_ServerCertificate, 8, "FreeRDP_ServerCertificate" }, diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index f9f830064046..22757fda02a8 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -50,6 +50,8 @@ set(${MODULE_PREFIX}_GATEWAY_SRCS ${${MODULE_PREFIX}_GATEWAY_DIR}/ncacn_http.h) set(${MODULE_PREFIX}_SRCS + utils.c + utils.h bulk.c bulk.h activation.c @@ -131,7 +133,10 @@ set(${MODULE_PREFIX}_SRCS peer.c peer.h display.c - display.h) + display.h + utils.c + utils.h + ) set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${${MODULE_PREFIX}_GATEWAY_SRCS}) diff --git a/libfreerdp/core/activation.c b/libfreerdp/core/activation.c index 3c9606ea6a3f..1110b76ba1bc 100644 --- a/libfreerdp/core/activation.c +++ b/libfreerdp/core/activation.c @@ -350,7 +350,7 @@ BOOL rdp_recv_deactivate_all(rdpRdp* rdp, wStream* s) UINT16 lengthSourceDescriptor; UINT32 timeout; - if (rdp->state == CONNECTION_STATE_ACTIVE) + if (rdp_get_state(rdp) == CONNECTION_STATE_ACTIVE) rdp->deactivation_reactivation = TRUE; else rdp->deactivation_reactivation = FALSE; @@ -387,10 +387,10 @@ BOOL rdp_recv_deactivate_all(rdpRdp* rdp, wStream* s) if (rdp_check_fds(rdp) < 0) return FALSE; - if (freerdp_shall_disconnect(rdp->instance)) + if (freerdp_shall_disconnect_context(rdp->context)) return TRUE; - if (rdp->state == CONNECTION_STATE_ACTIVE) + if (rdp_get_state(rdp) == CONNECTION_STATE_ACTIVE) return TRUE; Sleep(100); diff --git a/libfreerdp/core/capabilities.c b/libfreerdp/core/capabilities.c index 30edcbb1c0a6..5fcc6c39462a 100644 --- a/libfreerdp/core/capabilities.c +++ b/libfreerdp/core/capabilities.c @@ -2789,7 +2789,7 @@ static BOOL rdp_read_bitmap_codecs_capability_set(wStream* s, rdpSettings* setti Stream_Read_UINT32(&sub, captureFlags); /* captureFlags (4 bytes) */ Stream_Read_UINT32(&sub, rfxCapsLength); /* capsLength (4 bytes) */ settings->RemoteFxCaptureFlags = captureFlags; - settings->RemoteFxOnly = (captureFlags & CARDP_CAPS_CAPTURE_NON_CAC) ? TRUE : FALSE; + settings->RemoteFxOnly = (captureFlags & CARDP_CAPS_CAPTURE_NON_CAC) ? FALSE : TRUE; if (rfxCapsLength) { @@ -3846,7 +3846,7 @@ BOOL rdp_recv_get_active_header(rdpRdp* rdp, wStream* s, UINT16* pChannelId, UIN if (!rdp_read_header(rdp, s, length, pChannelId)) return FALSE; - if (freerdp_shall_disconnect(rdp->instance)) + if (freerdp_shall_disconnect_context(rdp->context)) return TRUE; if (rdp->settings->UseRdpSecurityLayer) @@ -3891,7 +3891,7 @@ BOOL rdp_recv_demand_active(rdpRdp* rdp, wStream* s) if (!rdp_recv_get_active_header(rdp, s, &channelId, &length)) return FALSE; - if (freerdp_shall_disconnect(rdp->instance)) + if (freerdp_shall_disconnect_context(rdp->context)) return TRUE; if (!rdp_read_share_control_header(s, NULL, NULL, &pduType, &pduSource)) diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 143a1cbc521b..9b67a86a6713 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -39,6 +39,8 @@ #include #include +#include "utils.h" + #define TAG FREERDP_TAG("core.connection") /** @@ -186,6 +188,7 @@ static int rdp_client_connect_finalize(rdpRdp* rdp); static BOOL rdp_send_server_control_granted_pdu(rdpRdp* rdp); +static BOOL rdp_set_state(rdpRdp* rdp, CONNECTION_STATE state); static BOOL rdp_client_reset_codecs(rdpContext* context) { @@ -356,7 +359,7 @@ BOOL rdp_client_connect(rdpRdp* rdp) rdp->transport->ReceiveExtra = rdp; transport_set_blocking_mode(rdp->transport, FALSE); - if (rdp->state != CONNECTION_STATE_NLA) + if (rdp_get_state(rdp) != CONNECTION_STATE_NLA) { if (!mcs_client_begin(rdp->mcs)) return FALSE; @@ -370,7 +373,7 @@ BOOL rdp_client_connect(rdpRdp* rdp) return FALSE; } - if (rdp->state == CONNECTION_STATE_ACTIVE) + if (rdp_get_state(rdp) == CONNECTION_STATE_ACTIVE) return TRUE; Sleep(100); @@ -411,10 +414,13 @@ BOOL rdp_client_disconnect_and_clear(rdpRdp* rdp) return FALSE; context = rdp->context; + + if (freerdp_get_last_error(context) == FREERDP_ERROR_CONNECT_CANCELLED) + return FALSE; + context->LastError = FREERDP_ERROR_SUCCESS; clearChannelError(context); - ResetEvent(context->abortEvent); - return TRUE; + return utils_reset_abort(context); } static BOOL rdp_client_reconnect_channels(rdpRdp* rdp, BOOL redirect) @@ -711,10 +717,9 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) goto end; } - rdp->rc4_decrypt_key = winpr_RC4_New(rdp->decrypt_key, rdp->rc4_key_len); - rdp->rc4_encrypt_key = winpr_RC4_New(rdp->encrypt_key, rdp->rc4_key_len); - - if (!rdp->rc4_decrypt_key || !rdp->rc4_encrypt_key) + if (!rdp_reset_rc4_encrypt_keys(rdp)) + goto end; + if (!rdp_reset_rc4_decrypt_keys(rdp)) goto end; ret = TRUE; @@ -725,12 +730,11 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) { winpr_Cipher_Free(rdp->fips_decrypt); winpr_Cipher_Free(rdp->fips_encrypt); - winpr_RC4_Free(rdp->rc4_decrypt_key); - winpr_RC4_Free(rdp->rc4_encrypt_key); rdp->fips_decrypt = NULL; rdp->fips_encrypt = NULL; - rdp->rc4_decrypt_key = NULL; - rdp->rc4_encrypt_key = NULL; + + rdp_free_rc4_decrypt_keys(rdp); + rdp_free_rc4_encrypt_keys(rdp); } return ret; @@ -846,10 +850,10 @@ BOOL rdp_server_establish_keys(rdpRdp* rdp, wStream* s) goto end; } - rdp->rc4_decrypt_key = winpr_RC4_New(rdp->decrypt_key, rdp->rc4_key_len); - rdp->rc4_encrypt_key = winpr_RC4_New(rdp->encrypt_key, rdp->rc4_key_len); + if (!rdp_reset_rc4_encrypt_keys(rdp)) + goto end; - if (!rdp->rc4_decrypt_key || !rdp->rc4_encrypt_key) + if (!rdp_reset_rc4_decrypt_keys(rdp)) goto end; ret = tpkt_ensure_stream_consumed(s, length); @@ -860,12 +864,11 @@ BOOL rdp_server_establish_keys(rdpRdp* rdp, wStream* s) { winpr_Cipher_Free(rdp->fips_encrypt); winpr_Cipher_Free(rdp->fips_decrypt); - winpr_RC4_Free(rdp->rc4_encrypt_key); - winpr_RC4_Free(rdp->rc4_decrypt_key); rdp->fips_encrypt = NULL; rdp->fips_decrypt = NULL; - rdp->rc4_encrypt_key = NULL; - rdp->rc4_decrypt_key = NULL; + + rdp_free_rc4_encrypt_keys(rdp); + rdp_free_rc4_decrypt_keys(rdp); } return ret; @@ -1060,7 +1063,7 @@ int rdp_client_connect_demand_active(rdpRdp* rdp, wStream* s) return rc; } - if (freerdp_shall_disconnect(rdp->instance)) + if (freerdp_shall_disconnect_context(rdp->context)) return 0; if (!rdp_send_confirm_active(rdp)) @@ -1132,68 +1135,69 @@ int rdp_client_transition_to_state(rdpRdp* rdp, int state) { int status = 0; + WLog_DBG(TAG, "%s %s --> %s", __FUNCTION__, rdp_get_state_string(rdp), rdp_state_string(state)); switch (state) { case CONNECTION_STATE_INITIAL: - rdp->state = CONNECTION_STATE_INITIAL; + rdp_set_state(rdp, CONNECTION_STATE_INITIAL); break; case CONNECTION_STATE_NEGO: - rdp->state = CONNECTION_STATE_NEGO; + rdp_set_state(rdp, CONNECTION_STATE_NEGO); break; case CONNECTION_STATE_NLA: - rdp->state = CONNECTION_STATE_NLA; + rdp_set_state(rdp, CONNECTION_STATE_NLA); break; case CONNECTION_STATE_MCS_CONNECT: - rdp->state = CONNECTION_STATE_MCS_CONNECT; + rdp_set_state(rdp, CONNECTION_STATE_MCS_CONNECT); break; case CONNECTION_STATE_MCS_ERECT_DOMAIN: - rdp->state = CONNECTION_STATE_MCS_ERECT_DOMAIN; + rdp_set_state(rdp, CONNECTION_STATE_MCS_ERECT_DOMAIN); break; case CONNECTION_STATE_MCS_ATTACH_USER: - rdp->state = CONNECTION_STATE_MCS_ATTACH_USER; + rdp_set_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER); break; case CONNECTION_STATE_MCS_CHANNEL_JOIN: - rdp->state = CONNECTION_STATE_MCS_CHANNEL_JOIN; + rdp_set_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN); break; case CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT: - rdp->state = CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT; + rdp_set_state(rdp, CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT); break; case CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE: - rdp->state = CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE; + rdp_set_state(rdp, CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE); break; case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT: - rdp->state = CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT; + rdp_set_state(rdp, CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT); break; case CONNECTION_STATE_LICENSING: - rdp->state = CONNECTION_STATE_LICENSING; + rdp_set_state(rdp, CONNECTION_STATE_LICENSING); break; case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING: - rdp->state = CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING; + rdp_set_state(rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING); break; case CONNECTION_STATE_CAPABILITIES_EXCHANGE: - rdp->state = CONNECTION_STATE_CAPABILITIES_EXCHANGE; + rdp_set_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); break; case CONNECTION_STATE_FINALIZATION: - rdp->state = CONNECTION_STATE_FINALIZATION; + rdp_set_state(rdp, CONNECTION_STATE_FINALIZATION); update_reset_state(rdp->update); rdp->finalize_sc_pdus = 0; break; case CONNECTION_STATE_ACTIVE: - rdp->state = CONNECTION_STATE_ACTIVE; + rdp_set_state(rdp, CONNECTION_STATE_ACTIVE); { ActivatedEventArgs activatedEvent; rdpContext* context = rdp->context; @@ -1213,8 +1217,8 @@ int rdp_client_transition_to_state(rdpRdp* rdp, int state) ConnectionStateChangeEventArgs stateEvent; rdpContext* context = rdp->context; EventArgsInit(&stateEvent, "libfreerdp"); - stateEvent.state = rdp->state; - stateEvent.active = rdp->state == CONNECTION_STATE_ACTIVE; + stateEvent.state = rdp_get_state(rdp); + stateEvent.active = rdp_get_state(rdp) == CONNECTION_STATE_ACTIVE; PubSub_OnConnectionStateChange(context->pubSub, context, &stateEvent); } @@ -1401,7 +1405,7 @@ BOOL rdp_server_accept_confirm_active(rdpRdp* rdp, wStream* s, UINT16 pduLength) { freerdp_peer* peer = rdp->context->peer; - if (rdp->state != CONNECTION_STATE_CAPABILITIES_EXCHANGE) + if (rdp_get_state(rdp) != CONNECTION_STATE_CAPABILITIES_EXCHANGE) return FALSE; if (!rdp_recv_confirm_active(rdp, s, pduLength)) @@ -1450,74 +1454,76 @@ int rdp_server_transition_to_state(rdpRdp* rdp, int state) { int status = 0; freerdp_peer* client = NULL; + const int cstate = rdp_get_state(rdp); - if (rdp->state >= CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT) + if (cstate >= CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT) client = rdp->context->peer; - if (rdp->state < CONNECTION_STATE_ACTIVE) + if (cstate < CONNECTION_STATE_ACTIVE) { if (client) client->activated = FALSE; } + WLog_DBG(TAG, "%s %s --> %s", __FUNCTION__, rdp_get_state_string(rdp), rdp_state_string(state)); switch (state) { case CONNECTION_STATE_INITIAL: - rdp->state = CONNECTION_STATE_INITIAL; + rdp_set_state(rdp, CONNECTION_STATE_INITIAL); break; case CONNECTION_STATE_NEGO: - rdp->state = CONNECTION_STATE_NEGO; + rdp_set_state(rdp, CONNECTION_STATE_NEGO); break; case CONNECTION_STATE_MCS_CONNECT: - rdp->state = CONNECTION_STATE_MCS_CONNECT; + rdp_set_state(rdp, CONNECTION_STATE_MCS_CONNECT); break; case CONNECTION_STATE_MCS_ERECT_DOMAIN: - rdp->state = CONNECTION_STATE_MCS_ERECT_DOMAIN; + rdp_set_state(rdp, CONNECTION_STATE_MCS_ERECT_DOMAIN); break; case CONNECTION_STATE_MCS_ATTACH_USER: - rdp->state = CONNECTION_STATE_MCS_ATTACH_USER; + rdp_set_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER); break; case CONNECTION_STATE_MCS_CHANNEL_JOIN: - rdp->state = CONNECTION_STATE_MCS_CHANNEL_JOIN; + rdp_set_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN); break; case CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT: - rdp->state = CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT; + rdp_set_state(rdp, CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT); break; case CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE: - rdp->state = CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE; + rdp_set_state(rdp, CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE); break; case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT: - rdp->state = CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT; + rdp_set_state(rdp, CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT); break; case CONNECTION_STATE_LICENSING: - rdp->state = CONNECTION_STATE_LICENSING; + rdp_set_state(rdp, CONNECTION_STATE_LICENSING); break; case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING: - rdp->state = CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING; + rdp_set_state(rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING); break; case CONNECTION_STATE_CAPABILITIES_EXCHANGE: - rdp->state = CONNECTION_STATE_CAPABILITIES_EXCHANGE; + rdp_set_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); rdp->AwaitCapabilities = FALSE; break; case CONNECTION_STATE_FINALIZATION: - rdp->state = CONNECTION_STATE_FINALIZATION; + rdp_set_state(rdp, CONNECTION_STATE_FINALIZATION); rdp->finalize_sc_pdus = 0; break; case CONNECTION_STATE_ACTIVE: - rdp->state = CONNECTION_STATE_ACTIVE; + rdp_set_state(rdp, CONNECTION_STATE_ACTIVE); update_reset_state(rdp->update); if (client) @@ -1534,7 +1540,7 @@ int rdp_server_transition_to_state(rdpRdp* rdp, int state) return -1; } - if (rdp->state >= CONNECTION_STATE_ACTIVE) + if (rdp_get_state(rdp) >= CONNECTION_STATE_ACTIVE) { IFCALLRET(client->Activate, client->activated, client); @@ -1568,7 +1574,7 @@ const char* rdp_client_connection_state_string(int state) } } -const char* rdp_server_connection_state_string(int state) +const char* rdp_state_string(CONNECTION_STATE state) { switch (state) { @@ -1607,9 +1613,21 @@ const char* rdp_server_connection_state_string(int state) } } -int rdp_client_get_state(rdpRdp* rdp) +CONNECTION_STATE rdp_get_state(rdpRdp* rdp) { - if (!rdp) - return -1; + WINPR_ASSERT(rdp); return rdp->state; } + +BOOL rdp_set_state(rdpRdp* rdp, CONNECTION_STATE state) +{ + WINPR_ASSERT(rdp); + rdp->state = state; + return TRUE; +} + +const char* rdp_get_state_string(rdpRdp* rdp) +{ + int state = rdp_get_state(rdp); + return rdp_state_string(state); +} diff --git a/libfreerdp/core/connection.h b/libfreerdp/core/connection.h index 8458c60c7b4c..946f745bd79e 100644 --- a/libfreerdp/core/connection.h +++ b/libfreerdp/core/connection.h @@ -30,25 +30,6 @@ #include #include -enum CONNECTION_STATE -{ - CONNECTION_STATE_INITIAL, - CONNECTION_STATE_NEGO, - CONNECTION_STATE_NLA, - CONNECTION_STATE_MCS_CONNECT, - CONNECTION_STATE_MCS_ERECT_DOMAIN, - CONNECTION_STATE_MCS_ATTACH_USER, - CONNECTION_STATE_MCS_CHANNEL_JOIN, - CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT, - CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE, - CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT, - CONNECTION_STATE_LICENSING, - CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING, - CONNECTION_STATE_CAPABILITIES_EXCHANGE, - CONNECTION_STATE_FINALIZATION, - CONNECTION_STATE_ACTIVE -}; - enum CLIENT_CONNECTION_STATE { CLIENT_STATE_INITIAL, @@ -66,8 +47,9 @@ FREERDP_LOCAL BOOL rdp_client_connect_auto_detect(rdpRdp* rdp, wStream* s); FREERDP_LOCAL int rdp_client_connect_license(rdpRdp* rdp, wStream* s); FREERDP_LOCAL int rdp_client_connect_demand_active(rdpRdp* rdp, wStream* s); FREERDP_LOCAL int rdp_client_transition_to_state(rdpRdp* rdp, int state); -FREERDP_LOCAL const char* rdp_client_connection_state_string(int state); -FREERDP_LOCAL int rdp_client_get_state(rdpRdp* rdp); + +FREERDP_LOCAL CONNECTION_STATE rdp_get_state(rdpRdp* rdp); +FREERDP_LOCAL const char* rdp_state_string(CONNECTION_STATE state); FREERDP_LOCAL BOOL rdp_server_accept_nego(rdpRdp* rdp, wStream* s); FREERDP_LOCAL BOOL rdp_server_accept_mcs_connect_initial(rdpRdp* rdp, wStream* s); @@ -78,6 +60,8 @@ FREERDP_LOCAL BOOL rdp_server_accept_confirm_active(rdpRdp* rdp, wStream* s, UIN FREERDP_LOCAL BOOL rdp_server_establish_keys(rdpRdp* rdp, wStream* s); FREERDP_LOCAL BOOL rdp_server_reactivate(rdpRdp* rdp); FREERDP_LOCAL int rdp_server_transition_to_state(rdpRdp* rdp, int state); -FREERDP_LOCAL const char* rdp_server_connection_state_string(int state); +FREERDP_LOCAL const char* rdp_get_state_string(rdpRdp* rdp); + +FREERDP_LOCAL const char* rdp_client_connection_state_string(int state); #endif /* FREERDP_LIB_CORE_CONNECTION_H */ diff --git a/libfreerdp/core/fastpath.c b/libfreerdp/core/fastpath.c index 7a67638fefd0..0b3549ace4de 100644 --- a/libfreerdp/core/fastpath.c +++ b/libfreerdp/core/fastpath.c @@ -948,7 +948,7 @@ BOOL fastpath_send_multiple_input_pdu(rdpFastPath* fastpath, wStream* s, size_t goto fail; rdp = fastpath->rdp; - state = rdp_client_get_state(rdp); + state = rdp_get_state(rdp); if (state != CONNECTION_STATE_ACTIVE) { WLog_WARN(TAG, "[%s] called before activation [%s]", __FUNCTION__, diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 2ce9cf771432..e2179f4df149 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -52,6 +52,7 @@ #include #include "settings.h" +#include "utils.h" #define TAG FREERDP_TAG("core") @@ -168,7 +169,9 @@ BOOL freerdp_connect(freerdp* instance) #endif freerdp_set_last_error_log(instance->context, FREERDP_ERROR_SUCCESS); clearChannelError(instance->context); - ResetEvent(instance->context->abortEvent); + if (!utils_reset_abort(instance->context)) + return FALSE; + rdp = instance->context->rdp; settings = instance->settings; @@ -181,7 +184,8 @@ BOOL freerdp_connect(freerdp* instance) if (status) status2 = freerdp_channels_pre_connect(instance->context->channels, instance); - if (settings->KeyboardLayout == KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002) + if (settings->KeyboardLayout == KBD_JAPANESE || + settings->KeyboardLayout == KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002) { settings->KeyboardType = 7; settings->KeyboardSubType = 2; @@ -307,7 +311,8 @@ BOOL freerdp_abort_connect(freerdp* instance) if (!instance || !instance->context) return FALSE; - return SetEvent(instance->context->abortEvent); + freerdp_set_last_error_if_not(instance->context, FREERDP_ERROR_CONNECT_CANCELLED); + return utils_abort_connect(instance->context); } BOOL freerdp_get_fds(freerdp* instance, void** rfds, int* rcount, void** wfds, int* wcount) @@ -539,19 +544,31 @@ BOOL freerdp_disconnect_before_reconnect(freerdp* instance) BOOL freerdp_reconnect(freerdp* instance) { - BOOL status; - rdpRdp* rdp = instance->context->rdp; - ResetEvent(instance->context->abortEvent); - status = rdp_client_reconnect(rdp); - return status; + rdpRdp* rdp; + + if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_CONNECT_CANCELLED) + return FALSE; + + rdp = instance->context->rdp; + + if (!utils_reset_abort(instance->context)) + return FALSE; + return rdp_client_reconnect(rdp); } BOOL freerdp_shall_disconnect(freerdp* instance) { - if (!instance || !instance->context) + if (!instance) + return FALSE; + return freerdp_shall_disconnect_context(instance->context); +} + +BOOL freerdp_shall_disconnect_context(rdpContext* context) +{ + if (!context || !context->abortEvent) return FALSE; - if (WaitForSingleObject(instance->context->abortEvent, 0) != WAIT_OBJECT_0) + if (WaitForSingleObject(context->abortEvent, 0) != WAIT_OBJECT_0) return FALSE; return TRUE; @@ -885,7 +902,7 @@ void freerdp_set_last_error_ex(rdpContext* context, UINT32 lastError, const char if (lastError == FREERDP_ERROR_SUCCESS) { - WLog_INFO(TAG, "%s:%s resetting error state", fkt, __FUNCTION__); + WLog_DBG(TAG, "%s:%s resetting error state", fkt, __FUNCTION__); } else if (context->LastError != FREERDP_ERROR_SUCCESS) { @@ -1125,3 +1142,14 @@ const char* freerdp_nego_get_routing_token(rdpContext* context, DWORD* length) return (const char*)nego_get_routing_token(context->rdp->nego, length); } + +CONNECTION_STATE freerdp_get_state(rdpContext* context) +{ + WINPR_ASSERT(context); + return rdp_get_state(context->rdp); +} + +const char* freerdp_state_string(CONNECTION_STATE state) +{ + return rdp_state_string(state); +} diff --git a/libfreerdp/core/gateway/ncacn_http.c b/libfreerdp/core/gateway/ncacn_http.c index cffb378ebe44..75da83d62d4e 100644 --- a/libfreerdp/core/gateway/ncacn_http.c +++ b/libfreerdp/core/gateway/ncacn_http.c @@ -121,6 +121,7 @@ BOOL rpc_ncacn_http_recv_in_channel_response(RpcChannel* inChannel, HttpResponse if (ntlmTokenData && ntlmTokenLength) return ntlm_client_set_input_buffer(ntlm, FALSE, ntlmTokenData, ntlmTokenLength); + free(ntlmTokenData); return TRUE; } @@ -274,5 +275,6 @@ BOOL rpc_ncacn_http_recv_out_channel_response(RpcChannel* outChannel, HttpRespon if (ntlmTokenData && ntlmTokenLength) return ntlm_client_set_input_buffer(ntlm, FALSE, ntlmTokenData, ntlmTokenLength); + free(ntlmTokenData); return TRUE; } diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c index f9ed725015cb..ffcd3d4382ab 100644 --- a/libfreerdp/core/gateway/rdg.c +++ b/libfreerdp/core/gateway/rdg.c @@ -139,6 +139,7 @@ typedef struct WEBSOCKET_STATE state; wStream* responseStreamBuffer; } rdg_http_websocket_context; + typedef enum _CHUNK_STATE { ChunkStateLenghHeader, @@ -256,20 +257,18 @@ static const char* flags_to_string(UINT32 flags, const t_flag_mapping* map, size { size_t x = 0; static char buffer[1024] = { 0 }; - char fields[12]; - memset(buffer, 0, sizeof(buffer)); + char fields[12] = { 0 }; for (x = 0; x < elements; x++) { - if (buffer[0] != '\0') - strcat(buffer, "|"); + const t_flag_mapping* cur = &map[x]; - if ((map[x].code & flags) != 0) - strcat(buffer, map[x].name); + if ((cur->code & flags) != 0) + winpr_str_append(cur->name, buffer, sizeof(buffer), "|"); } sprintf_s(fields, ARRAYSIZE(fields), " [%04" PRIx32 "]", flags); - strcat(buffer, fields); + winpr_str_append(fields, buffer, sizeof(buffer), NULL); return buffer; } @@ -313,7 +312,8 @@ static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT1 /* Read length of the string */ if (rem < 4) { - WLog_ERR(TAG, "[%s]: Could not read stream length, only have % " PRIuz " bytes", rem); + WLog_ERR(TAG, "[%s]: Could not read stream length, only have % " PRIuz " bytes", + __FUNCTION__, rem); return FALSE; } Stream_Read_UINT16(s, strLenBytes); @@ -326,7 +326,7 @@ static BOOL rdg_read_http_unicode_string(wStream* s, const WCHAR** string, UINT1 { WLog_ERR(TAG, "[%s]: Could not read stream data, only have % " PRIuz " bytes, expected %" PRIu16, - rem - 4, strLenBytes); + __FUNCTION__, rem - 4, strLenBytes); return FALSE; } @@ -358,12 +358,15 @@ static BOOL rdg_write_chunked(BIO* bio, wStream* sPacket) len = Stream_Length(sChunk); if (len > INT_MAX) + { + Stream_Free(sChunk, TRUE); return FALSE; + } status = BIO_write(bio, Stream_Buffer(sChunk), (int)len); Stream_Free(sChunk, TRUE); - if (status != len) + if (status != (SSIZE_T)len) return FALSE; return TRUE; @@ -437,7 +440,7 @@ static BOOL rdg_write_websocket(BIO* bio, wStream* sPacket, WEBSOCKET_OPCODE opc status = BIO_write(bio, Stream_Buffer(sWS), Stream_Length(sWS)); Stream_Free(sWS, TRUE); - if (status != fullLen) + if (status != (SSIZE_T)fullLen) return FALSE; return TRUE; @@ -559,11 +562,13 @@ static BOOL rdg_websocket_reply_close(BIO* bio, wStream* s) { uint16_t data; Stream_Read_UINT16(s, data); - Stream_Write_UINT16(s, data ^ maskingKey1); + Stream_Write_UINT16(closeFrame, data ^ maskingKey1); } Stream_SealLength(closeFrame); status = BIO_write(bio, Stream_Buffer(closeFrame), Stream_Length(closeFrame)); + Stream_Free(closeFrame, TRUE); + /* server MUST close socket now. The server is not allowed anymore to * send frames but if he does, nothing bad would happen */ if (status < 0) @@ -742,7 +747,6 @@ static int rdg_websocket_read(BIO* bio, BYTE* pBuffer, size_t size, TAG, "Websocket Server sends data with masking key. This is against RFC 6455."); return -1; } - break; case WebSocketStatePayload: { status = rdg_websocket_handle_payload(bio, pBuffer, size, encodingContext); @@ -761,6 +765,7 @@ static int rdg_websocket_read(BIO* bio, BYTE* pBuffer, size_t size, /* should be unreachable */ return -1; } + static int rdg_chuncked_read(BIO* bio, BYTE* pBuffer, size_t size, rdg_http_encoding_chunked_context* encodingContext) { @@ -832,9 +837,7 @@ static int rdg_chuncked_read(BIO* bio, BYTE* pBuffer, size_t size, else return (effectiveDataLen > 0 ? effectiveDataLen : status); } - *dst = '\0'; - /* strtoul is tricky, error are reported via errno, we also need * to ensure the result does not overflow */ errno = 0; @@ -868,10 +871,12 @@ static int rdg_socket_read(BIO* bio, BYTE* pBuffer, size_t size, rdg_http_encoding_context* encodingContext) { assert(encodingContext != NULL); + if (encodingContext->isWebsocketTransport) { return rdg_websocket_read(bio, pBuffer, size, &encodingContext->context.websocket); } + switch (encodingContext->httpTransferEncoding) { case TransferEncodingIdentity: @@ -881,7 +886,6 @@ static int rdg_socket_read(BIO* bio, BYTE* pBuffer, size_t size, default: return -1; } - return -1; /* should not be reached */ } static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, size_t size, @@ -893,7 +897,6 @@ static BOOL rdg_read_all(rdpTls* tls, BYTE* buffer, size_t size, while (readCount < size) { int status = rdg_socket_read(tls->bio, pBuffer, size - readCount, transferEncoding); - if (status <= 0) { if (!BIO_should_retry(tls->bio)) @@ -1169,7 +1172,7 @@ static wStream* rdg_build_http_request(rdpRdg* rdg, const char* method, goto out; } - http_request_set_transfer_encoding(request, transferEncoding); + http_request_set_transfer_encoding(request, transferEncoding); s = http_request_write(rdg->http, request); out: @@ -1852,7 +1855,6 @@ static BOOL rdg_establish_data_connection(rdpRdg* rdg, rdpTls* tls, const char* rdg->transferEncoding.context.websocket.responseStreamBuffer = NULL; return TRUE; - break; default: return FALSE; } @@ -1899,6 +1901,9 @@ static BOOL rdg_tunnel_connect(rdpRdg* rdg) if (!status) { + assert(rdg); + assert(rdg->context); + assert(rdg->context->rdp); rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED; return FALSE; } @@ -1937,6 +1942,9 @@ BOOL rdg_connect(rdpRdg* rdg, DWORD timeout, BOOL* rpcFallback) if (!status) { + assert(rdg); + assert(rdg->context); + assert(rdg->context->rdp); rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED; return FALSE; } @@ -2013,7 +2021,7 @@ static int rdg_write_websocket_data_packet(rdpRdg* rdg, const BYTE* buf, int isi /* mask as much as possible with 32bit access */ for (streamPos = 0; streamPos + 4 <= isize; streamPos += 4) { - uint32_t masked = *((uint32_t*)((BYTE*)buf + streamPos)) ^ maskingKey; + uint32_t masked = *((const uint32_t*)((const BYTE*)buf + streamPos)) ^ maskingKey; Stream_Write_UINT32(sWS, masked); } @@ -2021,7 +2029,7 @@ static int rdg_write_websocket_data_packet(rdpRdg* rdg, const BYTE* buf, int isi for (; streamPos < isize; streamPos++) { BYTE* partialMask = (BYTE*)(&maskingKey) + streamPos % 4; - BYTE masked = *((BYTE*)((BYTE*)buf + streamPos)) ^ *partialMask; + BYTE masked = *((const BYTE*)((const BYTE*)buf + streamPos)) ^ *partialMask; Stream_Write_UINT8(sWS, masked); } @@ -2068,7 +2076,10 @@ static int rdg_write_chunked_data_packet(rdpRdg* rdg, const BYTE* buf, int isize len = Stream_Length(sChunk); if (len > INT_MAX) + { + Stream_Free(sChunk, TRUE); return -1; + } status = tls_write_all(rdg->tlsIn, Stream_Buffer(sChunk), (int)len); Stream_Free(sChunk, TRUE); @@ -2089,8 +2100,6 @@ static int rdg_write_data_packet(rdpRdg* rdg, const BYTE* buf, int isize) } else return rdg_write_chunked_data_packet(rdg, buf, isize); - - return -1; } static BOOL rdg_process_close_packet(rdpRdg* rdg, wStream* s) @@ -2402,10 +2411,10 @@ static int rdg_bio_gets(BIO* bio, char* str, int size) return -2; } -static long rdg_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2) +static long rdg_bio_ctrl(BIO* in_bio, int cmd, long arg1, void* arg2) { long status = -1; - rdpRdg* rdg = (rdpRdg*)BIO_get_data(bio); + rdpRdg* rdg = (rdpRdg*)BIO_get_data(in_bio); rdpTls* tlsOut = rdg->tlsOut; rdpTls* tlsIn = rdg->tlsIn; @@ -2422,42 +2431,42 @@ static long rdg_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2) } else if (cmd == BIO_C_READ_BLOCKED) { - BIO* bio = tlsOut->bio; - status = BIO_read_blocked(bio); + BIO* cbio = tlsOut->bio; + status = BIO_read_blocked(cbio); } else if (cmd == BIO_C_WRITE_BLOCKED) { - BIO* bio = tlsIn->bio; + BIO* cbio = tlsIn->bio; if (rdg->transferEncoding.isWebsocketTransport) - bio = tlsOut->bio; + cbio = tlsOut->bio; - status = BIO_write_blocked(bio); + status = BIO_write_blocked(cbio); } else if (cmd == BIO_C_WAIT_READ) { int timeout = (int)arg1; - BIO* bio = tlsOut->bio; + BIO* cbio = tlsOut->bio; - if (BIO_read_blocked(bio)) - return BIO_wait_read(bio, timeout); - else if (BIO_write_blocked(bio)) - return BIO_wait_write(bio, timeout); + if (BIO_read_blocked(cbio)) + return BIO_wait_read(cbio, timeout); + else if (BIO_write_blocked(cbio)) + return BIO_wait_write(cbio, timeout); else status = 1; } else if (cmd == BIO_C_WAIT_WRITE) { int timeout = (int)arg1; - BIO* bio = tlsIn->bio; + BIO* cbio = tlsIn->bio; if (rdg->transferEncoding.isWebsocketTransport) - bio = tlsOut->bio; + cbio = tlsOut->bio; - if (BIO_write_blocked(bio)) - status = BIO_wait_write(bio, timeout); - else if (BIO_read_blocked(bio)) - status = BIO_wait_read(bio, timeout); + if (BIO_write_blocked(cbio)) + status = BIO_wait_write(cbio, timeout); + else if (BIO_read_blocked(cbio)) + status = BIO_wait_read(cbio, timeout); else status = 1; } @@ -2473,7 +2482,23 @@ static long rdg_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2) */ status = BIO_ctrl(tlsOut->bio, cmd, arg1, arg2); } - +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + else if (cmd == BIO_CTRL_GET_KTLS_SEND) + { + /* Even though BIO_get_ktls_send says that returning negative values is valid + * openssl internal sources are full of if(!BIO_get_ktls_send && ) stuff. This has some + * nasty sideeffects. return 0 as proper no KTLS offloading flag + */ + status = 0; + } + else if (cmd == BIO_CTRL_GET_KTLS_RECV) + { + /* Even though BIO_get_ktls_recv says that returning negative values is valid + * there is no reason to trust trust negative values are implemented right everywhere + */ + status = 0; + } +#endif return status; } @@ -2619,6 +2644,7 @@ void rdg_free(rdpRdg* rdg) BIO_free_all(rdg->frontBio); DeleteCriticalSection(&rdg->writeSection); + if (rdg->transferEncoding.isWebsocketTransport) { if (rdg->transferEncoding.context.websocket.responseStreamBuffer != NULL) diff --git a/libfreerdp/core/gateway/rpc.c b/libfreerdp/core/gateway/rpc.c index c5f85922e07b..4d6bf9222c67 100644 --- a/libfreerdp/core/gateway/rpc.c +++ b/libfreerdp/core/gateway/rpc.c @@ -24,6 +24,7 @@ #endif #include +#include #include #include #include @@ -46,6 +47,7 @@ #include "rpc_client.h" #include "rpc.h" +#include "rts.h" #define TAG FREERDP_TAG("core.gateway.rpc") @@ -88,8 +90,10 @@ static const char* PTYPE_STRINGS[] = { "PTYPE_REQUEST", "PTYPE_PING", * */ -void rpc_pdu_header_print(rpcconn_hdr_t* header) +void rpc_pdu_header_print(const rpcconn_hdr_t* header) { + assert(header); + WLog_INFO(TAG, "rpc_vers: %" PRIu8 "", header->common.rpc_vers); WLog_INFO(TAG, "rpc_vers_minor: %" PRIu8 "", header->common.rpc_vers_minor); @@ -139,26 +143,30 @@ void rpc_pdu_header_print(rpcconn_hdr_t* header) } } -void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_common_hdr_t* header) +rpcconn_common_hdr_t rpc_pdu_header_init(const rdpRpc* rpc) { - header->rpc_vers = rpc->rpc_vers; - header->rpc_vers_minor = rpc->rpc_vers_minor; - header->packed_drep[0] = rpc->packed_drep[0]; - header->packed_drep[1] = rpc->packed_drep[1]; - header->packed_drep[2] = rpc->packed_drep[2]; - header->packed_drep[3] = rpc->packed_drep[3]; + rpcconn_common_hdr_t header = { 0 }; + assert(rpc); + + header.rpc_vers = rpc->rpc_vers; + header.rpc_vers_minor = rpc->rpc_vers_minor; + header.packed_drep[0] = rpc->packed_drep[0]; + header.packed_drep[1] = rpc->packed_drep[1]; + header.packed_drep[2] = rpc->packed_drep[2]; + header.packed_drep[3] = rpc->packed_drep[3]; + return header; } -UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment) +size_t rpc_offset_align(size_t* offset, size_t alignment) { - UINT32 pad; + size_t pad; pad = *offset; *offset = (*offset + alignment - 1) & ~(alignment - 1); pad = *offset - pad; return pad; } -UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad) +size_t rpc_offset_pad(size_t* offset, size_t pad) { *offset += pad; return pad; @@ -239,64 +247,67 @@ UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad) * */ -BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* length) +BOOL rpc_get_stub_data_info(const rpcconn_hdr_t* header, size_t* poffset, size_t* length) { - UINT32 alloc_hint = 0; - rpcconn_hdr_t* header; + size_t used = 0; + size_t offset = 0; + BOOL rc = FALSE; UINT32 frag_length; UINT32 auth_length; - UINT32 auth_pad_length; + UINT32 auth_pad_length = 0; UINT32 sec_trailer_offset; - rpc_sec_trailer* sec_trailer; - *offset = RPC_COMMON_FIELDS_LENGTH; - header = ((rpcconn_hdr_t*)buffer); + const rpc_sec_trailer* sec_trailer = NULL; + + assert(header); + assert(poffset); + assert(length); + + offset = RPC_COMMON_FIELDS_LENGTH; switch (header->common.ptype) { case PTYPE_RESPONSE: - *offset += 8; - rpc_offset_align(offset, 8); - alloc_hint = header->response.alloc_hint; + offset += 8; + rpc_offset_align(&offset, 8); + sec_trailer = &header->response.auth_verifier; break; case PTYPE_REQUEST: - *offset += 4; - rpc_offset_align(offset, 8); - alloc_hint = header->request.alloc_hint; + offset += 4; + rpc_offset_align(&offset, 8); + sec_trailer = &header->request.auth_verifier; break; case PTYPE_RTS: - *offset += 4; + offset += 4; break; default: WLog_ERR(TAG, "Unknown PTYPE: 0x%02" PRIX8 "", header->common.ptype); - return FALSE; + goto fail; } - if (!length) - return TRUE; + frag_length = header->common.frag_length; + auth_length = header->common.auth_length; + + if (poffset) + *poffset = offset; - if (header->common.ptype == PTYPE_REQUEST) + /* The fragment must be larger than the authentication trailer */ + used = offset + auth_length + 8ull; + if (sec_trailer) { - UINT32 sec_trailer_offset; - sec_trailer_offset = header->common.frag_length - header->common.auth_length - 8; - *length = sec_trailer_offset - *offset; - return TRUE; + auth_pad_length = sec_trailer->auth_pad_length; + used += sec_trailer->auth_pad_length; } - frag_length = header->common.frag_length; - auth_length = header->common.auth_length; + if (frag_length < used) + goto fail; + + if (!length) + return TRUE; + sec_trailer_offset = frag_length - auth_length - 8; - sec_trailer = (rpc_sec_trailer*)&buffer[sec_trailer_offset]; - auth_pad_length = sec_trailer->auth_pad_length; -#if 0 - WLog_DBG(TAG, - "sec_trailer: type: %"PRIu8" level: %"PRIu8" pad_length: %"PRIu8" reserved: %"PRIu8" context_id: %"PRIu32"", - sec_trailer->auth_type, sec_trailer->auth_level, - sec_trailer->auth_pad_length, sec_trailer->auth_reserved, - sec_trailer->auth_context_id); -#endif /** * According to [MS-RPCE], auth_pad_length is the number of padding @@ -310,18 +321,21 @@ BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* l auth_length, (frag_length - (sec_trailer_offset + 8))); } - *length = frag_length - auth_length - 24 - 8 - auth_pad_length; - return TRUE; + *length = sec_trailer_offset - auth_pad_length - offset; + + rc = TRUE; +fail: + return rc; } SSIZE_T rpc_channel_read(RpcChannel* channel, wStream* s, size_t length) { int status; - if (!channel) + if (!channel || (length > INT32_MAX)) return -1; - status = BIO_read(channel->tls->bio, Stream_Pointer(s), length); + status = BIO_read(channel->tls->bio, Stream_Pointer(s), (INT32)length); if (status > 0) { @@ -340,10 +354,10 @@ SSIZE_T rpc_channel_write(RpcChannel* channel, const BYTE* data, size_t length) { int status; - if (!channel) + if (!channel || (length > INT32_MAX)) return -1; - status = tls_write_all(channel->tls, data, length); + status = tls_write_all(channel->tls, data, (INT32)length); return status; } @@ -629,7 +643,7 @@ static void rpc_virtual_connection_free(RpcVirtualConnection* connection) free(connection); } -static BOOL rpc_channel_tls_connect(RpcChannel* channel, int timeout) +static BOOL rpc_channel_tls_connect(RpcChannel* channel, UINT32 timeout) { int sockfd; rdpTls* tls; @@ -719,7 +733,7 @@ static BOOL rpc_channel_tls_connect(RpcChannel* channel, int timeout) return TRUE; } -static int rpc_in_channel_connect(RpcInChannel* inChannel, int timeout) +static int rpc_in_channel_connect(RpcInChannel* inChannel, UINT32 timeout) { rdpContext* context; @@ -814,7 +828,7 @@ int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, int timeout) return 1; } -BOOL rpc_connect(rdpRpc* rpc, int timeout) +BOOL rpc_connect(rdpRpc* rpc, UINT32 timeout) { RpcInChannel* inChannel; RpcOutChannel* outChannel; @@ -840,7 +854,15 @@ BOOL rpc_connect(rdpRpc* rpc, int timeout) rdpRpc* rpc_new(rdpTransport* transport) { - rdpRpc* rpc = (rdpRpc*)calloc(1, sizeof(rdpRpc)); + rdpContext* context; + rdpRpc* rpc; + + assert(transport); + + context = transport->context; + assert(context); + + rpc = (rdpRpc*)calloc(1, sizeof(rdpRpc)); if (!rpc) return NULL; @@ -848,7 +870,7 @@ rdpRpc* rpc_new(rdpTransport* transport) rpc->State = RPC_CLIENT_STATE_INITIAL; rpc->transport = transport; rpc->settings = transport->settings; - rpc->context = transport->context; + rpc->context = context; rpc->SendSeqNum = 0; rpc->ntlm = ntlm_new(); @@ -873,7 +895,7 @@ rdpRpc* rpc_new(rdpTransport* transport) rpc->CurrentKeepAliveInterval = rpc->KeepAliveInterval; rpc->CurrentKeepAliveTime = 0; rpc->CallId = 2; - rpc->client = rpc_client_new(rpc->context, rpc->max_recv_frag); + rpc->client = rpc_client_new(context, rpc->max_recv_frag); if (!rpc->client) goto out_free; diff --git a/libfreerdp/core/gateway/rpc.h b/libfreerdp/core/gateway/rpc.h index 1a58fe450a4e..26eebf131a42 100644 --- a/libfreerdp/core/gateway/rpc.h +++ b/libfreerdp/core/gateway/rpc.h @@ -73,7 +73,6 @@ typedef struct _RPC_PDU #include "../tcp.h" #include "../transport.h" -#include "rts.h" #include "http.h" #include "ntlm.h" @@ -525,7 +524,8 @@ typedef struct rpcconn_common_hdr_t header; } rpcconn_shutdown_hdr_t; -typedef union { +typedef union +{ rpcconn_common_hdr_t common; rpcconn_alter_context_hdr_t alter_context; rpcconn_alter_context_response_hdr_t alter_context_response; @@ -767,14 +767,14 @@ struct rdp_rpc RpcVirtualConnection* VirtualConnection; }; -FREERDP_LOCAL void rpc_pdu_header_print(rpcconn_hdr_t* header); -FREERDP_LOCAL void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_common_hdr_t* header); +FREERDP_LOCAL void rpc_pdu_header_print(const rpcconn_hdr_t* header); +FREERDP_LOCAL rpcconn_common_hdr_t rpc_pdu_header_init(const rdpRpc* rpc); -FREERDP_LOCAL UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment); -FREERDP_LOCAL UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad); +FREERDP_LOCAL size_t rpc_offset_align(size_t* offset, size_t alignment); +FREERDP_LOCAL size_t rpc_offset_pad(size_t* offset, size_t pad); -FREERDP_LOCAL BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* header, UINT32* offset, - UINT32* length); +FREERDP_LOCAL BOOL rpc_get_stub_data_info(const rpcconn_hdr_t* header, size_t* offset, + size_t* length); FREERDP_LOCAL SSIZE_T rpc_channel_write(RpcChannel* channel, const BYTE* data, size_t length); @@ -794,7 +794,7 @@ FREERDP_LOCAL BOOL rpc_virtual_connection_transition_to_state(rdpRpc* rpc, RpcVirtualConnection* connection, VIRTUAL_CONNECTION_STATE state); -FREERDP_LOCAL BOOL rpc_connect(rdpRpc* rpc, int timeout); +FREERDP_LOCAL BOOL rpc_connect(rdpRpc* rpc, UINT32 timeout); FREERDP_LOCAL rdpRpc* rpc_new(rdpTransport* transport); FREERDP_LOCAL void rpc_free(rdpRpc* rpc); diff --git a/libfreerdp/core/gateway/rpc_bind.c b/libfreerdp/core/gateway/rpc_bind.c index 98ed9a9dc55c..bdf54d5a0b63 100644 --- a/libfreerdp/core/gateway/rpc_bind.c +++ b/libfreerdp/core/gateway/rpc_bind.c @@ -22,11 +22,14 @@ #endif #include +#include #include #include "rpc_client.h" +#include "rts.h" + #include "rpc_bind.h" #define TAG FREERDP_TAG("core.gateway.rpc") @@ -106,18 +109,32 @@ int rpc_send_bind_pdu(rdpRpc* rpc) { BOOL continueNeeded = FALSE; int status = -1; - BYTE* buffer = NULL; + wStream* buffer = NULL; UINT32 offset; - UINT32 length; RpcClientCall* clientCall; p_cont_elem_t* p_cont_elem; - rpcconn_bind_hdr_t* bind_pdu = NULL; + rpcconn_bind_hdr_t bind_pdu = { 0 }; BOOL promptPassword = FALSE; - rdpSettings* settings = rpc->settings; - freerdp* instance = (freerdp*)settings->instance; - RpcVirtualConnection* connection = rpc->VirtualConnection; - RpcInChannel* inChannel = connection->DefaultInChannel; + rdpSettings* settings; + freerdp* instance; + RpcVirtualConnection* connection; + RpcInChannel* inChannel; const SecBuffer* sbuffer = NULL; + + assert(rpc); + + settings = rpc->settings; + assert(settings); + + instance = (freerdp*)settings->instance; + assert(instance); + + connection = rpc->VirtualConnection; + + assert(connection); + + inChannel = connection->DefaultInChannel; + WLog_DBG(TAG, "Sending Bind PDU"); ntlm_free(rpc->ntlm); rpc->ntlm = ntlm_new(); @@ -180,36 +197,31 @@ int rpc_send_bind_pdu(rdpRpc* rpc) if (!continueNeeded) goto fail; - bind_pdu = (rpcconn_bind_hdr_t*)calloc(1, sizeof(rpcconn_bind_hdr_t)); - - if (!bind_pdu) - goto fail; - sbuffer = ntlm_client_get_output_buffer(rpc->ntlm); if (!sbuffer) goto fail; - rpc_pdu_header_init(rpc, &bind_pdu->header); - bind_pdu->header.auth_length = (UINT16)sbuffer->cbBuffer; - bind_pdu->auth_verifier.auth_value = sbuffer->pvBuffer; - bind_pdu->header.ptype = PTYPE_BIND; - bind_pdu->header.pfc_flags = + bind_pdu.header = rpc_pdu_header_init(rpc); + bind_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer; + bind_pdu.auth_verifier.auth_value = sbuffer->pvBuffer; + bind_pdu.header.ptype = PTYPE_BIND; + bind_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_SUPPORT_HEADER_SIGN | PFC_CONC_MPX; - bind_pdu->header.call_id = 2; - bind_pdu->max_xmit_frag = rpc->max_xmit_frag; - bind_pdu->max_recv_frag = rpc->max_recv_frag; - bind_pdu->assoc_group_id = 0; - bind_pdu->p_context_elem.n_context_elem = 2; - bind_pdu->p_context_elem.reserved = 0; - bind_pdu->p_context_elem.reserved2 = 0; - bind_pdu->p_context_elem.p_cont_elem = - calloc(bind_pdu->p_context_elem.n_context_elem, sizeof(p_cont_elem_t)); - - if (!bind_pdu->p_context_elem.p_cont_elem) + bind_pdu.header.call_id = 2; + bind_pdu.max_xmit_frag = rpc->max_xmit_frag; + bind_pdu.max_recv_frag = rpc->max_recv_frag; + bind_pdu.assoc_group_id = 0; + bind_pdu.p_context_elem.n_context_elem = 2; + bind_pdu.p_context_elem.reserved = 0; + bind_pdu.p_context_elem.reserved2 = 0; + bind_pdu.p_context_elem.p_cont_elem = + calloc(bind_pdu.p_context_elem.n_context_elem, sizeof(p_cont_elem_t)); + + if (!bind_pdu.p_context_elem.p_cont_elem) goto fail; - p_cont_elem = &bind_pdu->p_context_elem.p_cont_elem[0]; + p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[0]; p_cont_elem->p_cont_id = 0; p_cont_elem->n_transfer_syn = 1; p_cont_elem->reserved = 0; @@ -222,7 +234,7 @@ int rpc_send_bind_pdu(rdpRpc* rpc) CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &NDR_UUID, sizeof(p_uuid_t)); p_cont_elem->transfer_syntaxes[0].if_version = NDR_SYNTAX_IF_VERSION; - p_cont_elem = &bind_pdu->p_context_elem.p_cont_elem[1]; + p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[1]; p_cont_elem->p_cont_id = 1; p_cont_elem->n_transfer_syn = 1; p_cont_elem->reserved = 0; @@ -236,32 +248,23 @@ int rpc_send_bind_pdu(rdpRpc* rpc) CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &BTFN_UUID, sizeof(p_uuid_t)); p_cont_elem->transfer_syntaxes[0].if_version = BTFN_SYNTAX_IF_VERSION; offset = 116; - bind_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4); - bind_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT; - bind_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; - bind_pdu->auth_verifier.auth_reserved = 0x00; - bind_pdu->auth_verifier.auth_context_id = 0x00000000; - offset += (8 + bind_pdu->header.auth_length); - bind_pdu->header.frag_length = offset; - buffer = (BYTE*)malloc(bind_pdu->header.frag_length); + + bind_pdu.auth_verifier.auth_type = RPC_C_AUTHN_WINNT; + bind_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; + bind_pdu.auth_verifier.auth_reserved = 0x00; + bind_pdu.auth_verifier.auth_context_id = 0x00000000; + offset += (8 + bind_pdu.header.auth_length); + bind_pdu.header.frag_length = offset; + + buffer = Stream_New(NULL, bind_pdu.header.frag_length); if (!buffer) goto fail; - CopyMemory(buffer, bind_pdu, 24); - CopyMemory(&buffer[24], &bind_pdu->p_context_elem, 4); - CopyMemory(&buffer[28], &bind_pdu->p_context_elem.p_cont_elem[0], 24); - CopyMemory(&buffer[52], bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes, 20); - CopyMemory(&buffer[72], &bind_pdu->p_context_elem.p_cont_elem[1], 24); - CopyMemory(&buffer[96], bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes, 20); - offset = 116; - rpc_offset_pad(&offset, bind_pdu->auth_verifier.auth_pad_length); - CopyMemory(&buffer[offset], &bind_pdu->auth_verifier.auth_type, 8); - CopyMemory(&buffer[offset + 8], bind_pdu->auth_verifier.auth_value, - bind_pdu->header.auth_length); - offset += (8 + bind_pdu->header.auth_length); - length = bind_pdu->header.frag_length; - clientCall = rpc_client_call_new(bind_pdu->header.call_id, 0); + if (!rts_write_pdu_bind(buffer, &bind_pdu)) + goto fail; + + clientCall = rpc_client_call_new(bind_pdu.header.call_id, 0); if (!clientCall) goto fail; @@ -272,22 +275,19 @@ int rpc_send_bind_pdu(rdpRpc* rpc) goto fail; } - status = rpc_in_channel_send_pdu(inChannel, buffer, length); + Stream_SealLength(buffer); + status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer)); fail: - if (bind_pdu) + if (bind_pdu.p_context_elem.p_cont_elem) { - if (bind_pdu->p_context_elem.p_cont_elem) - { - free(bind_pdu->p_context_elem.p_cont_elem[0].transfer_syntaxes); - free(bind_pdu->p_context_elem.p_cont_elem[1].transfer_syntaxes); - } - - free(bind_pdu->p_context_elem.p_cont_elem); + free(bind_pdu.p_context_elem.p_cont_elem[0].transfer_syntaxes); + free(bind_pdu.p_context_elem.p_cont_elem[1].transfer_syntaxes); } - free(bind_pdu); - free(buffer); + free(bind_pdu.p_context_elem.p_cont_elem); + + Stream_Free(buffer, TRUE); return (status > 0) ? 1 : -1; } @@ -317,31 +317,47 @@ int rpc_send_bind_pdu(rdpRpc* rpc) * example. */ -int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +BOOL rpc_recv_bind_ack_pdu(rdpRpc* rpc, wStream* s) { + BOOL rc = FALSE; BOOL continueNeeded = FALSE; - BYTE* auth_data; - rpcconn_hdr_t* header; - header = (rpcconn_hdr_t*)buffer; + const BYTE* auth_data; + size_t pos, end; + rpcconn_hdr_t header = { 0 }; + + assert(rpc); + assert(rpc->ntlm); + assert(s); + + pos = Stream_GetPosition(s); + if (!rts_read_pdu_header(s, &header)) + goto fail; + WLog_DBG(TAG, "Receiving BindAck PDU"); - if (!rpc || !rpc->ntlm) - return -1; + rpc->max_recv_frag = header.bind_ack.max_xmit_frag; + rpc->max_xmit_frag = header.bind_ack.max_recv_frag; - rpc->max_recv_frag = header->bind_ack.max_xmit_frag; - rpc->max_xmit_frag = header->bind_ack.max_recv_frag; - auth_data = buffer + (header->common.frag_length - header->common.auth_length); + /* Get the correct offset in the input data and pass that on as input buffer. + * rts_read_pdu_header did already do consistency checks */ + end = Stream_GetPosition(s); + Stream_SetPosition(s, pos + header.common.frag_length - header.common.auth_length); + auth_data = Stream_Pointer(s); + Stream_SetPosition(s, end); - if (!ntlm_client_set_input_buffer(rpc->ntlm, TRUE, auth_data, header->common.auth_length)) - return -1; + if (!ntlm_client_set_input_buffer(rpc->ntlm, TRUE, auth_data, header.common.auth_length)) + goto fail; if (!ntlm_authenticate(rpc->ntlm, &continueNeeded)) - return -1; + goto fail; if (continueNeeded) - return -1; + goto fail; - return (int)length; + rc = TRUE; +fail: + rts_free_pdu_header(&header, FALSE); + return rc; } /** @@ -354,68 +370,63 @@ int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc) { int status = -1; - BYTE* buffer; - UINT32 offset; - UINT32 length; + wStream* buffer; + size_t offset; const SecBuffer* sbuffer; RpcClientCall* clientCall; - rpcconn_rpc_auth_3_hdr_t* auth_3_pdu; - RpcVirtualConnection* connection = rpc->VirtualConnection; - RpcInChannel* inChannel = connection->DefaultInChannel; - WLog_DBG(TAG, "Sending RpcAuth3 PDU"); - auth_3_pdu = (rpcconn_rpc_auth_3_hdr_t*)calloc(1, sizeof(rpcconn_rpc_auth_3_hdr_t)); + rpcconn_rpc_auth_3_hdr_t auth_3_pdu = { 0 }; + RpcVirtualConnection* connection; + RpcInChannel* inChannel; - if (!auth_3_pdu) - return -1; + assert(rpc); + + connection = rpc->VirtualConnection; + assert(connection); + + inChannel = connection->DefaultInChannel; + assert(inChannel); + + WLog_DBG(TAG, "Sending RpcAuth3 PDU"); sbuffer = ntlm_client_get_output_buffer(rpc->ntlm); if (!sbuffer) - { - free(auth_3_pdu); return -1; - } - rpc_pdu_header_init(rpc, &auth_3_pdu->header); - auth_3_pdu->header.auth_length = (UINT16)sbuffer->cbBuffer; - auth_3_pdu->auth_verifier.auth_value = sbuffer->pvBuffer; - auth_3_pdu->header.ptype = PTYPE_RPC_AUTH_3; - auth_3_pdu->header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX; - auth_3_pdu->header.call_id = 2; - auth_3_pdu->max_xmit_frag = rpc->max_xmit_frag; - auth_3_pdu->max_recv_frag = rpc->max_recv_frag; + auth_3_pdu.header = rpc_pdu_header_init(rpc); + auth_3_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer; + auth_3_pdu.auth_verifier.auth_value = sbuffer->pvBuffer; + auth_3_pdu.header.ptype = PTYPE_RPC_AUTH_3; + auth_3_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX; + auth_3_pdu.header.call_id = 2; + auth_3_pdu.max_xmit_frag = rpc->max_xmit_frag; + auth_3_pdu.max_recv_frag = rpc->max_recv_frag; offset = 20; - auth_3_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4); - auth_3_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT; - auth_3_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; - auth_3_pdu->auth_verifier.auth_reserved = 0x00; - auth_3_pdu->auth_verifier.auth_context_id = 0x00000000; - offset += (8 + auth_3_pdu->header.auth_length); - auth_3_pdu->header.frag_length = offset; - buffer = (BYTE*)malloc(auth_3_pdu->header.frag_length); + auth_3_pdu.auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4); + auth_3_pdu.auth_verifier.auth_type = RPC_C_AUTHN_WINNT; + auth_3_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY; + auth_3_pdu.auth_verifier.auth_reserved = 0x00; + auth_3_pdu.auth_verifier.auth_context_id = 0x00000000; + offset += (8 + auth_3_pdu.header.auth_length); + auth_3_pdu.header.frag_length = offset; + + buffer = Stream_New(NULL, auth_3_pdu.header.frag_length); if (!buffer) - { - free(auth_3_pdu); return -1; - } - CopyMemory(buffer, auth_3_pdu, 20); - offset = 20; - rpc_offset_pad(&offset, auth_3_pdu->auth_verifier.auth_pad_length); - CopyMemory(&buffer[offset], &auth_3_pdu->auth_verifier.auth_type, 8); - CopyMemory(&buffer[offset + 8], auth_3_pdu->auth_verifier.auth_value, - auth_3_pdu->header.auth_length); - offset += (8 + auth_3_pdu->header.auth_length); - length = auth_3_pdu->header.frag_length; - clientCall = rpc_client_call_new(auth_3_pdu->header.call_id, 0); + if (!rts_write_pdu_auth3(buffer, &auth_3_pdu)) + goto fail; + + clientCall = rpc_client_call_new(auth_3_pdu.header.call_id, 0); if (ArrayList_Add(rpc->client->ClientCallList, clientCall) >= 0) { - status = rpc_in_channel_send_pdu(inChannel, buffer, length); + Stream_SealLength(buffer); + status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer)); } - free(auth_3_pdu); - free(buffer); +fail: + Stream_Free(buffer, TRUE); return (status > 0) ? 1 : -1; } diff --git a/libfreerdp/core/gateway/rpc_bind.h b/libfreerdp/core/gateway/rpc_bind.h index 759555f07a73..69758e5219cd 100644 --- a/libfreerdp/core/gateway/rpc_bind.h +++ b/libfreerdp/core/gateway/rpc_bind.h @@ -35,7 +35,7 @@ FREERDP_LOCAL extern const p_uuid_t BTFN_UUID; #define BTFN_SYNTAX_IF_VERSION 0x00000001 FREERDP_LOCAL int rpc_send_bind_pdu(rdpRpc* rpc); -FREERDP_LOCAL int rpc_recv_bind_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); +FREERDP_LOCAL BOOL rpc_recv_bind_ack_pdu(rdpRpc* rpc, wStream* s); FREERDP_LOCAL int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc); #endif /* FREERDP_LIB_CORE_GATEWAY_RPC_BIND_H */ diff --git a/libfreerdp/core/gateway/rpc_client.c b/libfreerdp/core/gateway/rpc_client.c index 845bbe4b8598..1ef0c04a627f 100644 --- a/libfreerdp/core/gateway/rpc_client.c +++ b/libfreerdp/core/gateway/rpc_client.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,9 @@ #include "rpc_bind.h" #include "rpc_fault.h" #include "rpc_client.h" +#include "rts_signature.h" + +#include "../utils.h" #include "../rdp.h" #include "../proxy.h" @@ -99,7 +103,7 @@ static int rpc_client_receive_pipe_write(RpcClient* client, const BYTE* buffer, int rpc_client_receive_pipe_read(RpcClient* client, BYTE* buffer, size_t length) { int index = 0; - int status = 0; + size_t status = 0; int nchunks = 0; DataChunk chunks[2]; @@ -122,7 +126,10 @@ int rpc_client_receive_pipe_read(RpcClient* client, BYTE* buffer, size_t length) ResetEvent(client->PipeEvent); LeaveCriticalSection(&(client->PipeLock)); - return status; + + if (status > INT_MAX) + return -1; + return (int)status; } static int rpc_client_transition_to_state(rdpRpc* rpc, RPC_CLIENT_STATE state) @@ -173,8 +180,15 @@ static int rpc_client_transition_to_state(rdpRpc* rpc, RPC_CLIENT_STATE state) static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) { int status = -1; - rpcconn_rts_hdr_t* rts; - rdpTsg* tsg = rpc->transport->tsg; + rdpTsg* tsg; + + assert(rpc); + assert(pdu); + + Stream_SealLength(pdu->s); + Stream_SetPosition(pdu->s, 0); + + tsg = rpc->transport->tsg; if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED) { @@ -187,17 +201,13 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) break; case VIRTUAL_CONNECTION_STATE_WAIT_A3W: - rts = (rpcconn_rts_hdr_t*)Stream_Buffer(pdu->s); - - if (!rts_match_pdu_signature(&RTS_PDU_CONN_A3_SIGNATURE, rts)) + if (!rts_match_pdu_signature(&RTS_PDU_CONN_A3_SIGNATURE, pdu->s, NULL)) { WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/A3"); return -1; } - status = rts_recv_CONN_A3_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s)); - - if (status < 0) + if (!rts_recv_CONN_A3_pdu(rpc, pdu->s)) { WLog_ERR(TAG, "rts_recv_CONN_A3_pdu failure"); return -1; @@ -209,17 +219,13 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) break; case VIRTUAL_CONNECTION_STATE_WAIT_C2: - rts = (rpcconn_rts_hdr_t*)Stream_Buffer(pdu->s); - - if (!rts_match_pdu_signature(&RTS_PDU_CONN_C2_SIGNATURE, rts)) + if (!rts_match_pdu_signature(&RTS_PDU_CONN_C2_SIGNATURE, pdu->s, NULL)) { WLog_ERR(TAG, "unexpected RTS PDU: Expected CONN/C2"); return -1; } - status = rts_recv_CONN_C2_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s)); - - if (status < 0) + if (!rts_recv_CONN_C2_pdu(rpc, pdu->s)) { WLog_ERR(TAG, "rts_recv_CONN_C2_pdu failure"); return -1; @@ -252,7 +258,7 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) { if (pdu->Type == PTYPE_BIND_ACK) { - if (rpc_recv_bind_ack_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s)) <= 0) + if (!rpc_recv_bind_ack_pdu(rpc, pdu->s)) { WLog_ERR(TAG, "rpc_recv_bind_ack_pdu failure"); return -1; @@ -301,90 +307,117 @@ static int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu) static int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment) { - BYTE* buffer; + int rc = -1; RPC_PDU* pdu; - UINT32 StubOffset; - UINT32 StubLength; + size_t StubOffset; + size_t StubLength; RpcClientCall* call; - rpcconn_hdr_t* header; + rpcconn_hdr_t header = { 0 }; + + assert(rpc); + assert(rpc->client); + assert(fragment); + pdu = rpc->client->pdu; - buffer = (BYTE*)Stream_Buffer(fragment); - header = (rpcconn_hdr_t*)Stream_Buffer(fragment); + assert(pdu); + + Stream_SealLength(fragment); + Stream_SetPosition(fragment, 0); - if (header->common.ptype == PTYPE_RESPONSE) + if (!rts_read_pdu_header(fragment, &header)) + goto fail; + + if (header.common.ptype == PTYPE_RESPONSE) { - rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length; + rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header.common.frag_length; rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= - header->common.frag_length; + header.common.frag_length; if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < (rpc->ReceiveWindow / 2)) { - if (rts_send_flow_control_ack_pdu(rpc) < 0) - return -1; + if (!rts_send_flow_control_ack_pdu(rpc)) + goto fail; } - if (!rpc_get_stub_data_info(rpc, buffer, &StubOffset, &StubLength)) + if (!rpc_get_stub_data_info(&header, &StubOffset, &StubLength)) { WLog_ERR(TAG, "expected stub"); - return -1; + goto fail; } if (StubLength == 4) { - if ((header->common.call_id == rpc->PipeCallId) && - (header->common.pfc_flags & PFC_LAST_FRAG)) + if ((header.common.call_id == rpc->PipeCallId) && + (header.common.pfc_flags & PFC_LAST_FRAG)) { /* End of TsProxySetupReceivePipe */ TerminateEventArgs e; - rpc->result = *((UINT32*)&buffer[StubOffset]); - freerdp_abort_connect(rpc->context->instance); - tsg_set_state(rpc->transport->tsg, TSG_STATE_TUNNEL_CLOSE_PENDING); + rdpContext* context = rpc->transport->context; + rdpTsg* tsg = rpc->transport->tsg; + + assert(context); + + if (Stream_Length(fragment) < StubOffset + 4) + goto fail; + Stream_SetPosition(fragment, StubOffset); + Stream_Read_UINT32(fragment, rpc->result); + + utils_abort_connect(context); + tsg_set_state(tsg, TSG_STATE_TUNNEL_CLOSE_PENDING); EventArgsInit(&e, "freerdp"); e.code = 0; - PubSub_OnTerminate(rpc->context->pubSub, rpc->context, &e); - return 0; + PubSub_OnTerminate(context->pubSub, context, &e); + rc = 0; + goto success; } - if (header->common.call_id != rpc->PipeCallId) + if (header.common.call_id != rpc->PipeCallId) { /* Ignoring non-TsProxySetupReceivePipe Response */ - return 0; + rc = 0; + goto success; } } if (rpc->StubFragCount == 0) - rpc->StubCallId = header->common.call_id; + rpc->StubCallId = header.common.call_id; - if (rpc->StubCallId != header->common.call_id) + if (rpc->StubCallId != header.common.call_id) { WLog_ERR(TAG, "invalid call_id: actual: %" PRIu32 ", expected: %" PRIu32 ", frag_count: %" PRIu32 "", - rpc->StubCallId, header->common.call_id, rpc->StubFragCount); + rpc->StubCallId, header.common.call_id, rpc->StubFragCount); } call = rpc_client_call_find_by_id(rpc->client, rpc->StubCallId); if (!call) - return -1; + goto fail; if (call->OpNum != TsProxySetupReceivePipeOpnum) { - if (!Stream_EnsureCapacity(pdu->s, header->response.alloc_hint)) - return -1; + const rpcconn_response_hdr_t* response = + (const rpcconn_response_hdr_t*)&header.response; + if (!Stream_EnsureCapacity(pdu->s, response->alloc_hint)) + goto fail; + + if (Stream_Length(fragment) < StubOffset + StubLength) + goto fail; - Stream_Write(pdu->s, &buffer[StubOffset], StubLength); + Stream_SetPosition(fragment, StubOffset); + Stream_Write(pdu->s, Stream_Pointer(fragment), StubLength); rpc->StubFragCount++; - if (header->response.alloc_hint == StubLength) + if (response->alloc_hint == StubLength) { pdu->Flags = RPC_PDU_FLAG_STUB; pdu->Type = PTYPE_RESPONSE; pdu->CallId = rpc->StubCallId; - Stream_SealLength(pdu->s); + if (rpc_client_recv_pdu(rpc, pdu) < 0) - return -1; + goto fail; rpc_pdu_reset(pdu); rpc->StubFragCount = 0; rpc->StubCallId = 0; @@ -392,75 +425,84 @@ static int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment) } else { - rpc_client_receive_pipe_write(rpc->client, &buffer[StubOffset], (size_t)StubLength); + const rpcconn_response_hdr_t* response = &header.response; + if (Stream_Length(fragment) < StubOffset + StubLength) + goto fail; + Stream_SetPosition(fragment, StubOffset); + rpc_client_receive_pipe_write(rpc->client, Stream_Pointer(fragment), + (size_t)StubLength); rpc->StubFragCount++; - if (header->response.alloc_hint == StubLength) + if (response->alloc_hint == StubLength) { rpc->StubFragCount = 0; rpc->StubCallId = 0; } } - return 1; + goto success; } - else if (header->common.ptype == PTYPE_RTS) + else if (header.common.ptype == PTYPE_RTS) { if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED) { pdu->Flags = 0; - pdu->Type = header->common.ptype; - pdu->CallId = header->common.call_id; + pdu->Type = header.common.ptype; + pdu->CallId = header.common.call_id; if (!Stream_EnsureCapacity(pdu->s, Stream_Length(fragment))) - return -1; + goto fail; - Stream_Write(pdu->s, buffer, Stream_Length(fragment)); - Stream_SealLength(pdu->s); + Stream_Write(pdu->s, Stream_Buffer(fragment), Stream_Length(fragment)); if (rpc_client_recv_pdu(rpc, pdu) < 0) - return -1; + goto fail; rpc_pdu_reset(pdu); } else { - if (rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length) < 0) - return -1; + if (!rts_recv_out_of_sequence_pdu(rpc, fragment, &header)) + goto fail; } - return 1; + goto success; } - else if (header->common.ptype == PTYPE_BIND_ACK) + else if (header.common.ptype == PTYPE_BIND_ACK) { pdu->Flags = 0; - pdu->Type = header->common.ptype; - pdu->CallId = header->common.call_id; + pdu->Type = header.common.ptype; + pdu->CallId = header.common.call_id; if (!Stream_EnsureCapacity(pdu->s, Stream_Length(fragment))) - return -1; + goto fail; - Stream_Write(pdu->s, buffer, Stream_Length(fragment)); - Stream_SealLength(pdu->s); + Stream_Write(pdu->s, Stream_Buffer(fragment), Stream_Length(fragment)); if (rpc_client_recv_pdu(rpc, pdu) < 0) - return -1; + goto fail; rpc_pdu_reset(pdu); - return 1; + goto success; } - else if (header->common.ptype == PTYPE_FAULT) + else if (header.common.ptype == PTYPE_FAULT) { - rpc_recv_fault_pdu(header->fault.status); - return -1; + const rpcconn_fault_hdr_t* fault = (const rpcconn_fault_hdr_t*)&header.fault; + rpc_recv_fault_pdu(fault->status); + goto fail; } else { - WLog_ERR(TAG, "unexpected RPC PDU type 0x%02" PRIX8 "", header->common.ptype); - return -1; + WLog_ERR(TAG, "unexpected RPC PDU type 0x%02" PRIX8 "", header.common.ptype); + goto fail; } - return 1; +success: + rc = (rc < 0) ? 1 : 0; /* In case of default error return change to 1, otherwise we already set + the return code */ +fail: + rts_free_pdu_header(&header, FALSE); + return rc; } static int rpc_client_default_out_channel_recv(rdpRpc* rpc) @@ -510,7 +552,7 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) /* Send CONN/A1 PDU over OUT channel */ - if (rts_send_CONN_A1_pdu(rpc) < 0) + if (!rts_send_CONN_A1_pdu(rpc)) { http_response_free(response); WLog_ERR(TAG, "rpc_send_CONN_A1_pdu error!"); @@ -550,7 +592,8 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) if (statusCode == HTTP_STATUS_DENIED) { - freerdp_set_last_error_if_not(rpc->context, FREERDP_ERROR_AUTHENTICATION_FAILED); + rdpContext* context = rpc->context; + freerdp_set_last_error_if_not(context, FREERDP_ERROR_AUTHENTICATION_FAILED); } http_response_free(response); @@ -564,12 +607,13 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) } else { - wStream* fragment; - rpcconn_common_hdr_t* header; - fragment = rpc->client->ReceiveFragment; + wStream* fragment = rpc->client->ReceiveFragment; while (1) { + size_t pos; + rpcconn_common_hdr_t header = { 0 }; + while (Stream_GetPosition(fragment) < RPC_COMMON_FIELDS_LENGTH) { status = rpc_channel_read(&outChannel->common, fragment, @@ -582,22 +626,27 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) return 0; } - header = (rpcconn_common_hdr_t*)Stream_Buffer(fragment); + pos = Stream_GetPosition(fragment); + Stream_SetPosition(fragment, 0); - if (header->frag_length > rpc->max_recv_frag) + /* Ignore errors, the PDU might not be complete. */ + rts_read_common_pdu_header(fragment, &header); + Stream_SetPosition(fragment, pos); + + if (header.frag_length > rpc->max_recv_frag) { WLog_ERR(TAG, "rpc_client_recv: invalid fragment size: %" PRIu16 " (max: %" PRIu16 ")", - header->frag_length, rpc->max_recv_frag); + header.frag_length, rpc->max_recv_frag); winpr_HexDump(TAG, WLOG_ERROR, Stream_Buffer(fragment), Stream_GetPosition(fragment)); return -1; } - while (Stream_GetPosition(fragment) < header->frag_length) + while (Stream_GetPosition(fragment) < header.frag_length) { status = rpc_channel_read(&outChannel->common, fragment, - header->frag_length - Stream_GetPosition(fragment)); + header.frag_length - Stream_GetPosition(fragment)); if (status < 0) { @@ -605,14 +654,12 @@ static int rpc_client_default_out_channel_recv(rdpRpc* rpc) return -1; } - if (Stream_GetPosition(fragment) < header->frag_length) + if (Stream_GetPosition(fragment) < header.frag_length) return 0; } { /* complete fragment received */ - Stream_SealLength(fragment); - Stream_SetPosition(fragment, 0); status = rpc_client_recv_fragment(rpc, fragment); if (status < 0) @@ -664,10 +711,10 @@ static int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc) if (rpc_ncacn_http_send_out_channel_request(&nextOutChannel->common, TRUE)) { rpc_ncacn_http_ntlm_uninit(&nextOutChannel->common); - status = rts_send_OUT_R1_A3_pdu(rpc); - if (status >= 0) + if (rts_send_OUT_R1_A3_pdu(rpc)) { + status = 1; rpc_out_channel_transition_to_state( nextOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_A6W); } @@ -688,11 +735,14 @@ static int rpc_client_nondefault_out_channel_recv(rdpRpc* rpc) break; + case CLIENT_OUT_CHANNEL_STATE_INITIAL: + case CLIENT_OUT_CHANNEL_STATE_CONNECTED: + case CLIENT_OUT_CHANNEL_STATE_NEGOTIATED: default: WLog_ERR(TAG, "rpc_client_nondefault_out_channel_recv: Unexpected message %08" PRIx32, nextOutChannel->State); - return -1; + status = -1; } http_response_free(response); @@ -770,7 +820,7 @@ int rpc_client_in_channel_recv(rdpRpc* rpc) /* Send CONN/B1 PDU over IN channel */ - if (rts_send_CONN_B1_pdu(rpc) < 0) + if (!rts_send_CONN_B1_pdu(rpc)) { WLog_ERR(TAG, "rpc_send_CONN_B1_pdu error!"); http_response_free(response); @@ -811,8 +861,8 @@ int rpc_client_in_channel_recv(rdpRpc* rpc) RpcClientCall* rpc_client_call_find_by_id(RpcClient* client, UINT32 CallId) { - int index; - int count; + size_t index; + size_t count; RpcClientCall* clientCall = NULL; if (!client) @@ -857,18 +907,23 @@ static void rpc_array_client_call_free(void* call) rpc_client_call_free((RpcClientCall*)call); } -int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length) +int rpc_in_channel_send_pdu(RpcInChannel* inChannel, const BYTE* buffer, size_t length) { - int status; + SSIZE_T status; RpcClientCall* clientCall; - rpcconn_common_hdr_t* header; + wStream s; + rpcconn_common_hdr_t header = { 0 }; + status = rpc_channel_write(&inChannel->common, buffer, length); if (status <= 0) return -1; - header = (rpcconn_common_hdr_t*)buffer; - clientCall = rpc_client_call_find_by_id(inChannel->common.client, header->call_id); + Stream_StaticInit(&s, buffer, length); + if (!rts_read_common_pdu_header(&s, &header)) + return -1; + + clientCall = rpc_client_call_find_by_id(inChannel->common.client, header.call_id); clientCall->State = RPC_CLIENT_CALL_STATE_DISPATCHED; /* @@ -878,7 +933,7 @@ int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length * variables specified by this abstract data model. */ - if (header->ptype == PTYPE_REQUEST) + if (header.ptype == PTYPE_REQUEST) { inChannel->BytesSent += status; inChannel->SenderAvailableWindow -= status; @@ -889,7 +944,7 @@ int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length BOOL rpc_client_write_call(rdpRpc* rpc, wStream* s, UINT16 opnum) { - UINT32 offset; + size_t offset; BYTE* buffer = NULL; UINT32 stub_data_pad; SecBuffer Buffers[2] = { 0 }; @@ -937,7 +992,7 @@ BOOL rpc_client_write_call(rdpRpc* rpc, wStream* s, UINT16 opnum) if (size < 0) goto fail; - rpc_pdu_header_init(rpc, &request_pdu.header); + request_pdu.header = rpc_pdu_header_init(rpc); request_pdu.header.ptype = PTYPE_REQUEST; request_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; request_pdu.header.auth_length = (UINT16)size; @@ -1026,7 +1081,7 @@ static BOOL rpc_client_resolve_gateway(rdpSettings* settings, char** host, UINT1 const char* peerHostname = freerdp_settings_get_string(settings, FreeRDP_GatewayHostname); const char* proxyUsername = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername); const char* proxyPassword = freerdp_settings_get_string(settings, FreeRDP_GatewayPassword); - *port = freerdp_settings_get_uint32(settings, FreeRDP_GatewayPort); + *port = (UINT16)freerdp_settings_get_uint32(settings, FreeRDP_GatewayPort); *isProxy = proxy_prepare(settings, &peerHostname, port, &proxyUsername, &proxyPassword); result = freerdp_tcp_resolve_host(peerHostname, *port, 0); @@ -1084,9 +1139,6 @@ RpcClient* rpc_client_new(rdpContext* context, UINT32 max_recv_frag) goto fail; obj = ArrayList_Object(client->ClientCallList); - if (!obj) - goto fail; - obj->fnObjectFree = rpc_array_client_call_free; return client; fail: diff --git a/libfreerdp/core/gateway/rpc_client.h b/libfreerdp/core/gateway/rpc_client.h index af0b8ce13c51..7b509def0e56 100644 --- a/libfreerdp/core/gateway/rpc_client.h +++ b/libfreerdp/core/gateway/rpc_client.h @@ -31,7 +31,8 @@ FREERDP_LOCAL RpcClientCall* rpc_client_call_find_by_id(RpcClient* client, UINT3 FREERDP_LOCAL RpcClientCall* rpc_client_call_new(UINT32 CallId, UINT32 OpNum); FREERDP_LOCAL void rpc_client_call_free(RpcClientCall* client_call); -FREERDP_LOCAL int rpc_in_channel_send_pdu(RpcInChannel* inChannel, BYTE* buffer, UINT32 length); +FREERDP_LOCAL int rpc_in_channel_send_pdu(RpcInChannel* inChannel, const BYTE* buffer, + size_t length); FREERDP_LOCAL int rpc_client_in_channel_recv(rdpRpc* rpc); FREERDP_LOCAL int rpc_client_out_channel_recv(rdpRpc* rpc); diff --git a/libfreerdp/core/gateway/rpc_fault.c b/libfreerdp/core/gateway/rpc_fault.c index dcf59d62ba5b..12d466cd9380 100644 --- a/libfreerdp/core/gateway/rpc_fault.c +++ b/libfreerdp/core/gateway/rpc_fault.c @@ -154,8 +154,7 @@ static const RPC_FAULT_CODE RPC_FAULT_CODES[] = { DEFINE_RPC_FAULT_CODE(RPC_S_COOKIE_AUTH_FAILED, CAT_GATEWAY), DEFINE_RPC_FAULT_CODE(RPC_S_GROUP_MEMBER_NOT_FOUND, CAT_GATEWAY), DEFINE_RPC_FAULT_CODE(EPT_S_CANT_CREATE, CAT_GATEWAY), - DEFINE_RPC_FAULT_CODE(RPC_S_INVALID_OBJECT, CAT_GATEWAY), - { 0, NULL, NULL } + DEFINE_RPC_FAULT_CODE(RPC_S_INVALID_OBJECT, CAT_GATEWAY) }; static const RPC_FAULT_CODE RPC_TSG_FAULT_CODES[] = { @@ -207,8 +206,7 @@ static const RPC_FAULT_CODE RPC_TSG_FAULT_CODES[] = { { HRESULT_CODE(E_PROXY_REAUTH_NAP_FAILED), "E_PROXY_REAUTH_NAP_FAILED", CAT_GATEWAY }, { HRESULT_CODE(E_PROXY_CONNECTIONABORTED), "E_PROXY_CONNECTIONABORTED", CAT_GATEWAY }, { HRESULT_CODE(E_PROXY_NOCERTAVAILABLE), "E_PROXY_NOCERTAVAILABLE", CAT_GATEWAY }, - { HRESULT_CODE(RPC_S_CALL_CANCELLED), "RPC_S_CALL_CANCELLED", CAT_GATEWAY }, - { 0, NULL, NULL } + { HRESULT_CODE(RPC_S_CALL_CANCELLED), "RPC_S_CALL_CANCELLED", CAT_GATEWAY } }; /** @@ -361,29 +359,32 @@ const char* rpc_error_to_string(UINT32 code) size_t index; static char buffer[1024]; - for (index = 0; RPC_FAULT_CODES[index].name != NULL; index++) + for (index = 0; index < ARRAYSIZE(RPC_FAULT_CODES); index++) { - if (RPC_FAULT_CODES[index].code == code) + const RPC_FAULT_CODE* const current = &RPC_FAULT_CODES[index]; + if (current->code == code) { - sprintf_s(buffer, ARRAYSIZE(buffer), "%s", RPC_FAULT_CODES[index].name); + sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name); goto out; } } - for (index = 0; RPC_TSG_FAULT_CODES[index].name != NULL; index++) + for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++) { - if (RPC_TSG_FAULT_CODES[index].code == code) + const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index]; + if (current->code == code) { - sprintf_s(buffer, ARRAYSIZE(buffer), "%s", RPC_TSG_FAULT_CODES[index].name); + sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name); goto out; } } - for (index = 0; RPC_TSG_FAULT_CODES[index].name != NULL; index++) + for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++) { - if (RPC_TSG_FAULT_CODES[index].code == HRESULT_CODE(code)) + const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index]; + if (current->code == HRESULT_CODE(code)) { - sprintf_s(buffer, ARRAYSIZE(buffer), "%s", RPC_TSG_FAULT_CODES[index].name); + sprintf_s(buffer, ARRAYSIZE(buffer), "%s", current->name); goto out; } } @@ -397,22 +398,25 @@ const char* rpc_error_to_category(UINT32 code) { size_t index; - for (index = 0; RPC_FAULT_CODES[index].category != NULL; index++) + for (index = 0; index < ARRAYSIZE(RPC_FAULT_CODES); index++) { - if (RPC_FAULT_CODES[index].code == code) - return RPC_FAULT_CODES[index].category; + const RPC_FAULT_CODE* const current = &RPC_FAULT_CODES[index]; + if (current->code == code) + return current->category; } - for (index = 0; RPC_TSG_FAULT_CODES[index].category != NULL; index++) + for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++) { - if (RPC_TSG_FAULT_CODES[index].code == code) - return RPC_TSG_FAULT_CODES[index].category; + const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index]; + if (current->code == code) + return current->category; } - for (index = 0; RPC_TSG_FAULT_CODES[index].category != NULL; index++) + for (index = 0; index < ARRAYSIZE(RPC_TSG_FAULT_CODES); index++) { - if (RPC_TSG_FAULT_CODES[index].code == HRESULT_CODE(code)) - return RPC_TSG_FAULT_CODES[index].category; + const RPC_FAULT_CODE* const current = &RPC_TSG_FAULT_CODES[index]; + if (current->code == HRESULT_CODE(code)) + return current->category; } return "UNKNOWN"; diff --git a/libfreerdp/core/gateway/rts.c b/libfreerdp/core/gateway/rts.c index c003b1ea07ac..85e65b0de3c6 100644 --- a/libfreerdp/core/gateway/rts.c +++ b/libfreerdp/core/gateway/rts.c @@ -21,6 +21,7 @@ #include "config.h" #endif +#include #include #include #include @@ -29,6 +30,7 @@ #include "ncacn_http.h" #include "rpc_client.h" +#include "rts_signature.h" #include "rts.h" @@ -67,545 +69,1645 @@ * */ +static const char* rts_pdu_ptype_to_string(UINT32 ptype) +{ + switch (ptype) + { + case PTYPE_REQUEST: + return "PTYPE_REQUEST"; + case PTYPE_PING: + return "PTYPE_PING"; + case PTYPE_RESPONSE: + return "PTYPE_RESPONSE"; + case PTYPE_FAULT: + return "PTYPE_FAULT"; + case PTYPE_WORKING: + return "PTYPE_WORKING"; + case PTYPE_NOCALL: + return "PTYPE_NOCALL"; + case PTYPE_REJECT: + return "PTYPE_REJECT"; + case PTYPE_ACK: + return "PTYPE_ACK"; + case PTYPE_CL_CANCEL: + return "PTYPE_CL_CANCEL"; + case PTYPE_FACK: + return "PTYPE_FACK"; + case PTYPE_CANCEL_ACK: + return "PTYPE_CANCEL_ACK"; + case PTYPE_BIND: + return "PTYPE_BIND"; + case PTYPE_BIND_ACK: + return "PTYPE_BIND_ACK"; + case PTYPE_BIND_NAK: + return "PTYPE_BIND_NAK"; + case PTYPE_ALTER_CONTEXT: + return "PTYPE_ALTER_CONTEXT"; + case PTYPE_ALTER_CONTEXT_RESP: + return "PTYPE_ALTER_CONTEXT_RESP"; + case PTYPE_RPC_AUTH_3: + return "PTYPE_RPC_AUTH_3"; + case PTYPE_SHUTDOWN: + return "PTYPE_SHUTDOWN"; + case PTYPE_CO_CANCEL: + return "PTYPE_CO_CANCEL"; + case PTYPE_ORPHANED: + return "PTYPE_ORPHANED"; + case PTYPE_RTS: + return "PTYPE_RTS"; + default: + return "UNKNOWN"; + } +} static rpcconn_rts_hdr_t rts_pdu_header_init(void) { - rpcconn_rts_hdr_t header = { 0 }; - header.header.rpc_vers = 5; - header.header.rpc_vers_minor = 0; - header.header.ptype = PTYPE_RTS; - header.header.packed_drep[0] = 0x10; - header.header.packed_drep[1] = 0x00; - header.header.packed_drep[2] = 0x00; - header.header.packed_drep[3] = 0x00; - header.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; - header.header.auth_length = 0; - header.header.call_id = 0; + rpcconn_rts_hdr_t header = { 0 }; + header.header.rpc_vers = 5; + header.header.rpc_vers_minor = 0; + header.header.ptype = PTYPE_RTS; + header.header.packed_drep[0] = 0x10; + header.header.packed_drep[1] = 0x00; + header.header.packed_drep[2] = 0x00; + header.header.packed_drep[3] = 0x00; + header.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG; + header.header.auth_length = 0; + header.header.call_id = 0; + + return header; +} + +static BOOL rts_align_stream(wStream* s, size_t alignment) +{ + size_t pos, pad; + + assert(s); + assert(alignment > 0); + + pos = Stream_GetPosition(s); + pad = rpc_offset_align(&pos, alignment); + return Stream_SafeSeek(s, pad); +} + +static char* sdup(const void* src, size_t length) +{ + char* dst; + assert(src || (length == 0)); + if (length == 0) + return NULL; + + dst = calloc(length + 1, sizeof(char)); + if (!dst) + return NULL; + memcpy(dst, src, length); + return dst; +} + +static BOOL rts_write_common_pdu_header(wStream* s, const rpcconn_common_hdr_t* header) +{ + assert(s); + assert(header); + if (!Stream_EnsureRemainingCapacity(s, sizeof(rpcconn_common_hdr_t))) + return FALSE; + + Stream_Write_UINT8(s, header->rpc_vers); + Stream_Write_UINT8(s, header->rpc_vers_minor); + Stream_Write_UINT8(s, header->ptype); + Stream_Write_UINT8(s, header->pfc_flags); + Stream_Write(s, header->packed_drep, ARRAYSIZE(header->packed_drep)); + Stream_Write_UINT16(s, header->frag_length); + Stream_Write_UINT16(s, header->auth_length); + Stream_Write_UINT32(s, header->call_id); + return TRUE; +} + +BOOL rts_read_common_pdu_header(wStream* s, rpcconn_common_hdr_t* header) +{ + size_t left; + assert(s); + assert(header); + + if (Stream_GetRemainingLength(s) < sizeof(rpcconn_common_hdr_t)) + return FALSE; + + Stream_Read_UINT8(s, header->rpc_vers); + Stream_Read_UINT8(s, header->rpc_vers_minor); + Stream_Read_UINT8(s, header->ptype); + Stream_Read_UINT8(s, header->pfc_flags); + Stream_Read(s, header->packed_drep, ARRAYSIZE(header->packed_drep)); + Stream_Read_UINT16(s, header->frag_length); + Stream_Read_UINT16(s, header->auth_length); + Stream_Read_UINT32(s, header->call_id); + + if (header->frag_length < sizeof(rpcconn_common_hdr_t)) + return FALSE; + if (header->auth_length > header->frag_length - 8ull) + return FALSE; + + left = Stream_GetRemainingLength(s); + if (left < header->frag_length - sizeof(rpcconn_common_hdr_t)) + return FALSE; + + return TRUE; +} + +static BOOL rts_read_auth_verifier_no_checks(wStream* s, auth_verifier_co_t* auth, + const rpcconn_common_hdr_t* header, size_t* startPos) +{ + assert(s); + assert(auth); + assert(header); + + assert(header->frag_length > header->auth_length); + + if (startPos) + *startPos = Stream_GetPosition(s); + + /* Read the auth verifier and check padding matches frag_length */ + { + const size_t expected = header->frag_length - header->auth_length - 8; + + Stream_SetPosition(s, expected); + if (Stream_GetRemainingLength(s) < sizeof(auth_verifier_co_t)) + return FALSE; + + Stream_Read_UINT8(s, auth->auth_type); + Stream_Read_UINT8(s, auth->auth_level); + Stream_Read_UINT8(s, auth->auth_pad_length); + Stream_Read_UINT8(s, auth->auth_reserved); + Stream_Read_UINT32(s, auth->auth_context_id); + } + + if (header->auth_length != 0) + { + const void* ptr = Stream_Pointer(s); + if (!Stream_SafeSeek(s, header->auth_length)) + return FALSE; + auth->auth_value = (BYTE*)sdup(ptr, header->auth_length); + if (auth->auth_value == NULL) + return FALSE; + } + + return TRUE; +} + +static BOOL rts_read_auth_verifier(wStream* s, auth_verifier_co_t* auth, + const rpcconn_common_hdr_t* header) +{ + size_t pos; + assert(s); + assert(auth); + assert(header); + + if (!rts_read_auth_verifier_no_checks(s, auth, header, &pos)) + return FALSE; + + { + const size_t expected = header->frag_length - header->auth_length - 8; + assert(pos + auth->auth_pad_length == expected); + } + + return TRUE; +} + +static BOOL rts_read_auth_verifier_with_stub(wStream* s, auth_verifier_co_t* auth, + rpcconn_common_hdr_t* header) +{ + size_t pos; + size_t alloc_hint = 0; + BYTE** ptr = NULL; + + if (!rts_read_auth_verifier_no_checks(s, auth, header, &pos)) + return FALSE; + + switch (header->ptype) + { + case PTYPE_FAULT: + { + rpcconn_fault_hdr_t* hdr = (rpcconn_fault_hdr_t*)header; + alloc_hint = hdr->alloc_hint; + ptr = &hdr->stub_data; + } + break; + case PTYPE_RESPONSE: + { + rpcconn_response_hdr_t* hdr = (rpcconn_response_hdr_t*)header; + alloc_hint = hdr->alloc_hint; + ptr = &hdr->stub_data; + } + break; + case PTYPE_REQUEST: + { + rpcconn_request_hdr_t* hdr = (rpcconn_request_hdr_t*)header; + alloc_hint = hdr->alloc_hint; + ptr = &hdr->stub_data; + } + break; + default: + return FALSE; + } + + if (alloc_hint > 0) + { + const size_t size = + header->frag_length - header->auth_length - 8 - auth->auth_pad_length - pos; + const void* src = Stream_Buffer(s) + pos; + + *ptr = (BYTE*)sdup(src, size); + if (!*ptr) + return FALSE; + } + + return TRUE; +} + +static void rts_free_auth_verifier(auth_verifier_co_t* auth) +{ + if (!auth) + return; + free(auth->auth_value); +} + +static BOOL rts_write_auth_verifier(wStream* s, const auth_verifier_co_t* auth, + const rpcconn_common_hdr_t* header) +{ + size_t pos; + UINT8 auth_pad_length = 0; + + assert(s); + assert(auth); + assert(header); + + /* Align start to a multiple of 4 */ + pos = Stream_GetPosition(s); + if ((pos % 4) != 0) + { + auth_pad_length = 4 - (pos % 4); + if (!Stream_EnsureRemainingCapacity(s, auth_pad_length)) + return FALSE; + Stream_Zero(s, auth_pad_length); + } + + assert(header->frag_length + 8ull > header->auth_length); + { + size_t pos = Stream_GetPosition(s); + size_t expected = header->frag_length - header->auth_length - 8; + + assert(pos == expected); + } + + if (!Stream_EnsureRemainingCapacity(s, sizeof(auth_verifier_co_t))) + return FALSE; + + Stream_Write_UINT8(s, auth->auth_type); + Stream_Write_UINT8(s, auth->auth_level); + Stream_Write_UINT8(s, auth_pad_length); + Stream_Write_UINT8(s, 0); /* auth->auth_reserved */ + Stream_Write_UINT32(s, auth->auth_context_id); + + if (!Stream_EnsureRemainingCapacity(s, header->auth_length)) + return FALSE; + Stream_Write(s, auth->auth_value, header->auth_length); + return TRUE; +} + +static BOOL rts_read_version(wStream* s, p_rt_version_t* version) +{ + assert(s); + assert(version); + + if (Stream_GetRemainingLength(s) < 2 * sizeof(UINT8)) + return FALSE; + Stream_Read_UINT8(s, version->major); + Stream_Read_UINT8(s, version->minor); + return TRUE; +} + +void rts_free_supported_versions(p_rt_versions_supported_t* versions) +{ + if (!versions) + return; + free(versions->p_protocols); + versions->p_protocols = NULL; +} + +static BOOL rts_read_supported_versions(wStream* s, p_rt_versions_supported_t* versions) +{ + BYTE x; + + assert(s); + assert(versions); + + if (Stream_GetRemainingLength(s) < sizeof(UINT8)) + return FALSE; + + Stream_Read_UINT8(s, versions->n_protocols); /* count */ + + if (versions->n_protocols > 0) + { + versions->p_protocols = calloc(versions->n_protocols, sizeof(p_rt_version_t)); + if (!versions->p_protocols) + return FALSE; + } + for (x = 0; x < versions->n_protocols; x++) + { + p_rt_version_t* version = &versions->p_protocols[x]; + if (!rts_read_version(s, version)) /* size_is(n_protocols) */ + { + rts_free_supported_versions(versions); + return FALSE; + } + } + + return TRUE; +} + +static BOOL rts_read_port_any(wStream* s, port_any_t* port) +{ + const void* ptr; + + assert(s); + assert(port); + + if (Stream_GetRemainingLength(s) < sizeof(UINT16)) + return FALSE; + + Stream_Read_UINT16(s, port->length); + if (port->length == 0) + return TRUE; + + ptr = Stream_Pointer(s); + if (!Stream_SafeSeek(s, port->length)) + return FALSE; + port->port_spec = sdup(ptr, port->length); + return port->port_spec != NULL; +} + +static void rts_free_port_any(port_any_t* port) +{ + if (!port) + return; + free(port->port_spec); +} + +static BOOL rts_read_uuid(wStream* s, p_uuid_t* uuid) +{ + assert(s); + assert(uuid); + + if (Stream_GetRemainingLength(s) < sizeof(p_uuid_t)) + return FALSE; + + Stream_Read_UINT32(s, uuid->time_low); + Stream_Read_UINT16(s, uuid->time_mid); + Stream_Read_UINT16(s, uuid->time_hi_and_version); + Stream_Read_UINT8(s, uuid->clock_seq_hi_and_reserved); + Stream_Read_UINT8(s, uuid->clock_seq_low); + Stream_Read(s, uuid->node, ARRAYSIZE(uuid->node)); + return TRUE; +} + +static BOOL rts_write_uuid(wStream* s, const p_uuid_t* uuid) +{ + assert(s); + assert(uuid); + + if (!Stream_EnsureRemainingCapacity(s, sizeof(p_uuid_t))) + return FALSE; + + Stream_Write_UINT32(s, uuid->time_low); + Stream_Write_UINT16(s, uuid->time_mid); + Stream_Write_UINT16(s, uuid->time_hi_and_version); + Stream_Write_UINT8(s, uuid->clock_seq_hi_and_reserved); + Stream_Write_UINT8(s, uuid->clock_seq_low); + Stream_Write(s, uuid->node, ARRAYSIZE(uuid->node)); + return TRUE; +} + +static p_syntax_id_t* rts_syntax_id_new(size_t count) +{ + return calloc(count, sizeof(p_syntax_id_t)); +} + +static void rts_syntax_id_free(p_syntax_id_t* ptr) +{ + free(ptr); +} + +static BOOL rts_read_syntax_id(wStream* s, p_syntax_id_t* syntax_id) +{ + assert(s); + assert(syntax_id); + + if (!rts_read_uuid(s, &syntax_id->if_uuid)) + return FALSE; + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + + Stream_Read_UINT32(s, syntax_id->if_version); + return TRUE; +} + +static BOOL rts_write_syntax_id(wStream* s, const p_syntax_id_t* syntax_id) +{ + assert(s); + assert(syntax_id); + + if (!rts_write_uuid(s, &syntax_id->if_uuid)) + return FALSE; + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return FALSE; + + Stream_Write_UINT32(s, syntax_id->if_version); + return TRUE; +} + +p_cont_elem_t* rts_context_elem_new(size_t count) +{ + p_cont_elem_t* ctx = calloc(count, sizeof(p_cont_elem_t)); + return ctx; +} + +void rts_context_elem_free(p_cont_elem_t* ptr) +{ + if (!ptr) + return; + rts_syntax_id_free(ptr->transfer_syntaxes); + free(ptr); +} + +static BOOL rts_read_context_elem(wStream* s, p_cont_elem_t* element) +{ + BYTE x; + assert(s); + assert(element); + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + + Stream_Read_UINT16(s, element->p_cont_id); + Stream_Read_UINT8(s, element->n_transfer_syn); /* number of items */ + Stream_Read_UINT8(s, element->reserved); /* alignment pad, m.b.z. */ + + if (!rts_read_syntax_id(s, &element->abstract_syntax)) /* transfer syntax list */ + return FALSE; + + if (element->n_transfer_syn > 0) + { + element->transfer_syntaxes = rts_syntax_id_new(element->n_transfer_syn); + if (!element->transfer_syntaxes) + return FALSE; + for (x = 0; x < element->n_transfer_syn; x++) + { + p_syntax_id_t* syn = &element->transfer_syntaxes[x]; + if (!rts_read_syntax_id(s, syn)) /* size_is(n_transfer_syn) */ + return FALSE; + } + } + + return TRUE; +} + +static BOOL rts_write_context_elem(wStream* s, const p_cont_elem_t* element) +{ + BYTE x; + assert(s); + assert(element); + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return FALSE; + Stream_Write_UINT16(s, element->p_cont_id); + Stream_Write_UINT8(s, element->n_transfer_syn); /* number of items */ + Stream_Write_UINT8(s, element->reserved); /* alignment pad, m.b.z. */ + if (!rts_write_syntax_id(s, &element->abstract_syntax)) /* transfer syntax list */ + return FALSE; + + for (x = 0; x < element->n_transfer_syn; x++) + { + const p_syntax_id_t* syn = &element->transfer_syntaxes[x]; + if (!rts_write_syntax_id(s, syn)) /* size_is(n_transfer_syn) */ + return FALSE; + } + + return TRUE; +} + +static BOOL rts_read_context_list(wStream* s, p_cont_list_t* list) +{ + BYTE x; + + assert(s); + assert(list); + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + Stream_Read_UINT8(s, list->n_context_elem); /* number of items */ + Stream_Read_UINT8(s, list->reserved); /* alignment pad, m.b.z. */ + Stream_Read_UINT16(s, list->reserved2); /* alignment pad, m.b.z. */ + + if (list->n_context_elem > 0) + { + list->p_cont_elem = rts_context_elem_new(list->n_context_elem); + if (!list->p_cont_elem) + return FALSE; + for (x = 0; x < list->n_context_elem; x++) + { + p_cont_elem_t* element = &list->p_cont_elem[x]; + if (!rts_read_context_elem(s, element)) + return FALSE; + } + } + return TRUE; +} + +static void rts_free_context_list(p_cont_list_t* list) +{ + if (!list) + return; + rts_context_elem_free(list->p_cont_elem); +} + +static BOOL rts_write_context_list(wStream* s, const p_cont_list_t* list) +{ + BYTE x; + + assert(s); + assert(list); + + if (!Stream_EnsureRemainingCapacity(s, 4)) + return FALSE; + Stream_Write_UINT8(s, list->n_context_elem); /* number of items */ + Stream_Write_UINT8(s, 0); /* alignment pad, m.b.z. */ + Stream_Write_UINT16(s, 0); /* alignment pad, m.b.z. */ + + for (x = 0; x < list->n_context_elem; x++) + { + const p_cont_elem_t* element = &list->p_cont_elem[x]; + if (!rts_write_context_elem(s, element)) + return FALSE; + } + return TRUE; +} + +static p_result_t* rts_result_new(size_t count) +{ + return calloc(count, sizeof(p_result_t)); +} + +static void rts_result_free(p_result_t* results) +{ + if (!results) + return; + free(results); +} + +static BOOL rts_read_result(wStream* s, p_result_t* result) +{ + assert(s); + assert(result); + + if (Stream_GetRemainingLength(s) < 2) + return FALSE; + Stream_Read_UINT16(s, result->result); + Stream_Read_UINT16(s, result->reason); + + return rts_read_syntax_id(s, &result->transfer_syntax); +} + +static void rts_free_result(p_result_t* result) +{ + if (!result) + return; +} + +static BOOL rts_read_result_list(wStream* s, p_result_list_t* list) +{ + BYTE x; + + assert(s); + assert(list); + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + Stream_Read_UINT8(s, list->n_results); /* count */ + Stream_Read_UINT8(s, list->reserved); /* alignment pad, m.b.z. */ + Stream_Read_UINT16(s, list->reserved2); /* alignment pad, m.b.z. */ + + if (list->n_results > 0) + { + list->p_results = rts_result_new(list->n_results); + if (!list->p_results) + return FALSE; + + for (x = 0; x < list->n_results; x++) + { + p_result_t* result = &list->p_results[x]; /* size_is(n_results) */ + if (!rts_read_result(s, result)) + return FALSE; + } + } + + return TRUE; +} + +static void rts_free_result_list(p_result_list_t* list) +{ + BYTE x; + + if (!list) + return; + for (x = 0; x < list->n_results; x++) + { + p_result_t* result = &list->p_results[x]; + rts_free_result(result); + } + rts_result_free(list->p_results); +} + +static void rts_free_pdu_alter_context(rpcconn_alter_context_hdr_t* ctx) +{ + if (!ctx) + return; + + rts_free_context_list(&ctx->p_context_elem); + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_alter_context(wStream* s, rpcconn_alter_context_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < + sizeof(rpcconn_alter_context_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + + Stream_Read_UINT16(s, ctx->max_xmit_frag); + Stream_Read_UINT16(s, ctx->max_recv_frag); + Stream_Read_UINT32(s, ctx->assoc_group_id); + + if (!rts_read_context_list(s, &ctx->p_context_elem)) + return FALSE; + + if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header)) + return FALSE; + + return TRUE; +} + +static BOOL rts_read_pdu_alter_context_response(wStream* s, + rpcconn_alter_context_response_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < + sizeof(rpcconn_alter_context_response_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT16(s, ctx->max_xmit_frag); + Stream_Read_UINT16(s, ctx->max_recv_frag); + Stream_Read_UINT32(s, ctx->assoc_group_id); + + if (!rts_read_port_any(s, &ctx->sec_addr)) + return FALSE; + + if (!rts_align_stream(s, 4)) + return FALSE; + + if (!rts_read_result_list(s, &ctx->p_result_list)) + return FALSE; + + if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header)) + return FALSE; + + return TRUE; +} + +static void rts_free_pdu_alter_context_response(rpcconn_alter_context_response_hdr_t* ctx) +{ + if (!ctx) + return; + + rts_free_port_any(&ctx->sec_addr); + rts_free_result_list(&ctx->p_result_list); + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_bind(wStream* s, rpcconn_bind_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < sizeof(rpcconn_bind_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT16(s, ctx->max_xmit_frag); + Stream_Read_UINT16(s, ctx->max_recv_frag); + Stream_Read_UINT32(s, ctx->assoc_group_id); + + if (!rts_read_context_list(s, &ctx->p_context_elem)) + return FALSE; + + if (!rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header)) + return FALSE; + + return TRUE; +} + +static void rts_free_pdu_bind(rpcconn_bind_hdr_t* ctx) +{ + if (!ctx) + return; + rts_free_context_list(&ctx->p_context_elem); + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_bind_ack(wStream* s, rpcconn_bind_ack_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < + sizeof(rpcconn_bind_ack_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT16(s, ctx->max_xmit_frag); + Stream_Read_UINT16(s, ctx->max_recv_frag); + Stream_Read_UINT32(s, ctx->assoc_group_id); + + if (!rts_read_port_any(s, &ctx->sec_addr)) + return FALSE; + + if (!rts_align_stream(s, 4)) + return FALSE; + + if (!rts_read_result_list(s, &ctx->p_result_list)) + return FALSE; + + return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); +} + +static void rts_free_pdu_bind_ack(rpcconn_bind_ack_hdr_t* ctx) +{ + if (!ctx) + return; + rts_free_port_any(&ctx->sec_addr); + rts_free_result_list(&ctx->p_result_list); + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_bind_nak(wStream* s, rpcconn_bind_nak_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < + sizeof(rpcconn_bind_nak_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT16(s, ctx->provider_reject_reason); + return rts_read_supported_versions(s, &ctx->versions); +} + +static void rts_free_pdu_bind_nak(rpcconn_bind_nak_hdr_t* ctx) +{ + if (!ctx) + return; + + rts_free_supported_versions(&ctx->versions); +} + +static BOOL rts_read_pdu_auth3(wStream* s, rpcconn_rpc_auth_3_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < + sizeof(rpcconn_rpc_auth_3_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT16(s, ctx->max_xmit_frag); + Stream_Read_UINT16(s, ctx->max_recv_frag); + + return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); +} + +static void rts_free_pdu_auth3(rpcconn_rpc_auth_3_hdr_t* ctx) +{ + if (!ctx) + return; + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_fault(wStream* s, rpcconn_fault_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < sizeof(rpcconn_fault_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT32(s, ctx->alloc_hint); + Stream_Read_UINT16(s, ctx->p_cont_id); + Stream_Read_UINT8(s, ctx->cancel_count); + Stream_Read_UINT8(s, ctx->reserved); + Stream_Read_UINT32(s, ctx->status); + + return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header); +} + +static void rts_free_pdu_fault(rpcconn_fault_hdr_t* ctx) +{ + if (!ctx) + return; + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_cancel_ack(wStream* s, rpcconn_cancel_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < sizeof(rpcconn_cancel_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); +} + +static void rts_free_pdu_cancel_ack(rpcconn_cancel_hdr_t* ctx) +{ + if (!ctx) + return; + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_orphaned(wStream* s, rpcconn_orphaned_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < + sizeof(rpcconn_orphaned_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + return rts_read_auth_verifier(s, &ctx->auth_verifier, &ctx->header); +} + +static void rts_free_pdu_orphaned(rpcconn_orphaned_hdr_t* ctx) +{ + if (!ctx) + return; + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_request(wStream* s, rpcconn_request_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < sizeof(rpcconn_request_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT32(s, ctx->alloc_hint); + Stream_Read_UINT16(s, ctx->p_cont_id); + Stream_Read_UINT16(s, ctx->opnum); + if (!rts_read_uuid(s, &ctx->object)) + return FALSE; + + return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header); +} + +static void rts_free_pdu_request(rpcconn_request_hdr_t* ctx) +{ + if (!ctx) + return; + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_response(wStream* s, rpcconn_response_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < + sizeof(rpcconn_response_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + Stream_Read_UINT32(s, ctx->alloc_hint); + Stream_Read_UINT16(s, ctx->p_cont_id); + Stream_Read_UINT8(s, ctx->cancel_count); + Stream_Read_UINT8(s, ctx->reserved); + + if (!rts_align_stream(s, 8)) + return FALSE; + + return rts_read_auth_verifier_with_stub(s, &ctx->auth_verifier, &ctx->header); +} + +static void rts_free_pdu_response(rpcconn_response_hdr_t* ctx) +{ + if (!ctx) + return; + free(ctx->stub_data); + rts_free_auth_verifier(&ctx->auth_verifier); +} + +static BOOL rts_read_pdu_rts(wStream* s, rpcconn_rts_hdr_t* ctx) +{ + assert(s); + assert(ctx); + + if (Stream_GetRemainingLength(s) < sizeof(rpcconn_rts_hdr_t) - sizeof(rpcconn_common_hdr_t)) + return FALSE; + + Stream_Read_UINT16(s, ctx->Flags); + Stream_Read_UINT16(s, ctx->NumberOfCommands); + return TRUE; +} + +static void rts_free_pdu_rts(rpcconn_rts_hdr_t* ctx) +{ + WINPR_UNUSED(ctx); +} + +void rts_free_pdu_header(rpcconn_hdr_t* header, BOOL allocated) +{ + if (!header) + return; + + switch (header->common.ptype) + { + case PTYPE_ALTER_CONTEXT: + rts_free_pdu_alter_context(&header->alter_context); + break; + case PTYPE_ALTER_CONTEXT_RESP: + rts_free_pdu_alter_context_response(&header->alter_context_response); + break; + case PTYPE_BIND: + rts_free_pdu_bind(&header->bind); + break; + case PTYPE_BIND_ACK: + rts_free_pdu_bind_ack(&header->bind_ack); + break; + case PTYPE_BIND_NAK: + rts_free_pdu_bind_nak(&header->bind_nak); + break; + case PTYPE_RPC_AUTH_3: + rts_free_pdu_auth3(&header->rpc_auth_3); + break; + case PTYPE_CANCEL_ACK: + rts_free_pdu_cancel_ack(&header->cancel); + break; + case PTYPE_FAULT: + rts_free_pdu_fault(&header->fault); + break; + case PTYPE_ORPHANED: + rts_free_pdu_orphaned(&header->orphaned); + break; + case PTYPE_REQUEST: + rts_free_pdu_request(&header->request); + break; + case PTYPE_RESPONSE: + rts_free_pdu_response(&header->response); + break; + case PTYPE_RTS: + rts_free_pdu_rts(&header->rts); + break; + /* No extra fields */ + case PTYPE_SHUTDOWN: + break; + + /* not handled */ + case PTYPE_PING: + case PTYPE_WORKING: + case PTYPE_NOCALL: + case PTYPE_REJECT: + case PTYPE_ACK: + case PTYPE_CL_CANCEL: + case PTYPE_FACK: + case PTYPE_CO_CANCEL: + default: + break; + } + + if (allocated) + free(header); +} + +BOOL rts_read_pdu_header(wStream* s, rpcconn_hdr_t* header) +{ + BOOL rc = FALSE; + assert(s); + assert(header); + + if (!rts_read_common_pdu_header(s, &header->common)) + return FALSE; + + WLog_DBG(TAG, "Reading PDU type %s", rts_pdu_ptype_to_string(header->common.ptype)); + fflush(stdout); + switch (header->common.ptype) + { + case PTYPE_ALTER_CONTEXT: + rc = rts_read_pdu_alter_context(s, &header->alter_context); + break; + case PTYPE_ALTER_CONTEXT_RESP: + rc = rts_read_pdu_alter_context_response(s, &header->alter_context_response); + break; + case PTYPE_BIND: + rc = rts_read_pdu_bind(s, &header->bind); + break; + case PTYPE_BIND_ACK: + rc = rts_read_pdu_bind_ack(s, &header->bind_ack); + break; + case PTYPE_BIND_NAK: + rc = rts_read_pdu_bind_nak(s, &header->bind_nak); + break; + case PTYPE_RPC_AUTH_3: + rc = rts_read_pdu_auth3(s, &header->rpc_auth_3); + break; + case PTYPE_CANCEL_ACK: + rc = rts_read_pdu_cancel_ack(s, &header->cancel); + break; + case PTYPE_FAULT: + rc = rts_read_pdu_fault(s, &header->fault); + break; + case PTYPE_ORPHANED: + rc = rts_read_pdu_orphaned(s, &header->orphaned); + break; + case PTYPE_REQUEST: + rc = rts_read_pdu_request(s, &header->request); + break; + case PTYPE_RESPONSE: + rc = rts_read_pdu_response(s, &header->response); + break; + case PTYPE_RTS: + rc = rts_read_pdu_rts(s, &header->rts); + break; + case PTYPE_SHUTDOWN: + rc = TRUE; /* No extra fields */ + break; + + /* not handled */ + case PTYPE_PING: + case PTYPE_WORKING: + case PTYPE_NOCALL: + case PTYPE_REJECT: + case PTYPE_ACK: + case PTYPE_CL_CANCEL: + case PTYPE_FACK: + case PTYPE_CO_CANCEL: + default: + break; + } + + return rc; +} + +static BOOL rts_write_pdu_header(wStream* s, const rpcconn_rts_hdr_t* header) +{ + assert(s); + assert(header); + if (!Stream_EnsureRemainingCapacity(s, sizeof(rpcconn_rts_hdr_t))) + return FALSE; - return header; + if (!rts_write_common_pdu_header(s, &header->header)) + return FALSE; + + Stream_Write_UINT16(s, header->Flags); + Stream_Write_UINT16(s, header->NumberOfCommands); + return TRUE; } -static int rts_receive_window_size_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, +static int rts_receive_window_size_command_read(rdpRpc* rpc, wStream* buffer, UINT32* ReceiveWindowSize) { + UINT32 val; + + assert(rpc); + assert(buffer); + + if (Stream_GetRemainingLength(buffer) < 4) + return -1; + Stream_Read_UINT32(buffer, val); if (ReceiveWindowSize) - *ReceiveWindowSize = *((UINT32*)&buffer[0]); /* ReceiveWindowSize (4 bytes) */ + *ReceiveWindowSize = val; /* ReceiveWindowSize (4 bytes) */ return 4; } -static int rts_receive_window_size_command_write(BYTE* buffer, UINT32 ReceiveWindowSize) +static BOOL rts_receive_window_size_command_write(wStream* s, UINT32 ReceiveWindowSize) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_RECEIVE_WINDOW_SIZE; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = ReceiveWindowSize; /* ReceiveWindowSize (4 bytes) */ - } + assert(s); + + if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT32))) + return FALSE; - return 8; + Stream_Write_UINT32(s, RTS_CMD_RECEIVE_WINDOW_SIZE); /* CommandType (4 bytes) */ + Stream_Write_UINT32(s, ReceiveWindowSize); /* ReceiveWindowSize (4 bytes) */ + + return TRUE; } -static int rts_flow_control_ack_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, - UINT32* BytesReceived, UINT32* AvailableWindow, - BYTE* ChannelCookie) +static int rts_flow_control_ack_command_read(rdpRpc* rpc, wStream* buffer, UINT32* BytesReceived, + UINT32* AvailableWindow, BYTE* ChannelCookie) { + UINT32 val; + assert(rpc); + assert(buffer); + /* Ack (24 bytes) */ + if (Stream_GetRemainingLength(buffer) < 24) + return -1; + + Stream_Read_UINT32(buffer, val); if (BytesReceived) - *BytesReceived = *((UINT32*)&buffer[0]); /* BytesReceived (4 bytes) */ + *BytesReceived = val; /* BytesReceived (4 bytes) */ + Stream_Read_UINT32(buffer, val); if (AvailableWindow) - *AvailableWindow = *((UINT32*)&buffer[4]); /* AvailableWindow (4 bytes) */ + *AvailableWindow = val; /* AvailableWindow (4 bytes) */ if (ChannelCookie) - CopyMemory(ChannelCookie, &buffer[8], 16); /* ChannelCookie (16 bytes) */ - + Stream_Read(buffer, ChannelCookie, 16); /* ChannelCookie (16 bytes) */ + else + Stream_Seek(buffer, 16); return 24; } -static int rts_flow_control_ack_command_write(BYTE* buffer, UINT32 BytesReceived, - UINT32 AvailableWindow, BYTE* ChannelCookie) +static BOOL rts_flow_control_ack_command_write(wStream* s, UINT32 BytesReceived, + UINT32 AvailableWindow, BYTE* ChannelCookie) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_FLOW_CONTROL_ACK; /* CommandType (4 bytes) */ - /* Ack (24 bytes) */ - *((UINT32*)&buffer[4]) = BytesReceived; /* BytesReceived (4 bytes) */ - *((UINT32*)&buffer[8]) = AvailableWindow; /* AvailableWindow (4 bytes) */ - CopyMemory(&buffer[12], ChannelCookie, 16); /* ChannelCookie (16 bytes) */ - } + assert(s); - return 28; -} + if (!Stream_EnsureRemainingCapacity(s, 28)) + return FALSE; -static int rts_connection_timeout_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, - UINT32* ConnectionTimeout) -{ - if (ConnectionTimeout) - *ConnectionTimeout = *((UINT32*)&buffer[0]); /* ConnectionTimeout (4 bytes) */ + Stream_Write_UINT32(s, RTS_CMD_FLOW_CONTROL_ACK); /* CommandType (4 bytes) */ + Stream_Write_UINT32(s, BytesReceived); /* BytesReceived (4 bytes) */ + Stream_Write_UINT32(s, AvailableWindow); /* AvailableWindow (4 bytes) */ + Stream_Write(s, ChannelCookie, 16); /* ChannelCookie (16 bytes) */ - return 4; + return TRUE; } -static int rts_connection_timeout_command_write(BYTE* buffer, UINT32 ConnectionTimeout) +static BOOL rts_connection_timeout_command_read(rdpRpc* rpc, wStream* buffer, + UINT32* ConnectionTimeout) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_CONNECTION_TIMEOUT; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = ConnectionTimeout; /* ConnectionTimeout (4 bytes) */ - } + UINT32 val; + assert(rpc); + assert(buffer); - return 8; -} + if (Stream_GetRemainingLength(buffer) < 4) + return FALSE; -static int rts_cookie_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - /* Cookie (16 bytes) */ - return 16; + Stream_Read_UINT32(buffer, val); + if (ConnectionTimeout) + *ConnectionTimeout = val; /* ConnectionTimeout (4 bytes) */ + + return TRUE; } -static int rts_cookie_command_write(BYTE* buffer, BYTE* Cookie) +static BOOL rts_cookie_command_write(wStream* s, const BYTE* Cookie) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_COOKIE; /* CommandType (4 bytes) */ - CopyMemory(&buffer[4], Cookie, 16); /* Cookie (16 bytes) */ - } + assert(s); - return 20; -} + if (!Stream_EnsureRemainingCapacity(s, 20)) + return FALSE; -static int rts_channel_lifetime_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - /* ChannelLifetime (4 bytes) */ - return 4; + Stream_Write_UINT32(s, RTS_CMD_COOKIE); /* CommandType (4 bytes) */ + Stream_Write(s, Cookie, 16); /* Cookie (16 bytes) */ + + return TRUE; } -static int rts_channel_lifetime_command_write(BYTE* buffer, UINT32 ChannelLifetime) +static BOOL rts_channel_lifetime_command_write(wStream* s, UINT32 ChannelLifetime) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_CHANNEL_LIFETIME; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = ChannelLifetime; /* ChannelLifetime (4 bytes) */ - } + assert(s); - return 8; -} + if (!Stream_EnsureRemainingCapacity(s, 8)) + return FALSE; + Stream_Write_UINT32(s, RTS_CMD_CHANNEL_LIFETIME); /* CommandType (4 bytes) */ + Stream_Write_UINT32(s, ChannelLifetime); /* ChannelLifetime (4 bytes) */ -static int rts_client_keepalive_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - /* ClientKeepalive (4 bytes) */ - return 4; + return TRUE; } -static int rts_client_keepalive_command_write(BYTE* buffer, UINT32 ClientKeepalive) +static BOOL rts_client_keepalive_command_write(wStream* s, UINT32 ClientKeepalive) { + assert(s); + + if (!Stream_EnsureRemainingCapacity(s, 8)) + return FALSE; /** * An unsigned integer that specifies the keep-alive interval, in milliseconds, * that this connection is configured to use. This value MUST be 0 or in the inclusive * range of 60,000 through 4,294,967,295. If it is 0, it MUST be interpreted as 300,000. */ - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_CLIENT_KEEPALIVE; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = ClientKeepalive; /* ClientKeepalive (4 bytes) */ - } - return 8; -} + Stream_Write_UINT32(s, RTS_CMD_CLIENT_KEEPALIVE); /* CommandType (4 bytes) */ + Stream_Write_UINT32(s, ClientKeepalive); /* ClientKeepalive (4 bytes) */ -static int rts_version_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - /* Version (4 bytes) */ - return 4; + return TRUE; } -static int rts_version_command_write(BYTE* buffer) +static BOOL rts_version_command_read(rdpRpc* rpc, wStream* buffer) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_VERSION; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = 1; /* Version (4 bytes) */ - } + assert(rpc); + assert(buffer); - return 8; -} + if (!Stream_SafeSeek(buffer, 4)) + return FALSE; -static int rts_empty_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - return 0; + /* Version (4 bytes) */ + return TRUE; } -static int rts_empty_command_write(BYTE* buffer) +static BOOL rts_version_command_write(wStream* buffer) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_EMPTY; /* CommandType (4 bytes) */ - } - - return 4; -} + assert(buffer); -static SSIZE_T rts_padding_command_read(const BYTE* buffer, size_t length) -{ - UINT32 ConformanceCount; - ConformanceCount = *((UINT32*)&buffer[0]); /* ConformanceCount (4 bytes) */ - /* Padding (variable) */ - return ConformanceCount + 4; -} + if (Stream_GetRemainingCapacity(buffer) < 8) + return FALSE; -static int rts_padding_command_write(BYTE* buffer, UINT32 ConformanceCount) -{ - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_PADDING; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = ConformanceCount; /* ConformanceCount (4 bytes) */ - ZeroMemory(&buffer[8], ConformanceCount); /* Padding (variable) */ - } + Stream_Write_UINT32(buffer, RTS_CMD_VERSION); /* CommandType (4 bytes) */ + Stream_Write_UINT32(buffer, 1); /* Version (4 bytes) */ - return 8 + ConformanceCount; + return TRUE; } -static int rts_negative_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) +static BOOL rts_empty_command_write(wStream* s) { - return 0; -} + assert(s); -static int rts_negative_ance_command_write(BYTE* buffer) -{ - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_NEGATIVE_ANCE; /* CommandType (4 bytes) */ - } + if (!Stream_EnsureRemainingCapacity(s, 8)) + return FALSE; - return 4; -} + Stream_Write_UINT32(s, RTS_CMD_EMPTY); /* CommandType (4 bytes) */ -static int rts_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - return 0; + return TRUE; } -static int rts_ance_command_write(BYTE* buffer) +static BOOL rts_padding_command_read(wStream* s, size_t* length) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_ANCE; /* CommandType (4 bytes) */ - } - - return 4; + UINT32 ConformanceCount; + assert(s); + assert(length); + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + Stream_Read_UINT32(s, ConformanceCount); /* ConformanceCount (4 bytes) */ + *length = ConformanceCount + 4; + return TRUE; } -static SSIZE_T rts_client_address_command_read(const BYTE* buffer, size_t length) +static BOOL rts_client_address_command_read(wStream* s, size_t* length) { UINT32 AddressType; - AddressType = *((UINT32*)&buffer[0]); /* AddressType (4 bytes) */ + + assert(s); + assert(length); + + if (Stream_GetRemainingLength(s) < 4) + return FALSE; + Stream_Read_UINT32(s, AddressType); /* AddressType (4 bytes) */ if (AddressType == 0) { /* ClientAddress (4 bytes) */ /* padding (12 bytes) */ - return 4 + 4 + 12; + *length = 4 + 4 + 12; } else { /* ClientAddress (16 bytes) */ /* padding (12 bytes) */ - return 4 + 16 + 12; + *length = 4 + 16 + 12; } + return TRUE; } -static int rts_client_address_command_write(BYTE* buffer, UINT32 AddressType, BYTE* ClientAddress) +static BOOL rts_association_group_id_command_write(wStream* s, const BYTE* AssociationGroupId) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_CLIENT_ADDRESS; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = AddressType; /* AddressType (4 bytes) */ - } + assert(s); - if (AddressType == 0) - { - if (buffer) - { - CopyMemory(&buffer[8], ClientAddress, 4); /* ClientAddress (4 bytes) */ - ZeroMemory(&buffer[12], 12); /* padding (12 bytes) */ - } - - return 24; - } - else - { - if (buffer) - { - CopyMemory(&buffer[8], ClientAddress, 16); /* ClientAddress (16 bytes) */ - ZeroMemory(&buffer[24], 12); /* padding (12 bytes) */ - } + if (!Stream_EnsureRemainingCapacity(s, 20)) + return FALSE; - return 36; - } -} + Stream_Write_UINT32(s, RTS_CMD_ASSOCIATION_GROUP_ID); /* CommandType (4 bytes) */ + Stream_Write(s, AssociationGroupId, 16); /* AssociationGroupId (16 bytes) */ -static int rts_association_group_id_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - /* AssociationGroupId (16 bytes) */ - return 16; + return TRUE; } -static int rts_association_group_id_command_write(BYTE* buffer, BYTE* AssociationGroupId) +static int rts_destination_command_read(rdpRpc* rpc, wStream* buffer, UINT32* Destination) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_ASSOCIATION_GROUP_ID; /* CommandType (4 bytes) */ - CopyMemory(&buffer[4], AssociationGroupId, 16); /* AssociationGroupId (16 bytes) */ - } - - return 20; -} + UINT32 val; + assert(rpc); + assert(buffer); -static int rts_destination_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, - UINT32* Destination) -{ + if (Stream_GetRemainingLength(buffer) < 4) + return -1; + Stream_Read_UINT32(buffer, val); if (Destination) - *Destination = *((UINT32*)&buffer[0]); /* Destination (4 bytes) */ + *Destination = val; /* Destination (4 bytes) */ return 4; } -static int rts_destination_command_write(BYTE* buffer, UINT32 Destination) +static BOOL rts_destination_command_write(wStream* s, UINT32 Destination) { - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_DESTINATION; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = Destination; /* Destination (4 bytes) */ - } - - return 8; -} + assert(s); -static int rts_ping_traffic_sent_notify_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length) -{ - /* PingTrafficSent (4 bytes) */ - return 4; -} + if (!Stream_EnsureRemainingCapacity(s, 8)) + return FALSE; -static int rts_ping_traffic_sent_notify_command_write(BYTE* buffer, UINT32 PingTrafficSent) -{ - if (buffer) - { - *((UINT32*)&buffer[0]) = RTS_CMD_PING_TRAFFIC_SENT_NOTIFY; /* CommandType (4 bytes) */ - *((UINT32*)&buffer[4]) = PingTrafficSent; /* PingTrafficSent (4 bytes) */ - } + Stream_Write_UINT32(s, RTS_CMD_DESTINATION); /* CommandType (4 bytes) */ + Stream_Write_UINT32(s, Destination); /* Destination (4 bytes) */ - return 8; + return TRUE; } void rts_generate_cookie(BYTE* cookie) { + assert(cookie); winpr_RAND(cookie, 16); } +static BOOL rts_send_buffer(RpcChannel* channel, wStream* s, size_t frag_length) +{ + BOOL status = FALSE; + SSIZE_T rc; + + assert(channel); + assert(s); + + Stream_SealLength(s); + if (Stream_Length(s) < sizeof(rpcconn_common_hdr_t)) + goto fail; + if (Stream_Length(s) != frag_length) + goto fail; + + rc = rpc_channel_write(channel, Stream_Buffer(s), Stream_Length(s)); + if (rc < 0) + goto fail; + if ((size_t)rc != Stream_Length(s)) + goto fail; + status = TRUE; +fail: + return status; +} + /* CONN/A Sequence */ -int rts_send_CONN_A1_pdu(rdpRpc* rpc) +BOOL rts_send_CONN_A1_pdu(rdpRpc* rpc) { - int status; - BYTE* buffer; + BOOL status = FALSE; + wStream* buffer; rpcconn_rts_hdr_t header = rts_pdu_header_init(); UINT32 ReceiveWindowSize; BYTE* OUTChannelCookie; BYTE* VirtualConnectionCookie; - RpcVirtualConnection* connection = rpc->VirtualConnection; - RpcOutChannel* outChannel = connection->DefaultOutChannel; + RpcVirtualConnection* connection; + RpcOutChannel* outChannel; + + assert(rpc); + + connection = rpc->VirtualConnection; + assert(connection); + + outChannel = connection->DefaultOutChannel; + assert(outChannel); header.header.frag_length = 76; header.Flags = RTS_FLAG_NONE; header.NumberOfCommands = 4; + WLog_DBG(TAG, "Sending CONN/A1 RTS PDU"); VirtualConnectionCookie = (BYTE*)&(connection->Cookie); OUTChannelCookie = (BYTE*)&(outChannel->common.Cookie); ReceiveWindowSize = outChannel->ReceiveWindow; - buffer = (BYTE*)malloc(header.header.frag_length); + + buffer = Stream_New(NULL, header.header.frag_length); if (!buffer) return -1; - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - rts_version_command_write(&buffer[20]); /* Version (8 bytes) */ - rts_cookie_command_write(&buffer[28], - VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ - rts_cookie_command_write(&buffer[48], OUTChannelCookie); /* OUTChannelCookie (20 bytes) */ - rts_receive_window_size_command_write(&buffer[68], - ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ - status = rpc_channel_write(&outChannel->common, buffer, header.header.frag_length); - free(buffer); - return (status > 0) ? 1 : -1; + if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ + goto fail; + status = rts_version_command_write(buffer); /* Version (8 bytes) */ + if (!status) + goto fail; + status = rts_cookie_command_write( + buffer, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ + if (!status) + goto fail; + status = rts_cookie_command_write(buffer, OUTChannelCookie); /* OUTChannelCookie (20 bytes) */ + if (!status) + goto fail; + status = rts_receive_window_size_command_write( + buffer, ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ + if (!status) + goto fail; + status = rts_send_buffer(&outChannel->common, buffer, header.header.frag_length); +fail: + Stream_Free(buffer, TRUE); + return status; } -int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +BOOL rts_recv_CONN_A3_pdu(rdpRpc* rpc, wStream* buffer) { + BOOL rc; UINT32 ConnectionTimeout; - rts_connection_timeout_command_read(rpc, &buffer[24], length - 24, &ConnectionTimeout); + + if (!Stream_SafeSeek(buffer, 24)) + return FALSE; + + rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout); + if (!rc) + return rc; + WLog_DBG(TAG, "Receiving CONN/A3 RTS PDU: ConnectionTimeout: %" PRIu32 "", ConnectionTimeout); + + assert(rpc); + assert(rpc->VirtualConnection); + assert(rpc->VirtualConnection->DefaultInChannel); + rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout; - return 1; + return TRUE; } /* CONN/B Sequence */ -int rts_send_CONN_B1_pdu(rdpRpc* rpc) +BOOL rts_send_CONN_B1_pdu(rdpRpc* rpc) { - int status; - BYTE* buffer; - UINT32 length; + BOOL status = FALSE; + wStream* buffer; rpcconn_rts_hdr_t header = rts_pdu_header_init(); BYTE* INChannelCookie; BYTE* AssociationGroupId; BYTE* VirtualConnectionCookie; - RpcVirtualConnection* connection = rpc->VirtualConnection; - RpcInChannel* inChannel = connection->DefaultInChannel; + RpcVirtualConnection* connection; + RpcInChannel* inChannel; + + assert(rpc); + + connection = rpc->VirtualConnection; + assert(connection); + + inChannel = connection->DefaultInChannel; + assert(inChannel); header.header.frag_length = 104; header.Flags = RTS_FLAG_NONE; header.NumberOfCommands = 6; + WLog_DBG(TAG, "Sending CONN/B1 RTS PDU"); + VirtualConnectionCookie = (BYTE*)&(connection->Cookie); INChannelCookie = (BYTE*)&(inChannel->common.Cookie); AssociationGroupId = (BYTE*)&(connection->AssociationGroupId); - buffer = (BYTE*)malloc(header.header.frag_length); + buffer = Stream_New(NULL, header.header.frag_length); if (!buffer) - return -1; - - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - rts_version_command_write(&buffer[20]); /* Version (8 bytes) */ - rts_cookie_command_write(&buffer[28], - VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ - rts_cookie_command_write(&buffer[48], INChannelCookie); /* INChannelCookie (20 bytes) */ - rts_channel_lifetime_command_write(&buffer[68], - rpc->ChannelLifetime); /* ChannelLifetime (8 bytes) */ - rts_client_keepalive_command_write(&buffer[76], - rpc->KeepAliveInterval); /* ClientKeepalive (8 bytes) */ - rts_association_group_id_command_write(&buffer[84], - AssociationGroupId); /* AssociationGroupId (20 bytes) */ - length = header.header.frag_length; - status = rpc_channel_write(&inChannel->common, buffer, length); - free(buffer); - return (status > 0) ? 1 : -1; + goto fail; + if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ + goto fail; + if (!rts_version_command_write(buffer)) /* Version (8 bytes) */ + goto fail; + if (!rts_cookie_command_write(buffer, + VirtualConnectionCookie)) /* VirtualConnectionCookie (20 bytes) */ + goto fail; + if (!rts_cookie_command_write(buffer, INChannelCookie)) /* INChannelCookie (20 bytes) */ + goto fail; + if (!rts_channel_lifetime_command_write(buffer, + rpc->ChannelLifetime)) /* ChannelLifetime (8 bytes) */ + goto fail; + if (!rts_client_keepalive_command_write(buffer, + rpc->KeepAliveInterval)) /* ClientKeepalive (8 bytes) */ + goto fail; + if (!rts_association_group_id_command_write( + buffer, AssociationGroupId)) /* AssociationGroupId (20 bytes) */ + goto fail; + status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); +fail: + Stream_Free(buffer, TRUE); + return status; } /* CONN/C Sequence */ -int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer) { - UINT32 offset; + BOOL rc; UINT32 ReceiveWindowSize; UINT32 ConnectionTimeout; - offset = 24; - offset += rts_version_command_read(rpc, &buffer[offset], length - offset) + 4; - offset += rts_receive_window_size_command_read(rpc, &buffer[offset], length - offset, - &ReceiveWindowSize) + - 4; - offset += rts_connection_timeout_command_read(rpc, &buffer[offset], length - offset, - &ConnectionTimeout) + - 4; + + assert(rpc); + assert(buffer); + + if (!Stream_SafeSeek(buffer, 24)) + return FALSE; + + rc = rts_version_command_read(rpc, buffer); + if (rc < 0) + return rc; + rc = rts_receive_window_size_command_read(rpc, buffer, &ReceiveWindowSize); + if (rc < 0) + return rc; + rc = rts_connection_timeout_command_read(rpc, buffer, &ConnectionTimeout); + if (rc < 0) + return rc; WLog_DBG(TAG, "Receiving CONN/C2 RTS PDU: ConnectionTimeout: %" PRIu32 " ReceiveWindowSize: %" PRIu32 "", ConnectionTimeout, ReceiveWindowSize); + + assert(rpc); + assert(rpc->VirtualConnection); + assert(rpc->VirtualConnection->DefaultInChannel); + rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout; rpc->VirtualConnection->DefaultInChannel->PeerReceiveWindow = ReceiveWindowSize; - return 1; + return TRUE; } /* Out-of-Sequence PDUs */ -static int rts_send_keep_alive_pdu(rdpRpc* rpc) +BOOL rts_send_flow_control_ack_pdu(rdpRpc* rpc) { - int status; - BYTE* buffer; - UINT32 length; + BOOL status = FALSE; + wStream* buffer; rpcconn_rts_hdr_t header = rts_pdu_header_init(); - RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel; + UINT32 BytesReceived; + UINT32 AvailableWindow; + BYTE* ChannelCookie; + RpcVirtualConnection* connection; + RpcInChannel* inChannel; + RpcOutChannel* outChannel; - header.header.frag_length = 28; - header.Flags = RTS_FLAG_OTHER_CMD; - header.NumberOfCommands = 1; - WLog_DBG(TAG, "Sending Keep-Alive RTS PDU"); - buffer = (BYTE*)malloc(header.header.frag_length); + assert(rpc); - if (!buffer) - return -1; + connection = rpc->VirtualConnection; + assert(connection); - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - rts_client_keepalive_command_write( - &buffer[20], rpc->CurrentKeepAliveInterval); /* ClientKeepAlive (8 bytes) */ - length = header.header.frag_length; - status = rpc_channel_write(&inChannel->common, buffer, length); - free(buffer); - return (status > 0) ? 1 : -1; -} + inChannel = connection->DefaultInChannel; + assert(inChannel); -int rts_send_flow_control_ack_pdu(rdpRpc* rpc) -{ - int status; - BYTE* buffer; - UINT32 length; - rpcconn_rts_hdr_t header = rts_pdu_header_init(); - UINT32 BytesReceived; - UINT32 AvailableWindow; - BYTE* ChannelCookie; - RpcVirtualConnection* connection = rpc->VirtualConnection; - RpcInChannel* inChannel = connection->DefaultInChannel; - RpcOutChannel* outChannel = connection->DefaultOutChannel; + outChannel = connection->DefaultOutChannel; + assert(outChannel); header.header.frag_length = 56; header.Flags = RTS_FLAG_OTHER_CMD; header.NumberOfCommands = 2; + WLog_DBG(TAG, "Sending FlowControlAck RTS PDU"); + BytesReceived = outChannel->BytesReceived; AvailableWindow = outChannel->AvailableWindowAdvertised; ChannelCookie = (BYTE*)&(outChannel->common.Cookie); outChannel->ReceiverAvailableWindow = outChannel->AvailableWindowAdvertised; - buffer = (BYTE*)malloc(header.header.frag_length); + buffer = Stream_New(NULL, header.header.frag_length); if (!buffer) - return -1; + goto fail; + + if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ + goto fail; + if (!rts_destination_command_write(buffer, FDOutProxy)) /* Destination Command (8 bytes) */ + goto fail; - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - rts_destination_command_write(&buffer[20], FDOutProxy); /* Destination Command (8 bytes) */ /* FlowControlAck Command (28 bytes) */ - rts_flow_control_ack_command_write(&buffer[28], BytesReceived, AvailableWindow, ChannelCookie); - length = header.header.frag_length; - status = rpc_channel_write(&inChannel->common, buffer, length); - free(buffer); - return (status > 0) ? 1 : -1; + if (!rts_flow_control_ack_command_write(buffer, BytesReceived, AvailableWindow, ChannelCookie)) + goto fail; + + status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); +fail: + Stream_Free(buffer, TRUE); + return status; } -static int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +static int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, wStream* buffer) { - UINT32 offset; + int rc; UINT32 BytesReceived; UINT32 AvailableWindow; - BYTE ChannelCookie[16]; - offset = 24; - offset += - rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset, &BytesReceived, - &AvailableWindow, (BYTE*)&ChannelCookie) + - 4; + BYTE ChannelCookie[16] = { 0 }; + + rc = rts_flow_control_ack_command_read(rpc, buffer, &BytesReceived, &AvailableWindow, + (BYTE*)&ChannelCookie); + if (rc < 0) + return rc; WLog_ERR(TAG, "Receiving FlowControlAck RTS PDU: BytesReceived: %" PRIu32 " AvailableWindow: %" PRIu32 "", BytesReceived, AvailableWindow); + + assert(rpc->VirtualConnection); + assert(rpc->VirtualConnection->DefaultInChannel); + rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow = AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived); return 1; } -static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, wStream* buffer) { - UINT32 offset; + int rc; UINT32 Destination; UINT32 BytesReceived; UINT32 AvailableWindow; - BYTE ChannelCookie[16]; + BYTE ChannelCookie[16] = { 0 }; /** * When the sender receives a FlowControlAck RTS PDU, it MUST use the following formula to * recalculate its Sender AvailableWindow variable: @@ -622,16 +1724,23 @@ static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buf * structure in the PDU received. * */ - offset = 24; - offset += rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination) + 4; - offset += - rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset, &BytesReceived, - &AvailableWindow, (BYTE*)&ChannelCookie) + - 4; + + rc = rts_destination_command_read(rpc, buffer, &Destination); + if (rc < 0) + return rc; + + rc = rts_flow_control_ack_command_read(rpc, buffer, &BytesReceived, &AvailableWindow, + ChannelCookie); + if (rc < 0) + return rc; + WLog_DBG(TAG, "Receiving FlowControlAckWithDestination RTS PDU: BytesReceived: %" PRIu32 " AvailableWindow: %" PRIu32 "", BytesReceived, AvailableWindow); + + assert(rpc->VirtualConnection); + assert(rpc->VirtualConnection->DefaultInChannel); rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow = AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived); return 1; @@ -639,31 +1748,41 @@ static int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buf static int rts_send_ping_pdu(rdpRpc* rpc) { - int status; - BYTE* buffer; - UINT32 length; + BOOL status = FALSE; + wStream* buffer; rpcconn_rts_hdr_t header = rts_pdu_header_init(); - RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel; + RpcInChannel* inChannel; + + assert(rpc); + assert(rpc->VirtualConnection); + + inChannel = rpc->VirtualConnection->DefaultInChannel; + assert(inChannel); header.header.frag_length = 20; header.Flags = RTS_FLAG_PING; header.NumberOfCommands = 0; + WLog_DBG(TAG, "Sending Ping RTS PDU"); - buffer = (BYTE*)malloc(header.header.frag_length); + buffer = Stream_New(NULL, header.header.frag_length); if (!buffer) - return -1; - - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - length = header.header.frag_length; - status = rpc_channel_write(&inChannel->common, buffer, length); - free(buffer); - return (status > 0) ? 1 : -1; + goto fail; + + if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ + goto fail; + status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); +fail: + Stream_Free(buffer, TRUE); + return (status) ? 1 : -1; } -SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length) +BOOL rts_command_length(UINT32 CommandType, wStream* s, size_t* length) { - int CommandLength = 0; + size_t padding = 0; + size_t CommandLength = 0; + + assert(s); switch (CommandType) { @@ -700,7 +1819,8 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length break; case RTS_CMD_PADDING: /* variable-size */ - CommandLength = rts_padding_command_read(buffer, length); + if (!rts_padding_command_read(s, &padding)) + return FALSE; break; case RTS_CMD_NEGATIVE_ANCE: @@ -712,7 +1832,8 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length break; case RTS_CMD_CLIENT_ADDRESS: /* variable-size */ - CommandLength = rts_client_address_command_read(buffer, length); + if (!rts_client_address_command_read(s, &CommandLength)) + return FALSE; break; case RTS_CMD_ASSOCIATION_GROUP_ID: @@ -729,118 +1850,176 @@ SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length default: WLog_ERR(TAG, "Error: Unknown RTS Command Type: 0x%" PRIx32 "", CommandType); - return -1; + return FALSE; } - return CommandLength; + CommandLength += padding; + if (Stream_GetRemainingLength(s) < CommandLength) + return FALSE; + + if (length) + *length = CommandLength; + return TRUE; } static int rts_send_OUT_R2_A7_pdu(rdpRpc* rpc) { - int status; - BYTE* buffer; + BOOL status = FALSE; + wStream* buffer; rpcconn_rts_hdr_t header = rts_pdu_header_init(); BYTE* SuccessorChannelCookie; - RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel; - RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; + RpcInChannel* inChannel; + RpcOutChannel* nextOutChannel; + + assert(rpc); + assert(rpc->VirtualConnection); + + inChannel = rpc->VirtualConnection->DefaultInChannel; + assert(inChannel); + + nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; + assert(nextOutChannel); header.header.frag_length = 56; header.Flags = RTS_FLAG_OUT_CHANNEL; header.NumberOfCommands = 3; + WLog_DBG(TAG, "Sending OUT_R2/A7 RTS PDU"); + SuccessorChannelCookie = (BYTE*)&(nextOutChannel->common.Cookie); - buffer = (BYTE*)malloc(header.header.frag_length); + buffer = Stream_New(NULL, header.header.frag_length); if (!buffer) return -1; - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - rts_destination_command_write(&buffer[20], FDServer); /* Destination (8 bytes)*/ - rts_cookie_command_write(&buffer[28], - SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */ - rts_version_command_write(&buffer[48]); /* Version (8 bytes) */ - status = rpc_channel_write(&inChannel->common, buffer, header.header.frag_length); - free(buffer); - return (status > 0) ? 1 : -1; + if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ + goto fail; + if (!rts_destination_command_write(buffer, FDServer)) /* Destination (8 bytes)*/ + goto fail; + if (!rts_cookie_command_write(buffer, + SuccessorChannelCookie)) /* SuccessorChannelCookie (20 bytes) */ + goto fail; + if (!rts_version_command_write(buffer)) /* Version (8 bytes) */ + goto fail; + status = rts_send_buffer(&inChannel->common, buffer, header.header.frag_length); +fail: + Stream_Free(buffer, TRUE); + return (status) ? 1 : -1; } static int rts_send_OUT_R2_C1_pdu(rdpRpc* rpc) { - int status; - BYTE* buffer; + BOOL status = FALSE; + wStream* buffer; rpcconn_rts_hdr_t header = rts_pdu_header_init(); - RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; + RpcOutChannel* nextOutChannel; + + assert(rpc); + assert(rpc->VirtualConnection); + + nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel; + assert(nextOutChannel); header.header.frag_length = 24; header.Flags = RTS_FLAG_PING; header.NumberOfCommands = 1; + WLog_DBG(TAG, "Sending OUT_R2/C1 RTS PDU"); - buffer = (BYTE*)malloc(header.header.frag_length); + buffer = Stream_New(NULL, header.header.frag_length); if (!buffer) return -1; - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - rts_empty_command_write(&buffer[20]); /* Empty command (4 bytes) */ - status = rpc_channel_write(&nextOutChannel->common, buffer, header.header.frag_length); - free(buffer); - return (status > 0) ? 1 : -1; + if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ + goto fail; + + if (!rts_empty_command_write(buffer)) /* Empty command (4 bytes) */ + goto fail; + status = rts_send_buffer(&nextOutChannel->common, buffer, header.header.frag_length); +fail: + Stream_Free(buffer, TRUE); + return (status) ? 1 : -1; } -int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc) +BOOL rts_send_OUT_R1_A3_pdu(rdpRpc* rpc) { - int status; - BYTE* buffer; + BOOL status = FALSE; + wStream* buffer; rpcconn_rts_hdr_t header = rts_pdu_header_init(); UINT32 ReceiveWindowSize; BYTE* VirtualConnectionCookie; BYTE* PredecessorChannelCookie; BYTE* SuccessorChannelCookie; - RpcVirtualConnection* connection = rpc->VirtualConnection; - RpcOutChannel* outChannel = connection->DefaultOutChannel; - RpcOutChannel* nextOutChannel = connection->NonDefaultOutChannel; + RpcVirtualConnection* connection; + RpcOutChannel* outChannel; + RpcOutChannel* nextOutChannel; + + assert(rpc); + + connection = rpc->VirtualConnection; + assert(connection); + + outChannel = connection->DefaultOutChannel; + assert(outChannel); + + nextOutChannel = connection->NonDefaultOutChannel; + assert(nextOutChannel); header.header.frag_length = 96; header.Flags = RTS_FLAG_RECYCLE_CHANNEL; header.NumberOfCommands = 5; + WLog_DBG(TAG, "Sending OUT_R1/A3 RTS PDU"); + VirtualConnectionCookie = (BYTE*)&(connection->Cookie); PredecessorChannelCookie = (BYTE*)&(outChannel->common.Cookie); SuccessorChannelCookie = (BYTE*)&(nextOutChannel->common.Cookie); ReceiveWindowSize = outChannel->ReceiveWindow; - buffer = (BYTE*)malloc(header.header.frag_length); + buffer = Stream_New(NULL, header.header.frag_length); if (!buffer) return -1; - CopyMemory(buffer, ((BYTE*)&header), 20); /* RTS Header (20 bytes) */ - rts_version_command_write(&buffer[20]); /* Version (8 bytes) */ - rts_cookie_command_write(&buffer[28], - VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */ - rts_cookie_command_write(&buffer[48], - PredecessorChannelCookie); /* PredecessorChannelCookie (20 bytes) */ - rts_cookie_command_write(&buffer[68], - SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */ - rts_receive_window_size_command_write(&buffer[88], - ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */ - status = rpc_channel_write(&nextOutChannel->common, buffer, header.header.frag_length); - free(buffer); - return (status > 0) ? 1 : -1; + if (!rts_write_pdu_header(buffer, &header)) /* RTS Header (20 bytes) */ + goto fail; + if (!rts_version_command_write(buffer)) /* Version (8 bytes) */ + goto fail; + if (!rts_cookie_command_write(buffer, + VirtualConnectionCookie)) /* VirtualConnectionCookie (20 bytes) */ + goto fail; + if (!rts_cookie_command_write( + buffer, PredecessorChannelCookie)) /* PredecessorChannelCookie (20 bytes) */ + goto fail; + if (!rts_cookie_command_write(buffer, + SuccessorChannelCookie)) /* SuccessorChannelCookie (20 bytes) */ + goto fail; + if (!rts_receive_window_size_command_write(buffer, + ReceiveWindowSize)) /* ReceiveWindowSize (8 bytes) */ + goto fail; + + status = rts_send_buffer(&nextOutChannel->common, buffer, header.header.frag_length); +fail: + Stream_Free(buffer, TRUE); + return status; } -static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, wStream* buffer) { int status; - UINT32 offset; UINT32 Destination = 0; - RpcVirtualConnection* connection = rpc->VirtualConnection; + RpcVirtualConnection* connection; + assert(rpc); + assert(buffer); + + connection = rpc->VirtualConnection; + assert(connection); + WLog_DBG(TAG, "Receiving OUT R1/A2 RTS PDU"); - offset = 24; - if (length < offset) - return -1; + status = rts_destination_command_read(rpc, buffer, &Destination); + if (status < 0) + return status; - rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination); connection->NonDefaultOutChannel = rpc_out_channel_new(rpc); if (!connection->NonDefaultOutChannel) @@ -859,10 +2038,17 @@ static int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) return 1; } -static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, wStream* buffer) { int status; - RpcVirtualConnection* connection = rpc->VirtualConnection; + RpcVirtualConnection* connection; + + assert(rpc); + assert(buffer); + + connection = rpc->VirtualConnection; + assert(connection); + WLog_DBG(TAG, "Receiving OUT R2/A6 RTS PDU"); status = rts_send_OUT_R2_C1_pdu(rpc); @@ -887,47 +2073,59 @@ static int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) return 1; } -static int rts_recv_OUT_R2_B3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +static int rts_recv_OUT_R2_B3_pdu(rdpRpc* rpc, wStream* buffer) { - RpcVirtualConnection* connection = rpc->VirtualConnection; + RpcVirtualConnection* connection; + + assert(rpc); + assert(buffer); + + connection = rpc->VirtualConnection; + assert(connection); + WLog_DBG(TAG, "Receiving OUT R2/B3 RTS PDU"); rpc_out_channel_transition_to_state(connection->DefaultOutChannel, CLIENT_OUT_CHANNEL_STATE_RECYCLED); return 1; } -int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) +BOOL rts_recv_out_of_sequence_pdu(rdpRpc* rpc, wStream* buffer, const rpcconn_hdr_t* header) { - int status = -1; + BOOL status = FALSE; UINT32 SignatureId; - rpcconn_rts_hdr_t* rts; - RtsPduSignature signature; + size_t length, total; + RtsPduSignature signature = { 0 }; RpcVirtualConnection* connection; - if (!rpc || !buffer) - return -1; + assert(rpc); + assert(buffer); + assert(header); + + total = Stream_Length(buffer); + length = header->common.frag_length; + if (total < length) + return FALSE; connection = rpc->VirtualConnection; if (!connection) - return -1; - - rts = (rpcconn_rts_hdr_t*)buffer; + return FALSE; - if (!rts_extract_pdu_signature(&signature, rts)) - return -1; + if (!rts_extract_pdu_signature(&signature, buffer, header)) + return FALSE; SignatureId = rts_identify_pdu_signature(&signature, NULL); - if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, rts)) + if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, buffer, header)) { - status = rts_recv_flow_control_ack_pdu(rpc, buffer, length); + status = rts_recv_flow_control_ack_pdu(rpc, buffer); } - else if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, rts)) + else if (rts_match_pdu_signature(&RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, buffer, + header)) { - status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer, length); + status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer); } - else if (rts_match_pdu_signature(&RTS_PDU_PING_SIGNATURE, rts)) + else if (rts_match_pdu_signature(&RTS_PDU_PING_SIGNATURE, buffer, header)) { status = rts_send_ping_pdu(rpc); } @@ -935,28 +2133,28 @@ int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) { if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED) { - if (rts_match_pdu_signature(&RTS_PDU_OUT_R1_A2_SIGNATURE, rts)) + if (rts_match_pdu_signature(&RTS_PDU_OUT_R1_A2_SIGNATURE, buffer, header)) { - status = rts_recv_OUT_R1_A2_pdu(rpc, buffer, length); + status = rts_recv_OUT_R1_A2_pdu(rpc, buffer); } } else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_A6W) { - if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_A6_SIGNATURE, rts)) + if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_A6_SIGNATURE, buffer, header)) { - status = rts_recv_OUT_R2_A6_pdu(rpc, buffer, length); + status = rts_recv_OUT_R2_A6_pdu(rpc, buffer); } } else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_B3W) { - if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_B3_SIGNATURE, rts)) + if (rts_match_pdu_signature(&RTS_PDU_OUT_R2_B3_SIGNATURE, buffer, header)) { - status = rts_recv_OUT_R2_B3_pdu(rpc, buffer, length); + status = rts_recv_OUT_R2_B3_pdu(rpc, buffer); } } } - if (status < 0) + if (!status) { WLog_ERR(TAG, "error parsing RTS PDU with signature id: 0x%08" PRIX32 "", SignatureId); rts_print_pdu_signature(&signature); @@ -964,3 +2162,42 @@ int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length) return status; } + +BOOL rts_write_pdu_auth3(wStream* s, const rpcconn_rpc_auth_3_hdr_t* auth) +{ + assert(s); + assert(auth); + + if (!rts_write_common_pdu_header(s, &auth->header)) + return FALSE; + + if (!Stream_EnsureRemainingCapacity(s, 2 * sizeof(UINT16))) + return FALSE; + + Stream_Write_UINT16(s, auth->max_xmit_frag); + Stream_Write_UINT16(s, auth->max_recv_frag); + + return rts_write_auth_verifier(s, &auth->auth_verifier, &auth->header); +} + +BOOL rts_write_pdu_bind(wStream* s, const rpcconn_bind_hdr_t* bind) +{ + + assert(s); + assert(bind); + + if (!rts_write_common_pdu_header(s, &bind->header)) + return FALSE; + + if (!Stream_EnsureRemainingCapacity(s, 8)) + return FALSE; + + Stream_Write_UINT16(s, bind->max_xmit_frag); + Stream_Write_UINT16(s, bind->max_recv_frag); + Stream_Write_UINT32(s, bind->assoc_group_id); + + if (!rts_write_context_list(s, &bind->p_context_elem)) + return FALSE; + + return rts_write_auth_verifier(s, &bind->auth_verifier, &bind->header); +} diff --git a/libfreerdp/core/gateway/rts.h b/libfreerdp/core/gateway/rts.h index ccc4cfd18bca..01a66a781013 100644 --- a/libfreerdp/core/gateway/rts.h +++ b/libfreerdp/core/gateway/rts.h @@ -24,12 +24,14 @@ #include "config.h" #endif -#include "rpc.h" +#include #include #include #include +#include "rpc.h" + #define RTS_FLAG_NONE 0x0000 #define RTS_FLAG_PING 0x0001 #define RTS_FLAG_OTHER_CMD 0x0002 @@ -79,21 +81,28 @@ FREERDP_LOCAL void rts_generate_cookie(BYTE* cookie); -FREERDP_LOCAL SSIZE_T rts_command_length(UINT32 CommandType, const BYTE* buffer, size_t length); +FREERDP_LOCAL BOOL rts_write_pdu_auth3(wStream* s, const rpcconn_rpc_auth_3_hdr_t* auth); +FREERDP_LOCAL BOOL rts_write_pdu_bind(wStream* s, const rpcconn_bind_hdr_t* bind); + +FREERDP_LOCAL BOOL rts_read_pdu_header(wStream* s, rpcconn_hdr_t* header); +FREERDP_LOCAL void rts_free_pdu_header(rpcconn_hdr_t* header, BOOL allocated); + +FREERDP_LOCAL BOOL rts_read_common_pdu_header(wStream* s, rpcconn_common_hdr_t* header); -FREERDP_LOCAL int rts_send_CONN_A1_pdu(rdpRpc* rpc); -FREERDP_LOCAL int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); +FREERDP_LOCAL BOOL rts_command_length(UINT32 CommandType, wStream* s, size_t* length); -FREERDP_LOCAL int rts_send_CONN_B1_pdu(rdpRpc* rpc); +FREERDP_LOCAL BOOL rts_send_CONN_A1_pdu(rdpRpc* rpc); +FREERDP_LOCAL BOOL rts_recv_CONN_A3_pdu(rdpRpc* rpc, wStream* buffer); -FREERDP_LOCAL int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); +FREERDP_LOCAL BOOL rts_send_CONN_B1_pdu(rdpRpc* rpc); -FREERDP_LOCAL int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc); +FREERDP_LOCAL BOOL rts_recv_CONN_C2_pdu(rdpRpc* rpc, wStream* buffer); -FREERDP_LOCAL int rts_send_flow_control_ack_pdu(rdpRpc* rpc); +FREERDP_LOCAL BOOL rts_send_OUT_R1_A3_pdu(rdpRpc* rpc); -FREERDP_LOCAL int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length); +FREERDP_LOCAL BOOL rts_send_flow_control_ack_pdu(rdpRpc* rpc); -#include "rts_signature.h" +FREERDP_LOCAL BOOL rts_recv_out_of_sequence_pdu(rdpRpc* rpc, wStream* buffer, + const rpcconn_hdr_t* header); #endif /* FREERDP_LIB_CORE_GATEWAY_RTS_H */ diff --git a/libfreerdp/core/gateway/rts_signature.c b/libfreerdp/core/gateway/rts_signature.c index 4d605f041689..b9319d185ce9 100644 --- a/libfreerdp/core/gateway/rts_signature.c +++ b/libfreerdp/core/gateway/rts_signature.c @@ -17,6 +17,9 @@ * limitations under the License. */ +#include +#include + #include #include "rts_signature.h" @@ -276,90 +279,74 @@ static const RTS_PDU_SIGNATURE_ENTRY RTS_PDU_SIGNATURE_TABLE[] = { { RTS_PDU_PING, TRUE, &RTS_PDU_PING_SIGNATURE, "Ping" }, { RTS_PDU_FLOW_CONTROL_ACK, TRUE, &RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, "FlowControlAck" }, { RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION, TRUE, - &RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, "FlowControlAckWithDestination" }, - - { 0, FALSE, NULL, NULL } + &RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, "FlowControlAckWithDestination" } }; -BOOL rts_match_pdu_signature(const RtsPduSignature* signature, const rpcconn_rts_hdr_t* rts) +BOOL rts_match_pdu_signature(const RtsPduSignature* signature, wStream* src, + const rpcconn_hdr_t* header) { - UINT16 i; - int status; - const BYTE* buffer; - UINT32 length; - UINT32 offset; - UINT32 CommandType; - UINT32 CommandLength; - - if (!signature || !rts) - return FALSE; + RtsPduSignature extracted = { 0 }; - if (rts->Flags != signature->Flags) - return FALSE; + assert(signature); + assert(src); - if (rts->NumberOfCommands != signature->NumberOfCommands) + if (!rts_extract_pdu_signature(&extracted, src, header)) return FALSE; - buffer = (const BYTE*)rts; - offset = RTS_PDU_HEADER_LENGTH; - length = rts->header.frag_length - offset; - - for (i = 0; i < rts->NumberOfCommands; i++) - { - CommandType = *((UINT32*)&buffer[offset]); /* CommandType (4 bytes) */ - offset += 4; - - if (CommandType != signature->CommandTypes[i]) - return FALSE; + return memcmp(signature, &extracted, sizeof(extracted)) == 0; +} - status = rts_command_length(CommandType, &buffer[offset], length); +BOOL rts_extract_pdu_signature(RtsPduSignature* signature, wStream* src, + const rpcconn_hdr_t* header) +{ + BOOL rc = FALSE; + UINT16 i; + wStream tmp; + rpcconn_hdr_t rheader = { 0 }; + const rpcconn_rts_hdr_t* rts; - if (status < 0) - return FALSE; + assert(signature); + assert(src); - CommandLength = (UINT32)status; - offset += CommandLength; - length = rts->header.frag_length - offset; + Stream_StaticInit(&tmp, Stream_Pointer(src), Stream_GetRemainingLength(src)); + if (!header) + { + if (!rts_read_pdu_header(&tmp, &rheader)) + goto fail; + header = &rheader; } - - return TRUE; -} - -BOOL rts_extract_pdu_signature(RtsPduSignature* signature, const rpcconn_rts_hdr_t* rts) -{ - int i; - int status; - BYTE* buffer; - UINT32 length; - UINT32 offset; - UINT32 CommandType; - UINT32 CommandLength; - - if (!signature || !rts) - return FALSE; + rts = &header->rts; + if (rts->header.frag_length < sizeof(rpcconn_rts_hdr_t)) + goto fail; signature->Flags = rts->Flags; signature->NumberOfCommands = rts->NumberOfCommands; - buffer = (BYTE*)rts; - offset = RTS_PDU_HEADER_LENGTH; - length = rts->header.frag_length - offset; for (i = 0; i < rts->NumberOfCommands; i++) { - CommandType = *((UINT32*)&buffer[offset]); /* CommandType (4 bytes) */ - offset += 4; - signature->CommandTypes[i] = CommandType; - status = rts_command_length(CommandType, &buffer[offset], length); + UINT32 CommandType; + size_t CommandLength; + + if (Stream_GetRemainingLength(&tmp) < 4) + goto fail; + + Stream_Read_UINT32(&tmp, CommandType); /* CommandType (4 bytes) */ - if (status < 0) - return FALSE; + /* We only need this for comparison against known command types */ + if (i < ARRAYSIZE(signature->CommandTypes)) + signature->CommandTypes[i] = CommandType; - CommandLength = (UINT32)status; - offset += CommandLength; - length = rts->header.frag_length - offset; + if (!rts_command_length(CommandType, &tmp, &CommandLength)) + goto fail; + if (!Stream_SafeSeek(&tmp, CommandLength)) + goto fail; } - return TRUE; + rc = TRUE; +fail: + rts_free_pdu_header(&rheader, FALSE); + Stream_Free(&tmp, FALSE); + return rc; } UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, @@ -367,11 +354,15 @@ UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, { size_t i, j; - for (i = 0; RTS_PDU_SIGNATURE_TABLE[i].SignatureId != 0; i++) + if (entry) + *entry = NULL; + + for (i = 0; i < ARRAYSIZE(RTS_PDU_SIGNATURE_TABLE); i++) { - const RtsPduSignature* pSignature = RTS_PDU_SIGNATURE_TABLE[i].Signature; + const RTS_PDU_SIGNATURE_ENTRY* current = &RTS_PDU_SIGNATURE_TABLE[i]; + const RtsPduSignature* pSignature = current->Signature; - if (!RTS_PDU_SIGNATURE_TABLE[i].SignatureClient) + if (!current->SignatureClient) continue; if (signature->Flags != pSignature->Flags) @@ -387,9 +378,9 @@ UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, } if (entry) - *entry = &RTS_PDU_SIGNATURE_TABLE[i]; + *entry = current; - return RTS_PDU_SIGNATURE_TABLE[i].SignatureId; + return current->SignatureId; } return 0; diff --git a/libfreerdp/core/gateway/rts_signature.h b/libfreerdp/core/gateway/rts_signature.h index 2c43cdc1b898..31f0e810056c 100644 --- a/libfreerdp/core/gateway/rts_signature.h +++ b/libfreerdp/core/gateway/rts_signature.h @@ -178,10 +178,10 @@ FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_PING_SIGNATURE; FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE; FREERDP_LOCAL extern const RtsPduSignature RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE; -FREERDP_LOCAL BOOL rts_match_pdu_signature(const RtsPduSignature* signature, - const rpcconn_rts_hdr_t* rts); -FREERDP_LOCAL BOOL rts_extract_pdu_signature(RtsPduSignature* signature, - const rpcconn_rts_hdr_t* rts); +FREERDP_LOCAL BOOL rts_match_pdu_signature(const RtsPduSignature* signature, wStream* s, + const rpcconn_hdr_t* header); +FREERDP_LOCAL BOOL rts_extract_pdu_signature(RtsPduSignature* signature, wStream* s, + const rpcconn_hdr_t* header); FREERDP_LOCAL UINT32 rts_identify_pdu_signature(const RtsPduSignature* signature, const RTS_PDU_SIGNATURE_ENTRY** entry); FREERDP_LOCAL BOOL rts_print_pdu_signature(const RtsPduSignature* signature); diff --git a/libfreerdp/core/gateway/tsg.c b/libfreerdp/core/gateway/tsg.c index 9d01c9fdd6a0..70fdf9e275ba 100644 --- a/libfreerdp/core/gateway/tsg.c +++ b/libfreerdp/core/gateway/tsg.c @@ -24,6 +24,7 @@ #include "config.h" #endif +#include #include #include #include @@ -39,13 +40,14 @@ #define TAG FREERDP_TAG("core.gateway.tsg") +#define TSG_CAPABILITY_TYPE_NAP 0x00000001 + #define TSG_PACKET_TYPE_HEADER 0x00004844 #define TSG_PACKET_TYPE_VERSIONCAPS 0x00005643 #define TSG_PACKET_TYPE_QUARCONFIGREQUEST 0x00005143 #define TSG_PACKET_TYPE_QUARREQUEST 0x00005152 #define TSG_PACKET_TYPE_RESPONSE 0x00005052 #define TSG_PACKET_TYPE_QUARENC_RESPONSE 0x00004552 -#define TSG_CAPABILITY_TYPE_NAP 0x00000001 #define TSG_PACKET_TYPE_CAPS_RESPONSE 0x00004350 #define TSG_PACKET_TYPE_MSGREQUEST_PACKET 0x00004752 #define TSG_PACKET_TYPE_MESSAGE_PACKET 0x00004750 @@ -222,7 +224,6 @@ struct rdp_tsg UINT32 TunnelId; UINT32 ChannelId; BOOL reauthSequence; - rdpSettings* settings; rdpTransport* transport; UINT64 ReauthTunnelContext; CONTEXT_HANDLE TunnelContext; @@ -311,16 +312,35 @@ static BOOL tsg_print(char** buffer, size_t* len, const char* fmt, ...) static BOOL tsg_packet_header_to_string(char** buffer, size_t* length, const TSG_PACKET_HEADER* header) { + assert(buffer); + assert(length); + assert(header); + return tsg_print(buffer, length, "header { ComponentId=0x%04" PRIx16 ", PacketId=0x%04" PRIx16 " }", header->ComponentId, header->PacketId); } +static BOOL tsg_type_capability_nap_to_string(char** buffer, size_t* length, + const TSG_CAPABILITY_NAP* cur) +{ + assert(buffer); + assert(length); + assert(cur); + + return tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }", + tsg_packet_id_to_string(TSG_CAPABILITY_TYPE_NAP), cur->capabilities); +} + static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, const TSG_PACKET_CAPABILITIES* caps, UINT32 numCaps) { UINT32 x; + assert(buffer); + assert(length); + assert(caps); + if (!tsg_print(buffer, length, "capabilities { ")) return FALSE; @@ -330,9 +350,7 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, switch (cur->capabilityType) { case TSG_CAPABILITY_TYPE_NAP: - if (!tsg_print(buffer, length, "%s { capabilities=0x%08" PRIx32 " }", - tsg_packet_id_to_string(cur->capabilityType), - cur->tsgPacket.tsgCapNap.capabilities)) + if (!tsg_type_capability_nap_to_string(buffer, length, &cur->tsgPacket.tsgCapNap)) return FALSE; break; default: @@ -347,6 +365,10 @@ static BOOL tsg_packet_capabilities_to_string(char** buffer, size_t* length, static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length, const TSG_PACKET_VERSIONCAPS* caps) { + assert(buffer); + assert(length); + assert(caps); + if (!tsg_print(buffer, length, "versioncaps { ")) return FALSE; if (!tsg_packet_header_to_string(buffer, length, &caps->tsgHeader)) @@ -368,6 +390,281 @@ static BOOL tsg_packet_versioncaps_to_string(char** buffer, size_t* length, return tsg_print(buffer, length, " }"); } +static BOOL tsg_packet_quarconfigrequest_to_string(char** buffer, size_t* length, + const TSG_PACKET_QUARCONFIGREQUEST* caps) +{ + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "quarconfigrequest { ")) + return FALSE; + + if (!tsg_print(buffer, length, " ")) + return FALSE; + + if (!tsg_print(buffer, length, " flags=0x%08" PRIx32, caps->flags)) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static BOOL tsg_packet_quarrequest_to_string(char** buffer, size_t* length, + const TSG_PACKET_QUARREQUEST* caps) +{ + BOOL rc = FALSE; + char* name = NULL; + char* strdata = NULL; + + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "quarrequest { ")) + return FALSE; + + if (!tsg_print(buffer, length, " ")) + return FALSE; + + if (caps->nameLength > 0) + { + if (caps->nameLength > INT_MAX) + return FALSE; + if (ConvertFromUnicode(CP_UTF8, 0, caps->machineName, (int)caps->nameLength, &name, 0, NULL, + NULL) < 0) + return FALSE; + } + + strdata = winpr_BinToHexString(caps->data, caps->dataLen, TRUE); + if (strdata || (caps->dataLen == 0)) + rc = tsg_print(buffer, length, + " flags=0x%08" PRIx32 ", machineName=%s [%" PRIu32 "], data[%" PRIu32 "]=%s", + caps->flags, name, caps->nameLength, caps->dataLen, strdata); + free(name); + free(strdata); + if (!rc) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static const char* tsg_bool_to_string(BOOL val) +{ + if (val) + return "true"; + return "false"; +} + +static const char* tsg_redirection_flags_to_string(char* buffer, size_t size, + const TSG_REDIRECTION_FLAGS* flags) +{ + assert(buffer || (size == 0)); + assert(flags); + + _snprintf(buffer, size, + "enableAllRedirections=%s, disableAllRedirections=%s, driveRedirectionDisabled=%s, " + "printerRedirectionDisabled=%s, portRedirectionDisabled=%s, reserved=%s, " + "clipboardRedirectionDisabled=%s, pnpRedirectionDisabled=%s", + tsg_bool_to_string(flags->enableAllRedirections), + tsg_bool_to_string(flags->disableAllRedirections), + tsg_bool_to_string(flags->driveRedirectionDisabled), + tsg_bool_to_string(flags->printerRedirectionDisabled), + tsg_bool_to_string(flags->portRedirectionDisabled), + tsg_bool_to_string(flags->reserved), + tsg_bool_to_string(flags->clipboardRedirectionDisabled), + tsg_bool_to_string(flags->pnpRedirectionDisabled)); + return buffer; +} + +static BOOL tsg_packet_response_to_string(char** buffer, size_t* length, + const TSG_PACKET_RESPONSE* caps) +{ + BOOL rc = FALSE; + char* strdata = NULL; + char tbuffer[8192] = { 0 }; + + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "response { ")) + return FALSE; + + if (!tsg_print(buffer, length, " ")) + return FALSE; + + strdata = winpr_BinToHexString(caps->responseData, caps->responseDataLen, TRUE); + if (strdata || (caps->responseDataLen == 0)) + rc = tsg_print( + buffer, length, + " flags=0x%08" PRIx32 ", reserved=0x%08" PRIx32 ", responseData[%" PRIu32 + "]=%s, redirectionFlags={ %s }", + caps->flags, caps->reserved, caps->responseDataLen, strdata, + tsg_redirection_flags_to_string(tbuffer, ARRAYSIZE(tbuffer), &caps->redirectionFlags)); + free(strdata); + if (!rc) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static BOOL tsg_packet_quarenc_response_to_string(char** buffer, size_t* length, + const TSG_PACKET_QUARENC_RESPONSE* caps) +{ + BOOL rc = FALSE; + char* strdata = NULL; + RPC_CSTR uuid; + char tbuffer[8192] = { 0 }; + size_t size = ARRAYSIZE(tbuffer); + char* ptbuffer = tbuffer; + + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "quarenc_response { ")) + return FALSE; + + if (!tsg_print(buffer, length, " ")) + return FALSE; + + if (caps->certChainLen > 0) + { + if (caps->certChainLen > INT_MAX) + return FALSE; + if (ConvertFromUnicode(CP_UTF8, 0, caps->certChainData, (int)caps->certChainLen, &strdata, + 0, NULL, NULL) <= 0) + return FALSE; + } + + tsg_packet_versioncaps_to_string(&ptbuffer, &size, caps->versionCaps); + UuidToStringA(&caps->nonce, &uuid); + if (strdata || (caps->certChainLen == 0)) + rc = + tsg_print(buffer, length, + " flags=0x%08" PRIx32 ", certChain[%" PRIu32 "]=%s, nonce=%s, versionCaps=%s", + caps->flags, caps->certChainLen, strdata, uuid, tbuffer); + free(strdata); + free(uuid); + if (!rc) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static BOOL tsg_packet_message_response_to_string(char** buffer, size_t* length, + const TSG_PACKET_MSG_RESPONSE* caps) +{ + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "msg_response { ")) + return FALSE; + + if (!tsg_print(buffer, length, + " msgID=0x%08" PRIx32 ", msgType=0x%08" PRIx32 ", isMsgPresent=%" PRId32, + caps->msgID, caps->msgType, caps->isMsgPresent)) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static BOOL tsg_packet_caps_response_to_string(char** buffer, size_t* length, + const TSG_PACKET_CAPS_RESPONSE* caps) +{ + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "caps_response { ")) + return FALSE; + + if (!tsg_packet_quarenc_response_to_string(buffer, length, &caps->pktQuarEncResponse)) + return FALSE; + + if (!tsg_packet_message_response_to_string(buffer, length, &caps->pktConsentMessage)) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static BOOL tsg_packet_message_request_to_string(char** buffer, size_t* length, + const TSG_PACKET_MSG_REQUEST* caps) +{ + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "caps_message_request { ")) + return FALSE; + + if (!tsg_print(buffer, length, " maxMessagesPerBatch=%" PRIu32, caps->maxMessagesPerBatch)) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static BOOL tsg_packet_auth_to_string(char** buffer, size_t* length, const TSG_PACKET_AUTH* caps) +{ + BOOL rc = FALSE; + char* strdata = NULL; + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "caps_message_request { ")) + return FALSE; + + if (!tsg_packet_versioncaps_to_string(buffer, length, &caps->tsgVersionCaps)) + return FALSE; + + strdata = winpr_BinToHexString(caps->cookie, caps->cookieLen, TRUE); + if (strdata || (caps->cookieLen == 0)) + rc = tsg_print(buffer, length, " cookie[%" PRIu32 "]=%s", caps->cookieLen, strdata); + free(strdata); + if (!rc) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + +static BOOL tsg_packet_reauth_to_string(char** buffer, size_t* length, + const TSG_PACKET_REAUTH* caps) +{ + BOOL rc = FALSE; + assert(buffer); + assert(length); + assert(caps); + + if (!tsg_print(buffer, length, "caps_message_request { ")) + return FALSE; + + if (!tsg_print(buffer, length, " tunnelContext=0x%016" PRIx64 ", packetId=%s [0x%08" PRIx32 "]", + caps->tunnelContext, tsg_packet_id_to_string(caps->packetId), caps->packetId)) + return FALSE; + + switch (caps->packetId) + { + case TSG_PACKET_TYPE_VERSIONCAPS: + rc = tsg_packet_versioncaps_to_string(buffer, length, + caps->tsgInitialPacket.packetVersionCaps); + break; + case TSG_PACKET_TYPE_AUTH: + rc = tsg_packet_auth_to_string(buffer, length, caps->tsgInitialPacket.packetAuth); + break; + default: + rc = tsg_print(buffer, length, "TODO: Unhandled packet type %s [0x%08" PRIx32 "]", + tsg_packet_id_to_string(caps->packetId), caps->packetId); + break; + } + + if (!rc) + return FALSE; + + return tsg_print(buffer, length, " }"); +} + static const char* tsg_packet_to_string(const TSG_PACKET* packet) { size_t len = 8192; @@ -390,43 +687,45 @@ static const char* tsg_packet_to_string(const TSG_PACKET* packet) goto fail; break; case TSG_PACKET_TYPE_QUARCONFIGREQUEST: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_quarconfigrequest_to_string(&buffer, &len, + packet->tsgPacket.packetQuarConfigRequest)) goto fail; break; case TSG_PACKET_TYPE_QUARREQUEST: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_quarrequest_to_string(&buffer, &len, + packet->tsgPacket.packetQuarRequest)) goto fail; break; case TSG_PACKET_TYPE_RESPONSE: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_response_to_string(&buffer, &len, packet->tsgPacket.packetResponse)) goto fail; break; case TSG_PACKET_TYPE_QUARENC_RESPONSE: - if (!tsg_print(&buffer, &len, "TODO")) - goto fail; - break; - case TSG_CAPABILITY_TYPE_NAP: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_quarenc_response_to_string(&buffer, &len, + packet->tsgPacket.packetQuarEncResponse)) goto fail; break; case TSG_PACKET_TYPE_CAPS_RESPONSE: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_caps_response_to_string(&buffer, &len, + packet->tsgPacket.packetCapsResponse)) goto fail; break; case TSG_PACKET_TYPE_MSGREQUEST_PACKET: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_message_request_to_string(&buffer, &len, + packet->tsgPacket.packetMsgRequest)) goto fail; break; case TSG_PACKET_TYPE_MESSAGE_PACKET: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_message_response_to_string(&buffer, &len, + packet->tsgPacket.packetMsgResponse)) goto fail; break; case TSG_PACKET_TYPE_AUTH: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_auth_to_string(&buffer, &len, packet->tsgPacket.packetAuth)) goto fail; break; case TSG_PACKET_TYPE_REAUTH: - if (!tsg_print(&buffer, &len, "TODO")) + if (!tsg_packet_reauth_to_string(&buffer, &len, packet->tsgPacket.packetReauth)) goto fail; break; default: @@ -500,7 +799,7 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI { wStream* s; rdpTsg* tsg; - int length; + size_t length; const byte* buffer1 = NULL; const byte* buffer2 = NULL; const byte* buffer3 = NULL; @@ -536,7 +835,9 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI totalDataBytes += lengths[2] + 4; } - length = 28 + totalDataBytes; + length = 28ull + totalDataBytes; + if (length > INT_MAX) + return -1; s = Stream_New(NULL, length); if (!s) @@ -572,7 +873,7 @@ static int TsProxySendToServer(handle_t IDL_handle, const byte pRpcMessage[], UI if (!rpc_client_write_call(tsg->rpc, s, TsProxySendToServerOpnum)) return -1; - return length; + return (int)length; } /** @@ -725,12 +1026,19 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, UINT32 SwitchValue; UINT32 MessageSwitchValue = 0; UINT32 IsMessagePresent; + rdpContext* context; PTSG_PACKET_CAPABILITIES tsgCaps = NULL; PTSG_PACKET_VERSIONCAPS versionCaps = NULL; TSG_PACKET_STRING_MESSAGE packetStringMessage; PTSG_PACKET_CAPS_RESPONSE packetCapsResponse = NULL; PTSG_PACKET_QUARENC_RESPONSE packetQuarEncResponse = NULL; + assert(tsg); + assert(tsg->rpc); + + context = tsg->rpc->context; + assert(context); + if (!pdu) return FALSE; @@ -877,8 +1185,8 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, if (Stream_GetRemainingLength(pdu->s) < 16) goto fail; - Stream_Read_UINT32(pdu->s, packetStringMessage.isDisplayMandatory); - Stream_Read_UINT32(pdu->s, packetStringMessage.isConsentMandatory); + Stream_Read_INT32(pdu->s, packetStringMessage.isDisplayMandatory); + Stream_Read_INT32(pdu->s, packetStringMessage.isConsentMandatory); Stream_Read_UINT32(pdu->s, packetStringMessage.msgBytes); Stream_Read_UINT32(pdu->s, Pointer); @@ -907,16 +1215,15 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, goto fail; } - if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance) + if (context->instance) { - rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage, - tsg->rpc->context->instance, - TSG_ASYNC_MESSAGE_CONSENT_MESSAGE - ? GATEWAY_MESSAGE_CONSENT - : TSG_ASYNC_MESSAGE_SERVICE_MESSAGE, - packetStringMessage.isDisplayMandatory != 0, - packetStringMessage.isConsentMandatory != 0, - packetStringMessage.msgBytes, packetStringMessage.msgBuffer); + rc = IFCALLRESULT( + TRUE, context->instance->PresentGatewayMessage, context->instance, + TSG_ASYNC_MESSAGE_CONSENT_MESSAGE ? GATEWAY_MESSAGE_CONSENT + : TSG_ASYNC_MESSAGE_SERVICE_MESSAGE, + packetStringMessage.isDisplayMandatory != 0, + packetStringMessage.isConsentMandatory != 0, packetStringMessage.msgBytes, + packetStringMessage.msgBuffer); if (!rc) goto fail; } @@ -1079,16 +1386,19 @@ static BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnelContext) { - UINT32 pad; + size_t pad; wStream* s; size_t count; - UINT32 offset; + size_t offset; rdpRpc* rpc; if (!tsg || !tsg->rpc || !tunnelContext || !tsg->MachineName) return FALSE; count = _wcslen(tsg->MachineName) + 1; + if (count > UINT32_MAX) + return FALSE; + rpc = tsg->rpc; WLog_DBG(TAG, "TsProxyAuthorizeTunnelWriteRequest"); s = Stream_New(NULL, 1024 + count * 2); @@ -1105,13 +1415,13 @@ static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunn Stream_Write_UINT32(s, 0x00020000); /* PacketQuarRequestPtr (4 bytes) */ Stream_Write_UINT32(s, 0x00000000); /* Flags (4 bytes) */ Stream_Write_UINT32(s, 0x00020004); /* MachineNamePtr (4 bytes) */ - Stream_Write_UINT32(s, count); /* NameLength (4 bytes) */ + Stream_Write_UINT32(s, (UINT32)count); /* NameLength (4 bytes) */ Stream_Write_UINT32(s, 0x00020008); /* DataPtr (4 bytes) */ Stream_Write_UINT32(s, 0); /* DataLength (4 bytes) */ /* MachineName */ - Stream_Write_UINT32(s, count); /* MaxCount (4 bytes) */ + Stream_Write_UINT32(s, (UINT32)count); /* MaxCount (4 bytes) */ Stream_Write_UINT32(s, 0); /* Offset (4 bytes) */ - Stream_Write_UINT32(s, count); /* ActualCount (4 bytes) */ + Stream_Write_UINT32(s, (UINT32)count); /* ActualCount (4 bytes) */ Stream_Write_UTF16_String(s, tsg->MachineName, count); /* Array */ /* 4-byte alignment */ offset = Stream_GetPosition(s); @@ -1122,7 +1432,7 @@ static BOOL TsProxyAuthorizeTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunn return rpc_client_write_call(rpc, s, TsProxyAuthorizeTunnelOpnum); } -static BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu) +static BOOL TsProxyAuthorizeTunnelReadResponse(RPC_PDU* pdu) { BOOL rc = FALSE; UINT32 Pointer; @@ -1184,25 +1494,24 @@ static BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu) Stream_Seek_UINT32(pdu->s); /* Reserved (4 bytes) */ Stream_Read_UINT32(pdu->s, Pointer); /* ResponseDataPtr (4 bytes) */ Stream_Read_UINT32(pdu->s, packetResponse->responseDataLen); /* ResponseDataLength (4 bytes) */ - Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags - .enableAllRedirections); /* EnableAllRedirections (4 bytes) */ - Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags - .disableAllRedirections); /* DisableAllRedirections (4 bytes) */ - Stream_Read_UINT32(pdu->s, - packetResponse->redirectionFlags - .driveRedirectionDisabled); /* DriveRedirectionDisabled (4 bytes) */ - Stream_Read_UINT32(pdu->s, - packetResponse->redirectionFlags - .printerRedirectionDisabled); /* PrinterRedirectionDisabled (4 bytes) */ - Stream_Read_UINT32(pdu->s, - packetResponse->redirectionFlags - .portRedirectionDisabled); /* PortRedirectionDisabled (4 bytes) */ - Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags.reserved); /* Reserved (4 bytes) */ - Stream_Read_UINT32( + Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags + .enableAllRedirections); /* EnableAllRedirections (4 bytes) */ + Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags + .disableAllRedirections); /* DisableAllRedirections (4 bytes) */ + Stream_Read_INT32(pdu->s, + packetResponse->redirectionFlags + .driveRedirectionDisabled); /* DriveRedirectionDisabled (4 bytes) */ + Stream_Read_INT32(pdu->s, + packetResponse->redirectionFlags + .printerRedirectionDisabled); /* PrinterRedirectionDisabled (4 bytes) */ + Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags + .portRedirectionDisabled); /* PortRedirectionDisabled (4 bytes) */ + Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags.reserved); /* Reserved (4 bytes) */ + Stream_Read_INT32( pdu->s, packetResponse->redirectionFlags .clipboardRedirectionDisabled); /* ClipboardRedirectionDisabled (4 bytes) */ - Stream_Read_UINT32(pdu->s, packetResponse->redirectionFlags - .pnpRedirectionDisabled); /* PnpRedirectionDisabled (4 bytes) */ + Stream_Read_INT32(pdu->s, packetResponse->redirectionFlags + .pnpRedirectionDisabled); /* PnpRedirectionDisabled (4 bytes) */ Stream_Read_UINT32(pdu->s, SizeValue); /* (4 bytes) */ if (SizeValue != packetResponse->responseDataLen) @@ -1302,11 +1611,18 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu) UINT32 Pointer; UINT32 SwitchValue; TSG_PACKET packet; + rdpContext* context; char* messageText = NULL; TSG_PACKET_MSG_RESPONSE packetMsgResponse = { 0 }; TSG_PACKET_STRING_MESSAGE packetStringMessage = { 0 }; TSG_PACKET_REAUTH_MESSAGE packetReauthMessage = { 0 }; + assert(tsg); + assert(tsg->rpc); + + context = tsg->rpc->context; + assert(context); + /* This is an asynchronous response */ if (!pdu) @@ -1356,10 +1672,10 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu) WLog_INFO(TAG, "Consent Message: %s", messageText); free(messageText); - if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance) + if (context->instance) { - rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage, - tsg->rpc->context->instance, GATEWAY_MESSAGE_CONSENT, + rc = IFCALLRESULT(TRUE, context->instance->PresentGatewayMessage, context->instance, + GATEWAY_MESSAGE_CONSENT, packetStringMessage.isDisplayMandatory != 0, packetStringMessage.isConsentMandatory != 0, packetStringMessage.msgBytes, packetStringMessage.msgBuffer); @@ -1377,10 +1693,10 @@ static BOOL TsProxyMakeTunnelCallReadResponse(rdpTsg* tsg, RPC_PDU* pdu) WLog_INFO(TAG, "Service Message: %s", messageText); free(messageText); - if (tsg->rpc && tsg->rpc->context && tsg->rpc->context->instance) + if (context->instance) { - rc = IFCALLRESULT(TRUE, tsg->rpc->context->instance->PresentGatewayMessage, - tsg->rpc->context->instance, GATEWAY_MESSAGE_SERVICE, + rc = IFCALLRESULT(TRUE, context->instance->PresentGatewayMessage, context->instance, + GATEWAY_MESSAGE_SERVICE, packetStringMessage.isDisplayMandatory != 0, packetStringMessage.isConsentMandatory != 0, packetStringMessage.msgBytes, packetStringMessage.msgBuffer); @@ -1432,6 +1748,8 @@ static BOOL TsProxyCreateChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnel rpc = tsg->rpc; count = _wcslen(tsg->Hostname) + 1; + if (count > UINT32_MAX) + return FALSE; s = Stream_New(NULL, 60 + count * 2); if (!s) @@ -1451,15 +1769,15 @@ static BOOL TsProxyCreateChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* tunnel Stream_Write_UINT16(s, tsg->Port); /* PortNumber (0xD3D = 3389) (2 bytes) */ Stream_Write_UINT32(s, 0x00000001); /* NumResourceNames (4 bytes) */ Stream_Write_UINT32(s, 0x00020004); /* ResourceNamePtr (4 bytes) */ - Stream_Write_UINT32(s, count); /* MaxCount (4 bytes) */ + Stream_Write_UINT32(s, (UINT32)count); /* MaxCount (4 bytes) */ Stream_Write_UINT32(s, 0); /* Offset (4 bytes) */ - Stream_Write_UINT32(s, count); /* ActualCount (4 bytes) */ + Stream_Write_UINT32(s, (UINT32)count); /* ActualCount (4 bytes) */ Stream_Write_UTF16_String(s, tsg->Hostname, count); /* Array */ return rpc_client_write_call(rpc, s, TsProxyCreateChannelOpnum); } -static BOOL TsProxyCreateChannelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, - CONTEXT_HANDLE* channelContext, UINT32* channelId) +static BOOL TsProxyCreateChannelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* channelContext, + UINT32* channelId) { BOOL rc = FALSE; WLog_DBG(TAG, "TsProxyCreateChannelReadResponse"); @@ -1507,7 +1825,7 @@ static BOOL TsProxyCloseChannelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* context return rpc_client_write_call(rpc, s, TsProxyCloseChannelOpnum); } -static BOOL TsProxyCloseChannelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, CONTEXT_HANDLE* context) +static BOOL TsProxyCloseChannelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* context) { BOOL rc = FALSE; WLog_DBG(TAG, "TsProxyCloseChannelReadResponse"); @@ -1554,7 +1872,7 @@ static BOOL TsProxyCloseTunnelWriteRequest(rdpTsg* tsg, CONTEXT_HANDLE* context) return rpc_client_write_call(rpc, s, TsProxyCloseTunnelOpnum); } -static BOOL TsProxyCloseTunnelReadResponse(rdpTsg* tsg, RPC_PDU* pdu, CONTEXT_HANDLE* context) +static BOOL TsProxyCloseTunnelReadResponse(RPC_PDU* pdu, CONTEXT_HANDLE* context) { BOOL rc = FALSE; WLog_DBG(TAG, "TsProxyCloseTunnelReadResponse"); @@ -1705,8 +2023,6 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) return FALSE; rpc = tsg->rpc; - Stream_SealLength(pdu->s); - Stream_SetPosition(pdu->s, 0); if (!(pdu->Flags & RPC_PDU_FLAG_STUB)) { @@ -1745,7 +2061,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) CONTEXT_HANDLE* TunnelContext; TunnelContext = (tsg->reauthSequence) ? &tsg->NewTunnelContext : &tsg->TunnelContext; - if (!TsProxyAuthorizeTunnelReadResponse(tsg, pdu)) + if (!TsProxyAuthorizeTunnelReadResponse(pdu)) { WLog_ERR(TAG, "TsProxyAuthorizeTunnelReadResponse failure"); return FALSE; @@ -1794,7 +2110,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) { CONTEXT_HANDLE ChannelContext; - if (!TsProxyCreateChannelReadResponse(tsg, pdu, &ChannelContext, &tsg->ChannelId)) + if (!TsProxyCreateChannelReadResponse(pdu, &ChannelContext, &tsg->ChannelId)) { WLog_ERR(TAG, "TsProxyCreateChannelReadResponse failure"); return FALSE; @@ -1867,7 +2183,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) { CONTEXT_HANDLE ChannelContext; - if (!TsProxyCloseChannelReadResponse(tsg, pdu, &ChannelContext)) + if (!TsProxyCloseChannelReadResponse(pdu, &ChannelContext)) { WLog_ERR(TAG, "TsProxyCloseChannelReadResponse failure"); return FALSE; @@ -1879,7 +2195,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) { CONTEXT_HANDLE TunnelContext; - if (!TsProxyCloseTunnelReadResponse(tsg, pdu, &TunnelContext)) + if (!TsProxyCloseTunnelReadResponse(pdu, &TunnelContext)) { WLog_ERR(TAG, "TsProxyCloseTunnelReadResponse failure"); return FALSE; @@ -1894,7 +2210,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) { CONTEXT_HANDLE ChannelContext; - if (!TsProxyCloseChannelReadResponse(tsg, pdu, &ChannelContext)) + if (!TsProxyCloseChannelReadResponse(pdu, &ChannelContext)) { WLog_ERR(TAG, "TsProxyCloseChannelReadResponse failure"); return FALSE; @@ -1924,7 +2240,7 @@ BOOL tsg_recv_pdu(rdpTsg* tsg, RPC_PDU* pdu) { CONTEXT_HANDLE TunnelContext; - if (!TsProxyCloseTunnelReadResponse(tsg, pdu, &TunnelContext)) + if (!TsProxyCloseTunnelReadResponse(pdu, &TunnelContext)) { WLog_ERR(TAG, "TsProxyCloseTunnelReadResponse failure"); return FALSE; @@ -2033,10 +2349,25 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, DWORD timeout) { UINT64 looptimeout = timeout * 1000ULL; DWORD nCount; - HANDLE events[64]; - rdpRpc* rpc = tsg->rpc; - rdpSettings* settings = rpc->settings; - rdpTransport* transport = rpc->transport; + HANDLE events[MAXIMUM_WAIT_OBJECTS] = { 0 }; + rdpRpc* rpc; + rdpContext* context; + rdpSettings* settings; + rdpTransport* transport; + + assert(tsg); + + rpc = tsg->rpc; + assert(rpc); + + transport = rpc->transport; + assert(transport); + + context = tsg->rpc->context; + assert(context); + + settings = context->settings; + tsg->Port = port; tsg->transport = transport; @@ -2055,7 +2386,7 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port, DWORD timeout) return FALSE; } - nCount = tsg_get_event_handles(tsg, events, 64); + nCount = tsg_get_event_handles(tsg, events, ARRAYSIZE(events)); if (nCount == 0) return FALSE; @@ -2138,7 +2469,7 @@ BOOL tsg_disconnect(rdpTsg* tsg) * @return < 0 on error; 0 if not enough data is available (non blocking mode); > 0 bytes to read */ -static int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length) +static int tsg_read(rdpTsg* tsg, BYTE* data, size_t length) { rdpRpc* rpc; int status = 0; @@ -2156,7 +2487,7 @@ static int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length) do { - status = rpc_client_receive_pipe_read(rpc->client, data, (size_t)length); + status = rpc_client_receive_pipe_read(rpc->client, data, length); if (status < 0) return -1; @@ -2206,7 +2537,7 @@ static int tsg_write(rdpTsg* tsg, const BYTE* data, UINT32 length) if (status < 0) return -1; - return length; + return (int)length; } rdpTsg* tsg_new(rdpTransport* transport) @@ -2218,7 +2549,6 @@ rdpTsg* tsg_new(rdpTransport* transport) return NULL; tsg->transport = transport; - tsg->settings = transport->settings; tsg->rpc = rpc_new(tsg->transport); if (!tsg->rpc) @@ -2246,7 +2576,10 @@ static int transport_bio_tsg_write(BIO* bio, const char* buf, int num) int status; rdpTsg* tsg = (rdpTsg*)BIO_get_data(bio); BIO_clear_flags(bio, BIO_FLAGS_WRITE); - status = tsg_write(tsg, (BYTE*)buf, num); + + if (num < 0) + return -1; + status = tsg_write(tsg, (const BYTE*)buf, (UINT32)num); if (status < 0) { @@ -2278,7 +2611,7 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size) } BIO_clear_flags(bio, BIO_FLAGS_READ); - status = tsg_read(tsg, (BYTE*)buf, size); + status = tsg_read(tsg, (BYTE*)buf, (size_t)size); if (status < 0) { @@ -2300,17 +2633,22 @@ static int transport_bio_tsg_read(BIO* bio, char* buf, int size) static int transport_bio_tsg_puts(BIO* bio, const char* str) { + WINPR_UNUSED(bio); + WINPR_UNUSED(str); return 1; } static int transport_bio_tsg_gets(BIO* bio, char* str, int size) { + WINPR_UNUSED(bio); + WINPR_UNUSED(str); + WINPR_UNUSED(size); return 1; } static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) { - int status = -1; + long status = -1; rdpTsg* tsg = (rdpTsg*)BIO_get_data(bio); RpcVirtualConnection* connection = tsg->rpc->VirtualConnection; RpcInChannel* inChannel = connection->DefaultInChannel; @@ -2339,27 +2677,27 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) case BIO_C_READ_BLOCKED: { - BIO* bio = outChannel->common.bio; - status = BIO_read_blocked(bio); + BIO* cbio = outChannel->common.bio; + status = BIO_read_blocked(cbio); } break; case BIO_C_WRITE_BLOCKED: { - BIO* bio = inChannel->common.bio; - status = BIO_write_blocked(bio); + BIO* cbio = inChannel->common.bio; + status = BIO_write_blocked(cbio); } break; case BIO_C_WAIT_READ: { int timeout = (int)arg1; - BIO* bio = outChannel->common.bio; + BIO* cbio = outChannel->common.bio; - if (BIO_read_blocked(bio)) - return BIO_wait_read(bio, timeout); - else if (BIO_write_blocked(bio)) - return BIO_wait_write(bio, timeout); + if (BIO_read_blocked(cbio)) + return BIO_wait_read(cbio, timeout); + else if (BIO_write_blocked(cbio)) + return BIO_wait_write(cbio, timeout); else status = 1; } @@ -2368,17 +2706,24 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) case BIO_C_WAIT_WRITE: { int timeout = (int)arg1; - BIO* bio = inChannel->common.bio; + BIO* cbio = inChannel->common.bio; - if (BIO_write_blocked(bio)) - status = BIO_wait_write(bio, timeout); - else if (BIO_read_blocked(bio)) - status = BIO_wait_read(bio, timeout); + if (BIO_write_blocked(cbio)) + status = BIO_wait_write(cbio, timeout); + else if (BIO_read_blocked(cbio)) + status = BIO_wait_read(cbio, timeout); else status = 1; } break; - +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + case BIO_CTRL_GET_KTLS_SEND: + status = 0; + break; + case BIO_CTRL_GET_KTLS_RECV: + status = 0; + break; +#endif default: break; } @@ -2388,6 +2733,7 @@ static long transport_bio_tsg_ctrl(BIO* bio, int cmd, long arg1, void* arg2) static int transport_bio_tsg_new(BIO* bio) { + assert(bio); BIO_set_init(bio, 1); BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); return 1; @@ -2395,6 +2741,8 @@ static int transport_bio_tsg_new(BIO* bio) static int transport_bio_tsg_free(BIO* bio) { + assert(bio); + WINPR_UNUSED(bio); return 1; } diff --git a/libfreerdp/core/heartbeat.c b/libfreerdp/core/heartbeat.c index 8418ba3cb695..b019d33c4685 100644 --- a/libfreerdp/core/heartbeat.c +++ b/libfreerdp/core/heartbeat.c @@ -45,7 +45,9 @@ int rdp_recv_heartbeat_packet(rdpRdp* rdp, wStream* s) "received Heartbeat PDU -> period=%" PRIu8 ", count1=%" PRIu8 ", count2=%" PRIu8 "", period, count1, count2); - rc = IFCALLRESULT(TRUE, rdp->heartbeat->ServerHeartbeat, rdp->instance, period, count1, count2); + WINPR_ASSERT(rdp->context->instance); + rc = IFCALLRESULT(TRUE, rdp->heartbeat->ServerHeartbeat, rdp->context->instance, period, count1, + count2); if (!rc) { WLog_ERR(HEARTBEAT_TAG, "heartbeat->ServerHeartbeat callback failed!"); diff --git a/libfreerdp/core/info.c b/libfreerdp/core/info.c index 3f8afaef26db..2f1bf21b9cbb 100644 --- a/libfreerdp/core/info.c +++ b/libfreerdp/core/info.c @@ -41,11 +41,13 @@ static const char* const INFO_TYPE_LOGON_STRINGS[4] = { "Logon Info V1", "Logon /* This define limits the length of the strings in the label field. */ #define MAX_LABEL_LENGTH 40 -static struct +struct info_flags_t { UINT32 flag; const char* label; -} const info_flags[] = { +}; + +static const struct info_flags_t info_flags[] = { { INFO_MOUSE, "INFO_MOUSE" }, { INFO_DISABLECTRLALTDEL, "INFO_DISABLECTRLALTDEL" }, { INFO_AUTOLOGON, "INFO_AUTOLOGON" }, @@ -67,8 +69,8 @@ static struct { INFO_HIDEF_RAIL_SUPPORTED, "INFO_HIDEF_RAIL_SUPPORTED" }, }; -static BOOL rdp_read_info_null_string(UINT32 flags, wStream* s, size_t cbLen, CHAR** dst, - size_t max) +static BOOL rdp_read_info_null_string(const char* what, UINT32 flags, wStream* s, size_t cbLen, + CHAR** dst, size_t max, BOOL isNullTerminated) { CHAR* ret = NULL; @@ -76,23 +78,24 @@ static BOOL rdp_read_info_null_string(UINT32 flags, wStream* s, size_t cbLen, CH const size_t nullSize = unicode ? sizeof(WCHAR) : sizeof(CHAR); if (Stream_GetRemainingLength(s) < (size_t)(cbLen)) + { + WLog_ERR(TAG, "protocol error: no data to read for %s [expected %" PRIuz "]", what, cbLen); return FALSE; + } if (cbLen > 0) { - WCHAR domain[512 / sizeof(WCHAR) + sizeof(WCHAR)] = { 0 }; - /* cbDomain is the size in bytes of the character data in the Domain field. - * This size excludes (!) the length of the mandatory null terminator. - * Maximum value including the mandatory null terminator: 512 - */ - if ((cbLen % 2) || (cbLen > (max - nullSize))) + const WCHAR* domain = (WCHAR*)Stream_Pointer(s); + + if (isNullTerminated && (max > 0)) + max -= nullSize; + + if ((cbLen > max) || (unicode && ((cbLen % 2) != 0))) { - WLog_ERR(TAG, "protocol error: invalid value: %" PRIuz "", cbLen); + WLog_ERR(TAG, "protocol error: %s has invalid value: %" PRIuz "", what, cbLen); return FALSE; } - Stream_Read(s, domain, cbLen); - if (unicode) { if (ConvertFromUnicode(CP_UTF8, 0, domain, cbLen, &ret, 0, NULL, NULL) < 1) @@ -108,6 +111,13 @@ static BOOL rdp_read_info_null_string(UINT32 flags, wStream* s, size_t cbLen, CH return FALSE; memcpy(ret, domain, cbLen); } + + if (!Stream_SafeSeek(s, cbLen)) + { + WLog_ERR(TAG, "protocol error: no data to read for %s [expected %" PRIuz "]", what, + cbLen); + return FALSE; + } } free(*dst); @@ -118,12 +128,8 @@ static BOOL rdp_read_info_null_string(UINT32 flags, wStream* s, size_t cbLen, CH static char* rdp_info_package_flags_description(UINT32 flags) { char* result; - size_t maximum_size = 1; /* Reserve space for the terminating '\0' by strcat if all flags set */ + size_t maximum_size = 1 + MAX_LABEL_LENGTH * ARRAYSIZE(info_flags); size_t i; - size_t size; - - for (i = 0; i < ARRAYSIZE(info_flags); i++) - maximum_size += strnlen(info_flags[i].label, MAX_LABEL_LENGTH) + 1; result = calloc(maximum_size, sizeof(char)); @@ -132,18 +138,13 @@ static char* rdp_info_package_flags_description(UINT32 flags) for (i = 0; i < ARRAYSIZE(info_flags); i++) { - if (info_flags[i].flag & flags) + const struct info_flags_t* cur = &info_flags[i]; + if (cur->flag & flags) { - strcat(result, info_flags[i].label); - strcat(result, "|"); + winpr_str_append(cur->label, result, maximum_size, "|"); } } - size = strnlen(result, maximum_size); - - if (size > 0) - result[size - 1] = '\0'; /* remove last "|" */ - return result; } @@ -258,13 +259,23 @@ static BOOL rdp_read_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s) * @param settings settings */ -static void rdp_write_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s) +static BOOL rdp_write_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s) { BYTE* p; ARC_CS_PRIVATE_PACKET* autoReconnectCookie; - rdpSettings* settings = rdp->settings; + rdpSettings* settings; + + WINPR_ASSERT(rdp); + + settings = rdp->settings; + WINPR_ASSERT(settings); + autoReconnectCookie = settings->ClientAutoReconnectCookie; + WINPR_ASSERT(autoReconnectCookie); + p = autoReconnectCookie->securityVerifier; + WINPR_ASSERT(p); + WLog_DBG(TAG, "ClientAutoReconnectCookie: Version: %" PRIu32 " LogonId: %" PRIu32 " ArcRandomBits: " "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "%02" PRIX8 @@ -273,10 +284,34 @@ static void rdp_write_client_auto_reconnect_cookie(rdpRdp* rdp, wStream* s) "%02" PRIX8 "", autoReconnectCookie->version, autoReconnectCookie->logonId, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + if (!Stream_EnsureRemainingCapacity(s, 12ull + 16ull)) + return FALSE; Stream_Write_UINT32(s, autoReconnectCookie->cbLen); /* cbLen (4 bytes) */ Stream_Write_UINT32(s, autoReconnectCookie->version); /* version (4 bytes) */ Stream_Write_UINT32(s, autoReconnectCookie->logonId); /* LogonId (4 bytes) */ Stream_Write(s, autoReconnectCookie->securityVerifier, 16); /* SecurityVerifier (16 bytes) */ + return TRUE; +} + +/* + * Get the cbClientAddress size limit + * see [MS-RDPBCGR] 2.2.1.11.1.1.1 Extended Info Packet (TS_EXTENDED_INFO_PACKET) + */ + +static size_t rdp_get_client_address_max_size(const rdpRdp* rdp) +{ + UINT32 version; + rdpSettings* settings; + + WINPR_ASSERT(rdp); + + settings = rdp->settings; + WINPR_ASSERT(settings); + + version = freerdp_settings_get_uint32(settings, FreeRDP_RdpVersion); + if (version < RDP_VERSION_10_0) + return 64; + return 80; } /** @@ -300,27 +335,11 @@ static BOOL rdp_read_extended_info_packet(rdpRdp* rdp, wStream* s) Stream_Read_UINT16(s, clientAddressFamily); /* clientAddressFamily (2 bytes) */ Stream_Read_UINT16(s, cbClientAddress); /* cbClientAddress (2 bytes) */ - /* cbClientAddress is the size in bytes of the character data in the clientAddress field. - * This size includes the length of the mandatory null terminator. - * The maximum allowed value is 80 bytes - * Note: Although according to [MS-RDPBCGR 2.2.1.11.1.1.1] the null terminator - * is mandatory, connections via Microsoft's TS Gateway set cbClientAddress to 0. - */ - - if ((cbClientAddress % 2) || cbClientAddress > 80) - { - WLog_ERR(TAG, "protocol error: invalid cbClientAddress value: %" PRIu16 "", - cbClientAddress); - return FALSE; - } - settings->IPv6Enabled = (clientAddressFamily == ADDRESS_FAMILY_INET6 ? TRUE : FALSE); - if (Stream_GetRemainingLength(s) < cbClientAddress) - return FALSE; - - if (!rdp_read_info_null_string(INFO_UNICODE, s, cbClientAddress, &settings->ClientAddress, - (settings->RdpVersion < RDP_VERSION_10_0) ? 64 : 80)) + if (!rdp_read_info_null_string("cbClientAddress", INFO_UNICODE, s, cbClientAddress, + &settings->ClientAddress, rdp_get_client_address_max_size(rdp), + TRUE)) return FALSE; if (Stream_GetRemainingLength(s) < 2) @@ -336,7 +355,8 @@ static BOOL rdp_read_extended_info_packet(rdpRdp* rdp, wStream* s) * sets cbClientDir to 0. */ - if (!rdp_read_info_null_string(INFO_UNICODE, s, cbClientDir, &settings->ClientDir, 512)) + if (!rdp_read_info_null_string("cbClientDir", INFO_UNICODE, s, cbClientDir, + &settings->ClientDir, 512, TRUE)) return FALSE; /** @@ -406,9 +426,11 @@ static BOOL rdp_write_extended_info_packet(rdpRdp* rdp, wStream* s) int rc; UINT16 clientAddressFamily; WCHAR* clientAddress = NULL; - UINT16 cbClientAddress; + size_t cbClientAddress; + const size_t cbClientAddressMax = rdp_get_client_address_max_size(rdp); WCHAR* clientDir = NULL; - UINT16 cbClientDir; + size_t cbClientDir; + const size_t cbClientDirMax = 512; UINT16 cbAutoReconnectCookie; rdpSettings* settings; if (!rdp || !rdp->settings || !s) @@ -419,30 +441,53 @@ static BOOL rdp_write_extended_info_packet(rdpRdp* rdp, wStream* s) if ((rc < 0) || (rc > (UINT16_MAX / 2))) goto fail; cbClientAddress = (UINT16)rc * 2; + if (cbClientAddress > cbClientAddressMax) + { + WLog_WARN(TAG, + "[%s] the client address %s [%" PRIuz "] exceeds the limit of %" PRIuz + ", truncating.", + __FUNCTION__, settings->ClientAddress, cbClientAddress, cbClientAddressMax); + + clientAddress[(cbClientAddressMax / sizeof(WCHAR)) - 1] = '\0'; + cbClientAddress = cbClientAddressMax; + } rc = ConvertToUnicode(CP_UTF8, 0, settings->ClientDir, -1, &clientDir, 0); if ((rc < 0) || (rc > (UINT16_MAX / 2))) goto fail; cbClientDir = (UINT16)rc * 2; + if (cbClientDir > cbClientDirMax) + { + WLog_WARN( + TAG, "[%s] the client dir %s [%" PRIuz "] exceeds the limit of %" PRIuz ", truncating.", + __FUNCTION__, settings->ClientDir, cbClientDir, cbClientDirMax); + + clientDir[(cbClientDirMax / sizeof(WCHAR)) - 1] = '\0'; + cbClientDir = cbClientDirMax; + } if (settings->ServerAutoReconnectCookie->cbLen > UINT16_MAX) goto fail; cbAutoReconnectCookie = (UINT16)settings->ServerAutoReconnectCookie->cbLen; + if (!Stream_EnsureRemainingCapacity(s, 4ull + cbClientAddress + 2ull + cbClientDir)) + goto fail; + Stream_Write_UINT16(s, clientAddressFamily); /* clientAddressFamily (2 bytes) */ - Stream_Write_UINT16(s, cbClientAddress + 2); /* cbClientAddress (2 bytes) */ + Stream_Write_UINT16(s, cbClientAddress); /* cbClientAddress (2 bytes) */ Stream_Write(s, clientAddress, cbClientAddress); /* clientAddress */ - Stream_Write_UINT16(s, 0); - Stream_Write_UINT16(s, cbClientDir + 2); /* cbClientDir (2 bytes) */ + Stream_Write_UINT16(s, cbClientDir); /* cbClientDir (2 bytes) */ Stream_Write(s, clientDir, cbClientDir); /* clientDir */ - Stream_Write_UINT16(s, 0); if (!rdp_write_client_time_zone(s, settings)) /* clientTimeZone (172 bytes) */ goto fail; + if (!Stream_EnsureRemainingCapacity(s, 10ull)) + goto fail; + Stream_Write_UINT32(s, 0); /* clientSessionId (4 bytes), should be set to 0 */ freerdp_performance_flags_make(settings); Stream_Write_UINT32(s, settings->PerformanceFlags); /* performanceFlags (4 bytes) */ @@ -452,7 +497,11 @@ static BOOL rdp_write_extended_info_packet(rdpRdp* rdp, wStream* s) { if (!rdp_compute_client_auto_reconnect_cookie(rdp)) goto fail; - rdp_write_client_auto_reconnect_cookie(rdp, s); /* autoReconnectCookie */ + if (!rdp_write_client_auto_reconnect_cookie(rdp, s)) /* autoReconnectCookie */ + goto fail; + + if (!Stream_EnsureRemainingCapacity(s, 4ull)) + goto fail; Stream_Write_UINT16(s, 0); /* reserved1 (2 bytes) */ Stream_Write_UINT16(s, 0); /* reserved2 (2 bytes) */ } @@ -711,7 +760,9 @@ static BOOL rdp_write_info_packet(rdpRdp* rdp, wStream* s) if (!settings->RemoteAssistanceMode) { - if (settings->RedirectionPassword && settings->RedirectionPasswordLength > 0) + /* Ignore redirection password if we´re using smartcard and have the pin as password */ + if (((flags & INFO_PASSWORD_IS_SC_PIN) == 0) && settings->RedirectionPassword && + (settings->RedirectionPasswordLength > 0)) { union { BYTE* bp; @@ -1126,8 +1177,13 @@ static BOOL rdp_recv_logon_error_info(rdpRdp* rdp, wStream* s, logon_info_ex* in Stream_Read_UINT32(s, errorNotificationData); /* errorNotificationData (4 bytes) */ WLog_DBG(TAG, "LogonErrorInfo: Data: 0x%08" PRIX32 " Type: 0x%08" PRIX32 "", errorNotificationData, errorNotificationType); - IFCALL(rdp->instance->LogonErrorInfo, rdp->instance, errorNotificationData, - errorNotificationType); + + WINPR_ASSERT(rdp); + WINPR_ASSERT(rdp->context); + freerdp* instance = rdp->context->instance; + + WINPR_ASSERT(instance); + IFCALL(instance->LogonErrorInfo, instance, errorNotificationData, errorNotificationType); info->ErrorNotificationType = errorNotificationType; info->ErrorNotificationData = errorNotificationData; return TRUE; @@ -1271,6 +1327,10 @@ static BOOL rdp_write_logon_info_v1(wStream* s, logon_info* info) return FALSE; /* domain */ + WINPR_ASSERT(info); + if (!info->domain || !info->username) + return FALSE; + ilen = ConvertToUnicode(CP_UTF8, 0, info->domain, -1, &wString, 0); if (ilen < 0) @@ -1312,7 +1372,7 @@ static BOOL rdp_write_logon_info_v1(wStream* s, logon_info* info) return TRUE; } -static BOOL rdp_write_logon_info_v2(wStream* s, logon_info* info) +static BOOL rdp_write_logon_info_v2(wStream* s, const logon_info* info) { UINT32 Size = 2 + 4 + 4 + 4 + 4 + 558; size_t domainLen, usernameLen; @@ -1325,11 +1385,13 @@ static BOOL rdp_write_logon_info_v2(wStream* s, logon_info* info) Stream_Write_UINT16(s, SAVE_SESSION_PDU_VERSION_ONE); Stream_Write_UINT32(s, Size); Stream_Write_UINT32(s, info->sessionId); - domainLen = strlen(info->domain); + if (info->domain) + domainLen = strlen(info->domain); if (domainLen > UINT32_MAX) return FALSE; Stream_Write_UINT32(s, (UINT32)(domainLen + 1) * 2); - usernameLen = strlen(info->username); + if (info->username) + usernameLen = strlen(info->username); if (usernameLen > UINT32_MAX) return FALSE; Stream_Write_UINT32(s, (UINT32)(usernameLen + 1) * 2); @@ -1408,6 +1470,7 @@ BOOL rdp_send_save_session_info(rdpContext* context, UINT32 type, void* data) { wStream* s; BOOL status; + WINPR_ASSERT(context); rdpRdp* rdp = context->rdp; s = rdp_data_pdu_init(rdp); diff --git a/libfreerdp/core/listener.c b/libfreerdp/core/listener.c index 3d0a1b164a4a..dadc73dd5932 100644 --- a/libfreerdp/core/listener.c +++ b/libfreerdp/core/listener.c @@ -224,12 +224,13 @@ static BOOL freerdp_listener_open_from_socket(freerdp_listener* instance, int fd return FALSE; listener->sockfds[listener->num_sockfds] = fd; - listener->events[listener->num_sockfds] = - CreateFileDescriptorEvent(NULL, FALSE, FALSE, fd, WINPR_FD_READ); + listener->events[listener->num_sockfds] = WSACreateEvent(); if (!listener->events[listener->num_sockfds]) return FALSE; + WSAEventSelect(fd, listener->events[listener->num_sockfds], FD_READ | FD_ACCEPT | FD_CLOSE); + listener->num_sockfds++; WLog_INFO(TAG, "Listening on socket %d.", fd); return TRUE; @@ -357,7 +358,7 @@ static BOOL freerdp_listener_check_fds(freerdp_listener* instance) } #ifndef _WIN32 - else if (peer_addr.ss_family == AF_UNIX) + else if (peer_addr.ss_family == AF_UNIX || peer_addr.ss_family == AF_VSOCK) client->local = TRUE; #endif diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 8357ff9abe53..5c3c54a1140a 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -277,6 +277,9 @@ static BOOL nego_tcp_connect(rdpNego* nego) { if (!nego->TcpConnected) { + const UINT32 TcpConnectTimeout = freerdp_settings_get_uint32( + nego->transport->context->settings, FreeRDP_TcpConnectTimeout); + if (nego->GatewayEnabled) { if (nego->GatewayBypassLocal) @@ -286,20 +289,21 @@ static BOOL nego_tcp_connect(rdpNego* nego) "Detecting if host can be reached locally. - This might take some time."); WLog_INFO(TAG, "To disable auto detection use /gateway-usage-method:direct"); transport_set_gateway_enabled(nego->transport, FALSE); - nego->TcpConnected = - transport_connect(nego->transport, nego->hostname, nego->port, 1); + nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, + TcpConnectTimeout); } if (!nego->TcpConnected) { transport_set_gateway_enabled(nego->transport, TRUE); - nego->TcpConnected = - transport_connect(nego->transport, nego->hostname, nego->port, 15); + nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, + TcpConnectTimeout); } } else { - nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, 15); + nego->TcpConnected = + transport_connect(nego->transport, nego->hostname, nego->port, TcpConnectTimeout); } } @@ -706,6 +710,11 @@ static BOOL nego_read_request_token_or_cookie(rdpNego* nego, wStream* s) if (memcmp(Stream_Pointer(s), "Cookie: mstshash=", 17) != 0) { + if (memcmp(Stream_Pointer(s), "Cookie: msts=", 13) != 0) + { + /* remaining bytes are neither a token nor a cookie */ + return TRUE; + } isToken = TRUE; } else @@ -801,6 +810,14 @@ BOOL nego_read_request(rdpNego* nego, wStream* s) return FALSE; } + /* Skip over optional RDP_NEG_CORRELATION_INFO + * see MS-RDPBCGR 2.2.1.1.2 RDP Correlation Info (RDP_NEG_CORRELATION_INFO) + */ + if (Stream_GetRemainingLength(s) >= 36) + { + Stream_Seek(s, 36); + } + return tpkt_ensure_stream_consumed(s, length); } @@ -941,6 +958,28 @@ BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s) * @param nego * @param s */ +static const char* nego_rdp_neg_rsp_flags_str(UINT32 flags) +{ + static char buffer[1024] = { 0 }; + + _snprintf(buffer, ARRAYSIZE(buffer), "[0x%02" PRIx32 "] ", flags); + if (flags & EXTENDED_CLIENT_DATA_SUPPORTED) + winpr_str_append("EXTENDED_CLIENT_DATA_SUPPORTED", buffer, sizeof(buffer), "|"); + if (flags & DYNVC_GFX_PROTOCOL_SUPPORTED) + winpr_str_append("DYNVC_GFX_PROTOCOL_SUPPORTED", buffer, sizeof(buffer), "|"); + if (flags & RDP_NEGRSP_RESERVED) + winpr_str_append("RDP_NEGRSP_RESERVED", buffer, sizeof(buffer), "|"); + if (flags & RESTRICTED_ADMIN_MODE_SUPPORTED) + winpr_str_append("RESTRICTED_ADMIN_MODE_SUPPORTED", buffer, sizeof(buffer), "|"); + if (flags & REDIRECTED_AUTHENTICATION_MODE_SUPPORTED) + winpr_str_append("REDIRECTED_AUTHENTICATION_MODE_SUPPORTED", buffer, sizeof(buffer), "|"); + if ((flags & + ~(EXTENDED_CLIENT_DATA_SUPPORTED | DYNVC_GFX_PROTOCOL_SUPPORTED | RDP_NEGRSP_RESERVED | + RESTRICTED_ADMIN_MODE_SUPPORTED | REDIRECTED_AUTHENTICATION_MODE_SUPPORTED))) + winpr_str_append("UNKNOWN", buffer, sizeof(buffer), "|"); + + return buffer; +} BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s) { @@ -955,6 +994,7 @@ BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s) } Stream_Read_UINT8(s, nego->flags); + WLog_DBG(TAG, "RDP_NEG_RSP::flags = { %s }", nego_rdp_neg_rsp_flags_str(nego->flags)); Stream_Read_UINT16(s, length); Stream_Read_UINT32(s, nego->SelectedProtocol); nego->state = NEGO_STATE_FINAL; @@ -1112,8 +1152,8 @@ BOOL nego_send_negotiation_response(rdpNego* nego) settings->UseRdpSecurityLayer = FALSE; settings->EncryptionLevel = ENCRYPTION_LEVEL_NONE; } - - if (!settings->RdpServerRsaKey && !settings->RdpKeyFile && !settings->RdpKeyContent) + else if (!settings->RdpServerRsaKey && !settings->RdpKeyFile && + !settings->RdpKeyContent) { WLog_ERR(TAG, "Missing server certificate"); return FALSE; diff --git a/libfreerdp/core/nego.h b/libfreerdp/core/nego.h index f67e4fa95ce2..ce7a47e346bc 100644 --- a/libfreerdp/core/nego.h +++ b/libfreerdp/core/nego.h @@ -81,6 +81,7 @@ enum RDP_NEG_MSG #define DYNVC_GFX_PROTOCOL_SUPPORTED 0x02 #define RDP_NEGRSP_RESERVED 0x04 #define RESTRICTED_ADMIN_MODE_SUPPORTED 0x08 +#define REDIRECTED_AUTHENTICATION_MODE_SUPPORTED 0x10 #define PRECONNECTION_PDU_V1_SIZE 16 #define PRECONNECTION_PDU_V2_MIN_SIZE (PRECONNECTION_PDU_V1_SIZE + 2) diff --git a/libfreerdp/core/nla.c b/libfreerdp/core/nla.c index 2e99b7e9864d..33f366ec1f92 100644 --- a/libfreerdp/core/nla.c +++ b/libfreerdp/core/nla.c @@ -424,8 +424,10 @@ static int nla_client_init(rdpNla* nla) nla->haveContext = FALSE; nla->haveInputBuffer = FALSE; nla->havePubKeyAuth = FALSE; - ZeroMemory(&nla->inputBuffer, sizeof(SecBuffer)); - ZeroMemory(&nla->outputBuffer, sizeof(SecBuffer)); + + sspi_SecBufferFree(&nla->inputBuffer); + sspi_SecBufferFree(&nla->outputBuffer); + ZeroMemory(&nla->ContextSizes, sizeof(SecPkgContext_Sizes)); /* * from tspkg.dll: 0x00000132 @@ -438,6 +440,26 @@ static int nla_client_init(rdpNla* nla) return 1; } +static BOOL nla_alloc_buffer(SecBuffer* buffer, size_t size) +{ + sspi_SecBufferFree(buffer); + buffer->BufferType = SECBUFFER_TOKEN; + return sspi_SecBufferAlloc(buffer, size) != 0; +} + +static BOOL nla_transfer_buffer(SecBuffer* dst, SecBuffer* src) +{ + WINPR_ASSERT(dst); + WINPR_ASSERT(src); + sspi_SecBufferFree(dst); + dst->cbBuffer = src->cbBuffer; + src->cbBuffer = 0; + + dst->pvBuffer = src->pvBuffer; + src->pvBuffer = NULL; + return TRUE; +} + int nla_client_begin(rdpNla* nla) { if (nla_client_init(nla) < 1) @@ -449,11 +471,8 @@ int nla_client_begin(rdpNla* nla) nla->outputBufferDesc.ulVersion = SECBUFFER_VERSION; nla->outputBufferDesc.cBuffers = 1; nla->outputBufferDesc.pBuffers = &nla->outputBuffer; - nla->outputBuffer.BufferType = SECBUFFER_TOKEN; - nla->outputBuffer.cbBuffer = nla->cbMaxToken; - nla->outputBuffer.pvBuffer = malloc(nla->outputBuffer.cbBuffer); - if (!nla->outputBuffer.pvBuffer) + if (!nla_alloc_buffer(&nla->outputBuffer, nla->cbMaxToken)) return -1; nla->status = nla->table->InitializeSecurityContext( @@ -518,8 +537,8 @@ int nla_client_begin(rdpNla* nla) if (nla->outputBuffer.cbBuffer < 1) return -1; - nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; - nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; + if (!nla_transfer_buffer(&nla->negoToken, &nla->outputBuffer)) + return -1; if (!nla_send(nla, "Client: Sending Authentication Token")) { @@ -542,16 +561,15 @@ static int nla_client_recv(rdpNla* nla) nla->inputBufferDesc.cBuffers = 1; nla->inputBufferDesc.pBuffers = &nla->inputBuffer; nla->inputBuffer.BufferType = SECBUFFER_TOKEN; - nla->inputBuffer.pvBuffer = nla->negoToken.pvBuffer; - nla->inputBuffer.cbBuffer = nla->negoToken.cbBuffer; + + if (!nla_transfer_buffer(&nla->inputBuffer, &nla->negoToken)) + return -1; + nla->outputBufferDesc.ulVersion = SECBUFFER_VERSION; nla->outputBufferDesc.cBuffers = 1; nla->outputBufferDesc.pBuffers = &nla->outputBuffer; - nla->outputBuffer.BufferType = SECBUFFER_TOKEN; - nla->outputBuffer.cbBuffer = nla->cbMaxToken; - nla->outputBuffer.pvBuffer = malloc(nla->outputBuffer.cbBuffer); - if (!nla->outputBuffer.pvBuffer) + if (!nla_alloc_buffer(&nla->outputBuffer, nla->cbMaxToken)) return -1; nla->status = nla->table->InitializeSecurityContext( @@ -560,8 +578,8 @@ static int nla_client_recv(rdpNla* nla) &nla->pfContextAttr, &nla->expiration); WLog_VRB(TAG, "InitializeSecurityContext %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - free(nla->inputBuffer.pvBuffer); - nla->inputBuffer.pvBuffer = NULL; + + sspi_SecBufferFree(&nla->inputBuffer); if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) { @@ -607,8 +625,8 @@ static int nla_client_recv(rdpNla* nla) return -1; } - nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; - nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; + if (!nla_transfer_buffer(&nla->negoToken, &nla->outputBuffer)) + return -1; if (!nla_send(nla, "Client: Sending Authentication Token")) { @@ -799,8 +817,10 @@ static int nla_server_init(rdpNla* nla) nla->haveContext = FALSE; nla->haveInputBuffer = FALSE; nla->havePubKeyAuth = FALSE; - ZeroMemory(&nla->inputBuffer, sizeof(SecBuffer)); - ZeroMemory(&nla->outputBuffer, sizeof(SecBuffer)); + + sspi_SecBufferFree(&nla->inputBuffer); + sspi_SecBufferFree(&nla->outputBuffer); + ZeroMemory(&nla->inputBufferDesc, sizeof(SecBufferDesc)); ZeroMemory(&nla->outputBufferDesc, sizeof(SecBufferDesc)); ZeroMemory(&nla->ContextSizes, sizeof(SecPkgContext_Sizes)); @@ -843,10 +863,10 @@ static int nla_server_authenticate(rdpNla* nla) if (nla_recv(nla, "Receiving Authentication Token") < 0) return -1; - nla->inputBuffer.pvBuffer = nla->negoToken.pvBuffer; - nla->inputBuffer.cbBuffer = nla->negoToken.cbBuffer; + if (!nla_transfer_buffer(&nla->inputBuffer, &nla->negoToken)) + return -1; - if (nla->negoToken.cbBuffer < 1) + if (nla->inputBuffer.cbBuffer < 1) { WLog_ERR(TAG, "CredSSP: invalid negoToken!"); return -1; @@ -855,11 +875,8 @@ static int nla_server_authenticate(rdpNla* nla) nla->outputBufferDesc.ulVersion = SECBUFFER_VERSION; nla->outputBufferDesc.cBuffers = 1; nla->outputBufferDesc.pBuffers = &nla->outputBuffer; - nla->outputBuffer.BufferType = SECBUFFER_TOKEN; - nla->outputBuffer.cbBuffer = nla->cbMaxToken; - nla->outputBuffer.pvBuffer = malloc(nla->outputBuffer.cbBuffer); - if (!nla->outputBuffer.pvBuffer) + if (!nla_alloc_buffer(&nla->outputBuffer, nla->cbMaxToken)) return -1; nla->status = nla->table->AcceptSecurityContext( @@ -868,8 +885,9 @@ static int nla_server_authenticate(rdpNla* nla) &nla->pfContextAttr, &nla->expiration); WLog_VRB(TAG, "AcceptSecurityContext status %s [0x%08" PRIX32 "]", GetSecurityStatusString(nla->status), nla->status); - nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; - nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; + + if (!nla_transfer_buffer(&nla->negoToken, &nla->outputBuffer)) + return -1; if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) { @@ -2261,6 +2279,8 @@ void nla_buffer_free(rdpNla* nla) sspi_SecBufferFree(&nla->negoToken); sspi_SecBufferFree(&nla->pubKeyAuth); sspi_SecBufferFree(&nla->authInfo); + sspi_SecBufferFree(&nla->inputBuffer); + sspi_SecBufferFree(&nla->outputBuffer); } LPTSTR nla_make_spn(const char* ServiceClass, const char* hostname) diff --git a/libfreerdp/core/orders.c b/libfreerdp/core/orders.c index 0e14bbe6aef4..a616a22898f5 100644 --- a/libfreerdp/core/orders.c +++ b/libfreerdp/core/orders.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -796,6 +797,7 @@ static INLINE BOOL update_write_4byte_unsigned(wStream* s, UINT32 value) static INLINE BOOL update_read_delta(wStream* s, INT32* value) { BYTE byte; + UINT32 uvalue = 0; if (Stream_GetRemainingLength(s) < 1) { @@ -806,9 +808,9 @@ static INLINE BOOL update_read_delta(wStream* s, INT32* value) Stream_Read_UINT8(s, byte); if (byte & 0x40) - *value = (byte | ~0x3F); + uvalue = (byte | ~0x3F); else - *value = (byte & 0x3F); + uvalue = (byte & 0x3F); if (byte & 0x80) { @@ -819,8 +821,9 @@ static INLINE BOOL update_read_delta(wStream* s, INT32* value) } Stream_Read_UINT8(s, byte); - *value = (*value << 8) | byte; + uvalue = (uvalue << 8) | byte; } + *value = (INT32)uvalue; return TRUE; } @@ -1373,12 +1376,13 @@ static BOOL update_read_draw_nine_grid_order(wStream* s, const ORDER_INFO* order static BOOL update_read_multi_dstblt_order(wStream* s, const ORDER_INFO* orderInfo, MULTI_DSTBLT_ORDER* multi_dstblt) { + UINT32 numRectangles = multi_dstblt->numRectangles; if (!read_order_field_coord(orderInfo, s, 1, &multi_dstblt->nLeftRect, FALSE) || !read_order_field_coord(orderInfo, s, 2, &multi_dstblt->nTopRect, FALSE) || !read_order_field_coord(orderInfo, s, 3, &multi_dstblt->nWidth, FALSE) || !read_order_field_coord(orderInfo, s, 4, &multi_dstblt->nHeight, FALSE) || !read_order_field_byte(orderInfo, s, 5, &multi_dstblt->bRop, TRUE) || - !read_order_field_byte(orderInfo, s, 6, &multi_dstblt->numRectangles, TRUE)) + !read_order_field_byte(orderInfo, s, 6, &numRectangles, TRUE)) return FALSE; if ((orderInfo->fieldFlags & ORDER_FIELD_07) != 0) @@ -1386,12 +1390,21 @@ static BOOL update_read_multi_dstblt_order(wStream* s, const ORDER_INFO* orderIn if (Stream_GetRemainingLength(s) < 2) return FALSE; + multi_dstblt->numRectangles = numRectangles; Stream_Read_UINT16(s, multi_dstblt->cbData); return update_read_delta_rects(s, multi_dstblt->rectangles, &multi_dstblt->numRectangles); } - + if (numRectangles > multi_dstblt->numRectangles) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles, + multi_dstblt->numRectangles); + return FALSE; + } + multi_dstblt->numRectangles = numRectangles; return TRUE; } + static BOOL update_read_multi_patblt_order(wStream* s, const ORDER_INFO* orderInfo, MULTI_PATBLT_ORDER* multi_patblt) { @@ -1407,7 +1420,8 @@ static BOOL update_read_multi_patblt_order(wStream* s, const ORDER_INFO* orderIn if (!update_read_brush(s, &multi_patblt->brush, orderInfo->fieldFlags >> 7)) return FALSE; - if (!read_order_field_byte(orderInfo, s, 13, &multi_patblt->numRectangles, TRUE)) + UINT32 numRectangles = multi_patblt->numRectangles; + if (!read_order_field_byte(orderInfo, s, 13, &numRectangles, TRUE)) return FALSE; if ((orderInfo->fieldFlags & ORDER_FIELD_14) != 0) @@ -1415,17 +1429,31 @@ static BOOL update_read_multi_patblt_order(wStream* s, const ORDER_INFO* orderIn if (Stream_GetRemainingLength(s) < 2) return FALSE; + multi_patblt->numRectangles = numRectangles; Stream_Read_UINT16(s, multi_patblt->cbData); if (!update_read_delta_rects(s, multi_patblt->rectangles, &multi_patblt->numRectangles)) return FALSE; } + if (numRectangles > multi_patblt->numRectangles) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles, + multi_patblt->numRectangles); + return FALSE; + } + multi_patblt->numRectangles = numRectangles; + return TRUE; } static BOOL update_read_multi_scrblt_order(wStream* s, const ORDER_INFO* orderInfo, MULTI_SCRBLT_ORDER* multi_scrblt) { + WINPR_ASSERT(orderInfo); + WINPR_ASSERT(multi_scrblt); + + UINT32 numRectangles = multi_scrblt->numRectangles; if (!read_order_field_coord(orderInfo, s, 1, &multi_scrblt->nLeftRect, FALSE) || !read_order_field_coord(orderInfo, s, 2, &multi_scrblt->nTopRect, FALSE) || !read_order_field_coord(orderInfo, s, 3, &multi_scrblt->nWidth, FALSE) || @@ -1433,7 +1461,7 @@ static BOOL update_read_multi_scrblt_order(wStream* s, const ORDER_INFO* orderIn !read_order_field_byte(orderInfo, s, 5, &multi_scrblt->bRop, TRUE) || !read_order_field_coord(orderInfo, s, 6, &multi_scrblt->nXSrc, FALSE) || !read_order_field_coord(orderInfo, s, 7, &multi_scrblt->nYSrc, FALSE) || - !read_order_field_byte(orderInfo, s, 8, &multi_scrblt->numRectangles, TRUE)) + !read_order_field_byte(orderInfo, s, 8, &numRectangles, TRUE)) return FALSE; if ((orderInfo->fieldFlags & ORDER_FIELD_09) != 0) @@ -1441,10 +1469,20 @@ static BOOL update_read_multi_scrblt_order(wStream* s, const ORDER_INFO* orderIn if (Stream_GetRemainingLength(s) < 2) return FALSE; + multi_scrblt->numRectangles = numRectangles; Stream_Read_UINT16(s, multi_scrblt->cbData); return update_read_delta_rects(s, multi_scrblt->rectangles, &multi_scrblt->numRectangles); } + if (numRectangles > multi_scrblt->numRectangles) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles, + multi_scrblt->numRectangles); + return FALSE; + } + multi_scrblt->numRectangles = numRectangles; + return TRUE; } static BOOL update_read_multi_opaque_rect_order(wStream* s, const ORDER_INFO* orderInfo, @@ -1484,7 +1522,8 @@ static BOOL update_read_multi_opaque_rect_order(wStream* s, const ORDER_INFO* or multi_opaque_rect->color = (multi_opaque_rect->color & 0x0000FFFF) | ((UINT32)byte << 16); } - if (!read_order_field_byte(orderInfo, s, 8, &multi_opaque_rect->numRectangles, TRUE)) + UINT32 numRectangles = multi_opaque_rect->numRectangles; + if (!read_order_field_byte(orderInfo, s, 8, &numRectangles, TRUE)) return FALSE; if ((orderInfo->fieldFlags & ORDER_FIELD_09) != 0) @@ -1492,22 +1531,32 @@ static BOOL update_read_multi_opaque_rect_order(wStream* s, const ORDER_INFO* or if (Stream_GetRemainingLength(s) < 2) return FALSE; + multi_opaque_rect->numRectangles = numRectangles; Stream_Read_UINT16(s, multi_opaque_rect->cbData); return update_read_delta_rects(s, multi_opaque_rect->rectangles, &multi_opaque_rect->numRectangles); } + if (numRectangles > multi_opaque_rect->numRectangles) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s numRectangles %" PRIu32 " > %" PRIu32, orderName, numRectangles, + multi_opaque_rect->numRectangles); + return FALSE; + } + multi_opaque_rect->numRectangles = numRectangles; return TRUE; } static BOOL update_read_multi_draw_nine_grid_order(wStream* s, const ORDER_INFO* orderInfo, MULTI_DRAW_NINE_GRID_ORDER* multi_draw_nine_grid) { + UINT32 nDeltaEntries = multi_draw_nine_grid->nDeltaEntries; if (!read_order_field_coord(orderInfo, s, 1, &multi_draw_nine_grid->srcLeft, FALSE) || !read_order_field_coord(orderInfo, s, 2, &multi_draw_nine_grid->srcTop, FALSE) || !read_order_field_coord(orderInfo, s, 3, &multi_draw_nine_grid->srcRight, FALSE) || !read_order_field_coord(orderInfo, s, 4, &multi_draw_nine_grid->srcBottom, FALSE) || !read_order_field_uint16(orderInfo, s, 5, &multi_draw_nine_grid->bitmapId, TRUE) || - !read_order_field_byte(orderInfo, s, 6, &multi_draw_nine_grid->nDeltaEntries, TRUE)) + !read_order_field_byte(orderInfo, s, 6, &nDeltaEntries, TRUE)) return FALSE; if ((orderInfo->fieldFlags & ORDER_FIELD_07) != 0) @@ -1515,11 +1564,21 @@ static BOOL update_read_multi_draw_nine_grid_order(wStream* s, const ORDER_INFO* if (Stream_GetRemainingLength(s) < 2) return FALSE; + multi_draw_nine_grid->nDeltaEntries = nDeltaEntries; Stream_Read_UINT16(s, multi_draw_nine_grid->cbData); return update_read_delta_rects(s, multi_draw_nine_grid->rectangles, &multi_draw_nine_grid->nDeltaEntries); } + if (nDeltaEntries > multi_draw_nine_grid->nDeltaEntries) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s nDeltaEntries %" PRIu32 " > %" PRIu32, orderName, nDeltaEntries, + multi_draw_nine_grid->nDeltaEntries); + return FALSE; + } + multi_draw_nine_grid->nDeltaEntries = nDeltaEntries; + return TRUE; } static BOOL update_read_line_to_order(wStream* s, const ORDER_INFO* orderInfo, @@ -1610,6 +1669,14 @@ static BOOL update_read_polyline_order(wStream* s, const ORDER_INFO* orderInfo, return update_read_delta_points(s, polyline->points, polyline->numDeltaEntries, polyline->xStart, polyline->yStart); } + if (new_num > polyline->numDeltaEntries) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s numDeltaEntries %" PRIu32 " > %" PRIu32, orderName, new_num, + polyline->numDeltaEntries); + return FALSE; + } + polyline->numDeltaEntries = new_num; return TRUE; } @@ -1881,6 +1948,13 @@ static BOOL update_read_fast_glyph_order(wStream* s, const ORDER_INFO* orderInfo !update_read_2byte_unsigned(&sub, &glyph->cy)) return FALSE; + if ((glyph->cx == 0) || (glyph->cy == 0)) + { + WLog_ERR(TAG, "GLYPH_DATA_V2::cx=%" PRIu32 ", GLYPH_DATA_V2::cy=%" PRIu32, + glyph->cx, glyph->cy); + return FALSE; + } + glyph->cb = Stream_GetRemainingLength(&sub); if (glyph->cb > 0) { @@ -1935,6 +2009,13 @@ static BOOL update_read_polygon_sc_order(wStream* s, const ORDER_INFO* orderInfo return update_read_delta_points(s, polygon_sc->points, polygon_sc->numPoints, polygon_sc->xStart, polygon_sc->yStart); } + if (num > polygon_sc->numPoints) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s numPoints %" PRIu32 " > %" PRIu32, orderName, num, polygon_sc->numPoints); + return FALSE; + } + polygon_sc->numPoints = num; return TRUE; } @@ -1980,6 +2061,14 @@ static BOOL update_read_polygon_cb_order(wStream* s, const ORDER_INFO* orderInfo return FALSE; } + if (num > polygon_cb->numPoints) + { + const char* orderName = __func__; + WLog_ERR(TAG, "%s numPoints %" PRIu32 " > %" PRIu32, orderName, num, polygon_cb->numPoints); + return FALSE; + } + polygon_cb->numPoints = num; + polygon_cb->backMode = (polygon_cb->bRop2 & 0x80) ? BACKMODE_TRANSPARENT : BACKMODE_OPAQUE; polygon_cb->bRop2 = (polygon_cb->bRop2 & 0x1F); return TRUE; @@ -2055,6 +2144,8 @@ static CACHE_BITMAP_ORDER* update_read_cache_bitmap_order(rdpUpdate* update, wSt goto fail; Stream_Read(s, bitmapComprHdr, 8); /* bitmapComprHdr (8 bytes) */ + if (cache_bitmap->bitmapLength < 8) + goto fail; cache_bitmap->bitmapLength -= 8; } } @@ -2867,6 +2958,13 @@ update_read_create_offscreen_bitmap_order(wStream* s, Stream_Read_UINT16(s, create_offscreen_bitmap->cy); /* cy (2 bytes) */ deleteList = &(create_offscreen_bitmap->deleteList); + if ((create_offscreen_bitmap->cx == 0) || (create_offscreen_bitmap->cy == 0)) + { + WLog_ERR(TAG, "Invalid OFFSCREEN_DELETE_LIST: cx=%" PRIu16 ", cy=%" PRIu16, + create_offscreen_bitmap->cx, create_offscreen_bitmap->cy); + return FALSE; + } + if (deleteListPresent) { UINT32 i; diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c index ce8628f2353c..f629705d5016 100644 --- a/libfreerdp/core/peer.c +++ b/libfreerdp/core/peer.c @@ -203,7 +203,6 @@ static BOOL freerdp_peer_initialize(freerdp_peer* client) settings->ServerMode = TRUE; settings->FrameAcknowledge = 0; settings->LocalConnection = client->local; - rdp->state = CONNECTION_STATE_INITIAL; if (settings->RdpKeyFile) { @@ -360,7 +359,7 @@ static int peer_recv_tpkt_pdu(freerdp_peer* client, wStream* s) } rdp->inPackets++; - if (freerdp_shall_disconnect(rdp->instance)) + if (freerdp_shall_disconnect_context(rdp->context)) return 0; if (rdp->settings->UseRdpSecurityLayer) @@ -475,13 +474,13 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) freerdp_peer* client = (freerdp_peer*)extra; rdpRdp* rdp = client->context->rdp; - switch (rdp->state) + switch (rdp_get_state(rdp)) { case CONNECTION_STATE_INITIAL: if (!rdp_server_accept_nego(rdp, s)) { WLog_ERR(TAG, "%s: %s - rdp_server_accept_nego() fail", __FUNCTION__, - rdp_server_connection_state_string(rdp->state)); + rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -510,7 +509,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_server_accept_mcs_connect_initial() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -522,7 +521,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_server_accept_mcs_erect_domain_request() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -534,7 +533,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_server_accept_mcs_attach_user_request() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -546,7 +545,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_server_accept_mcs_channel_join_request() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -560,7 +559,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_server_establish_keys() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } } @@ -578,7 +577,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_recv_client_info() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -606,7 +605,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - callback internal " "error, aborting", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; case LICENSE_CB_ABORT: @@ -641,7 +640,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_send_demand_active() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -654,7 +653,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "peer_recv_pdu() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } } @@ -670,7 +669,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "peer_recv_pdu() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); return -1; } } @@ -681,7 +680,7 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (peer_recv_pdu(client, s) < 0) { WLog_ERR(TAG, "%s: %s - peer_recv_pdu() fail", __FUNCTION__, - rdp_server_connection_state_string(rdp->state)); + rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -691,15 +690,14 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (peer_recv_pdu(client, s) < 0) { WLog_ERR(TAG, "%s: %s - peer_recv_pdu() fail", __FUNCTION__, - rdp_server_connection_state_string(rdp->state)); + rdp_state_string(rdp_get_state(rdp))); return -1; } break; default: - WLog_ERR(TAG, "%s state %d", rdp_server_connection_state_string(rdp->state), - rdp->state); + WLog_ERR(TAG, "%s state %d", rdp_state_string(rdp_get_state(rdp)), rdp_get_state(rdp)); return -1; } @@ -745,6 +743,245 @@ static BOOL freerdp_peer_send_channel_data(freerdp_peer* client, UINT16 channelI return rdp_send_channel_data(client->context->rdp, channelId, data, size); } +static BOOL freerdp_peer_send_server_redirection_pdu( + freerdp_peer* peer, UINT32 sessionId, const char* targetNetAddress, const char* routingToken, + const char* userName, const char* domain, const char* password, const char* targetFQDN, + const char* targetNetBiosName, DWORD tsvUrlLength, const BYTE* tsvUrl, + UINT32 targetNetAddressesCount, const char** targetNetAddresses) +{ + wStream* s = rdp_send_stream_pdu_init(peer->context->rdp); + UINT16 length; + UINT32 redirFlags; + + UINT32 targetNetAddressLength; + UINT32 loadBalanceInfoLength; + UINT32 userNameLength; + UINT32 domainLength; + UINT32 passwordLength; + UINT32 targetFQDNLength; + UINT32 targetNetBiosNameLength; + UINT32 targetNetAddressesLength; + UINT32* targetNetAddressesWLength; + + LPWSTR targetNetAddressW = NULL; + LPWSTR userNameW = NULL; + LPWSTR domainW = NULL; + LPWSTR passwordW = NULL; + LPWSTR targetFQDNW = NULL; + LPWSTR targetNetBiosNameW = NULL; + LPWSTR* targetNetAddressesW = NULL; + + length = 12; /* Flags (2) + length (2) + sessionId (4) + redirection flags (4) */ + redirFlags = 0; + + if (targetNetAddress) + { + redirFlags |= LB_TARGET_NET_ADDRESS; + + ConvertToUnicode(CP_UTF8, 0, (LPCSTR)targetNetAddress, -1, &targetNetAddressW, 0); + targetNetAddressLength = (strlen(targetNetAddress) + 1) * sizeof(WCHAR); + + length += 4 + targetNetAddressLength; + } + + if (routingToken) + { + redirFlags |= LB_LOAD_BALANCE_INFO; + loadBalanceInfoLength = + 13 + strlen(routingToken) + 2; /* Add routing token prefix and suffix */ + length += 4 + loadBalanceInfoLength; + } + + if (userName) + { + redirFlags |= LB_USERNAME; + + ConvertToUnicode(CP_UTF8, 0, (LPCSTR)userName, -1, &userNameW, 0); + userNameLength = (strlen(userName) + 1) * sizeof(WCHAR); + + length += 4 + userNameLength; + } + + if (domain) + { + redirFlags |= LB_DOMAIN; + + ConvertToUnicode(CP_UTF8, 0, (LPCSTR)domain, -1, &domainW, 0); + domainLength = (strlen(domain) + 1) * sizeof(WCHAR); + + length += 4 + domainLength; + } + + if (password) + { + redirFlags |= LB_PASSWORD; + + ConvertToUnicode(CP_UTF8, 0, (LPCSTR)password, -1, &passwordW, 0); + passwordLength = (strlen(password) + 1) * sizeof(WCHAR); + + length += 4 + passwordLength; + } + + if (targetFQDN) + { + redirFlags |= LB_TARGET_FQDN; + + ConvertToUnicode(CP_UTF8, 0, (LPCSTR)targetFQDN, -1, &targetFQDNW, 0); + targetFQDNLength = (strlen(targetFQDN) + 1) * sizeof(WCHAR); + + length += 4 + targetFQDNLength; + } + + if (targetNetBiosName) + { + redirFlags |= LB_TARGET_NETBIOS_NAME; + + ConvertToUnicode(CP_UTF8, 0, (LPCSTR)targetNetBiosName, -1, &targetNetBiosNameW, 0); + targetNetBiosNameLength = (strlen(targetNetBiosName) + 1) * sizeof(WCHAR); + + length += 4 + targetNetBiosNameLength; + } + + if (tsvUrl) + { + redirFlags |= LB_CLIENT_TSV_URL; + length += 4 + tsvUrlLength; + } + + if (targetNetAddresses) + { + UINT32 i; + + redirFlags |= LB_TARGET_NET_ADDRESSES; + + targetNetAddressesLength = 0; + targetNetAddressesW = calloc(targetNetAddressesCount, sizeof(LPWSTR)); + targetNetAddressesWLength = calloc(targetNetAddressesCount, sizeof(UINT32)); + for (i = 0; i < targetNetAddressesCount; i++) + { + ConvertToUnicode(CP_UTF8, 0, (LPCSTR)targetNetAddresses[i], -1, &targetNetAddressesW[i], + 0); + targetNetAddressesWLength[i] = (strlen(targetNetAddresses[i]) + 1) * sizeof(WCHAR); + targetNetAddressesLength += 4 + targetNetAddressesWLength[i]; + } + + length += 4 + 4 + targetNetAddressesLength; + } + + Stream_Write_UINT16(s, 0); + Stream_Write_UINT16(s, SEC_REDIRECTION_PKT); + Stream_Write_UINT16(s, length); + + if (!Stream_EnsureRemainingCapacity(s, length)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + goto fail; + } + + if (sessionId) + Stream_Write_UINT32(s, sessionId); + else + Stream_Write_UINT32(s, 0); + + Stream_Write_UINT32(s, redirFlags); + + if (redirFlags & LB_TARGET_NET_ADDRESS) + { + Stream_Write_UINT32(s, targetNetAddressLength); + Stream_Write(s, targetNetAddressW, targetNetAddressLength); + free(targetNetAddressW); + } + + if (redirFlags & LB_LOAD_BALANCE_INFO) + { + Stream_Write_UINT32(s, loadBalanceInfoLength); + Stream_Write(s, "Cookie: msts=", 13); + Stream_Write(s, routingToken, strlen(routingToken)); + Stream_Write_UINT8(s, 0x0d); + Stream_Write_UINT8(s, 0x0a); + } + + if (redirFlags & LB_USERNAME) + { + Stream_Write_UINT32(s, userNameLength); + Stream_Write(s, userNameW, userNameLength); + free(userNameW); + } + + if (redirFlags & LB_DOMAIN) + { + Stream_Write_UINT32(s, domainLength); + Stream_Write(s, domainW, domainLength); + free(domainW); + } + + if (redirFlags & LB_PASSWORD) + { + Stream_Write_UINT32(s, passwordLength); + Stream_Write(s, passwordW, passwordLength); + free(passwordW); + } + + if (redirFlags & LB_TARGET_FQDN) + { + Stream_Write_UINT32(s, targetFQDNLength); + Stream_Write(s, targetFQDNW, targetFQDNLength); + free(targetFQDNW); + } + + if (redirFlags & LB_TARGET_NETBIOS_NAME) + { + Stream_Write_UINT32(s, targetNetBiosNameLength); + Stream_Write(s, targetNetBiosNameW, targetNetBiosNameLength); + free(targetNetBiosNameW); + } + + if (redirFlags & LB_CLIENT_TSV_URL) + { + Stream_Write_UINT32(s, tsvUrlLength); + Stream_Write(s, tsvUrl, tsvUrlLength); + } + + if (redirFlags & LB_TARGET_NET_ADDRESSES) + { + UINT32 i; + Stream_Write_UINT32(s, targetNetAddressesLength); + Stream_Write_UINT32(s, targetNetAddressesCount); + for (i = 0; i < targetNetAddressesCount; i++) + { + Stream_Write_UINT32(s, targetNetAddressesWLength[i]); + Stream_Write(s, targetNetAddressesW[i], targetNetAddressesWLength[i]); + free(targetNetAddressesW[i]); + } + free(targetNetAddressesW); + free(targetNetAddressesWLength); + } + + Stream_Write_UINT8(s, 0); + rdp_send_pdu(peer->context->rdp, s, PDU_TYPE_SERVER_REDIRECTION, 0); + + return TRUE; + +fail: + free(targetNetAddressW); + free(userNameW); + free(domainW); + free(passwordW); + free(targetFQDNW); + free(targetNetBiosNameW); + free(targetNetAddressesWLength); + + if (targetNetAddressesCount > 0) + { + UINT32 i; + for (i = 0; i < targetNetAddressesCount; i++) + free(targetNetAddressesW[i]); + free(targetNetAddressesW); + } + + return FALSE; +} + static BOOL freerdp_peer_is_write_blocked(freerdp_peer* peer) { rdpTransport* transport = peer->context->rdp->transport; @@ -866,6 +1103,94 @@ void freerdp_peer_context_free(freerdp_peer* client) } } +static const char* os_major_type_to_string(UINT16 osMajorType) +{ + switch (osMajorType) + { + case OSMAJORTYPE_UNSPECIFIED: + return "Unspecified platform"; + case OSMAJORTYPE_WINDOWS: + return "Windows platform"; + case OSMAJORTYPE_OS2: + return "OS/2 platform"; + case OSMAJORTYPE_MACINTOSH: + return "Macintosh platform"; + case OSMAJORTYPE_UNIX: + return "UNIX platform"; + case OSMAJORTYPE_IOS: + return "iOS platform"; + case OSMAJORTYPE_OSX: + return "OS X platform"; + case OSMAJORTYPE_ANDROID: + return "Android platform"; + case OSMAJORTYPE_CHROME_OS: + return "Chrome OS platform"; + } + + return "Unknown platform"; +} + +const char* freerdp_peer_os_major_type_string(freerdp_peer* client) +{ + rdpContext* context; + UINT16 osMajorType; + + WINPR_ASSERT(client); + + context = client->context; + WINPR_ASSERT(context); + WINPR_ASSERT(context->settings); + + osMajorType = freerdp_settings_get_uint32(context->settings, FreeRDP_OsMajorType); + + return os_major_type_to_string(osMajorType); +} + +static const char* os_minor_type_to_string(UINT16 osMinorType) +{ + switch (osMinorType) + { + case OSMINORTYPE_UNSPECIFIED: + return "Unspecified version"; + case OSMINORTYPE_WINDOWS_31X: + return "Windows 3.1x"; + case OSMINORTYPE_WINDOWS_95: + return "Windows 95"; + case OSMINORTYPE_WINDOWS_NT: + return "Windows NT"; + case OSMINORTYPE_OS2_V21: + return "OS/2 2.1"; + case OSMINORTYPE_POWER_PC: + return "PowerPC"; + case OSMINORTYPE_MACINTOSH: + return "Macintosh"; + case OSMINORTYPE_NATIVE_XSERVER: + return "Native X Server"; + case OSMINORTYPE_PSEUDO_XSERVER: + return "Pseudo X Server"; + case OSMINORTYPE_WINDOWS_RT: + return "Windows RT"; + } + + return "Unknown version"; +} + +const char* freerdp_peer_os_minor_type_string(freerdp_peer* client) +{ + rdpContext* context; + UINT16 osMinorType; + + WINPR_ASSERT(client); + + context = client->context; + WINPR_ASSERT(context); + WINPR_ASSERT(context->settings); + + osMinorType = freerdp_settings_get_uint32(context->settings, FreeRDP_OsMinorType); + + return os_minor_type_to_string(osMinorType); +} + freerdp_peer* freerdp_peer_new(int sockfd) { UINT32 option_value; @@ -892,6 +1217,7 @@ freerdp_peer* freerdp_peer_new(int sockfd) client->Close = freerdp_peer_close; client->Disconnect = freerdp_peer_disconnect; client->SendChannelData = freerdp_peer_send_channel_data; + client->SendServerRedirection = freerdp_peer_send_server_redirection_pdu; client->IsWriteBlocked = freerdp_peer_is_write_blocked; client->DrainOutputBuffer = freerdp_peer_drain_output_buffer; client->HasMoreToRead = freerdp_peer_has_more_to_read; diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 7da499e96f9b..33e7a54668fa 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -27,6 +27,7 @@ #include "rdp.h" #include "info.h" +#include "utils.h" #include "redirection.h" #include @@ -414,7 +415,7 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId) { if (code == X224_TPDU_DISCONNECT_REQUEST) { - freerdp_abort_connect(rdp->instance); + utils_abort_connect(rdp->context); return TRUE; } @@ -449,10 +450,10 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId) if (!mcs_recv_disconnect_provider_ultimatum(rdp->mcs, s, &reason)) return FALSE; - if (!rdp->instance) + if (!rdp->context) return FALSE; - context = rdp->instance->context; + context = rdp->context; context->disconnectUltimatum = reason; if (rdp->errorInfo == ERRINFO_SUCCESS) @@ -471,7 +472,7 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId) } WLog_DBG(TAG, "DisconnectProviderUltimatum: reason: %d", reason); - freerdp_abort_connect(rdp->instance); + utils_abort_connect(context); EventArgsInit(&e, "freerdp"); e.code = 0; PubSub_OnTerminate(context->pubSub, context, &e); @@ -783,7 +784,7 @@ static BOOL rdp_recv_server_set_keyboard_indicators_pdu(rdpRdp* rdp, wStream* s) { UINT16 unitId; UINT16 ledFlags; - rdpContext* context = rdp->instance->context; + rdpContext* context = rdp->context; if (Stream_GetRemainingLength(s) < 4) return FALSE; @@ -1318,7 +1319,7 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s) return -1; } - if (freerdp_shall_disconnect(rdp->instance)) + if (freerdp_shall_disconnect_context(rdp->context)) return 0; if (rdp->autodetect->bandwidthMeasureStarted) @@ -1434,7 +1435,8 @@ static int rdp_recv_tpkt_pdu(rdpRdp* rdp, wStream* s) { rdp->inPackets++; - if (!freerdp_channel_process(rdp->instance, s, channelId, length)) + WINPR_ASSERT(rdp->context); + if (!freerdp_channel_process(rdp->context->instance, s, channelId, length)) return -1; } @@ -1501,13 +1503,14 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) * enters the active state, an auto-detect PDU can be received * on the MCS message channel. */ - if ((rdp->state > CONNECTION_STATE_MCS_CHANNEL_JOIN) && (rdp->state < CONNECTION_STATE_ACTIVE)) + if ((rdp_get_state(rdp) > CONNECTION_STATE_MCS_CHANNEL_JOIN) && + (rdp_get_state(rdp) < CONNECTION_STATE_ACTIVE)) { if (rdp_client_connect_auto_detect(rdp, s)) return 0; } - switch (rdp->state) + switch (rdp_get_state(rdp)) { case CONNECTION_STATE_NLA: if (nla_get_state(rdp->nla) < NLA_STATE_AUTH_INFO) @@ -1515,7 +1518,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (nla_recv_pdu(rdp->nla, s) < 1) { WLog_ERR(TAG, "%s: %s - nla_recv_pdu() fail", __FUNCTION__, - rdp_server_connection_state_string(rdp->state)); + rdp_state_string(rdp_get_state(rdp))); return -1; } } @@ -1526,7 +1529,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (nego_get_state(rdp->nego) != NEGO_STATE_FINAL) { WLog_ERR(TAG, "%s: %s - nego_recv() fail", __FUNCTION__, - rdp_server_connection_state_string(rdp->state)); + rdp_state_string(rdp_get_state(rdp))); return -1; } @@ -1566,7 +1569,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (!mcs_client_begin(rdp->mcs)) { WLog_ERR(TAG, "%s: %s - mcs_client_begin() fail", __FUNCTION__, - rdp_server_connection_state_string(rdp->state)); + rdp_state_string(rdp_get_state(rdp))); return -1; } } @@ -1617,7 +1620,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_ERR(TAG, "%s: %s - " "rdp_client_connect_mcs_channel_join_confirm() fail", - __FUNCTION__, rdp_server_connection_state_string(rdp->state)); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp))); status = -1; } @@ -1628,7 +1631,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (status < 0) WLog_DBG(TAG, "%s: %s - rdp_client_connect_license() - %i", __FUNCTION__, - rdp_server_connection_state_string(rdp->state), status); + rdp_state_string(rdp_get_state(rdp)), status); break; @@ -1639,7 +1642,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) WLog_DBG(TAG, "%s: %s - " "rdp_client_connect_demand_active() - %i", - __FUNCTION__, rdp_server_connection_state_string(rdp->state), status); + __FUNCTION__, rdp_state_string(rdp_get_state(rdp)), status); break; @@ -1654,7 +1657,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (status < 0) WLog_DBG(TAG, "%s: %s - rdp_recv_pdu() - %i", __FUNCTION__, - rdp_server_connection_state_string(rdp->state), status); + rdp_state_string(rdp_get_state(rdp)), status); break; @@ -1663,13 +1666,13 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) if (status < 0) WLog_DBG(TAG, "%s: %s - rdp_recv_pdu() - %i", __FUNCTION__, - rdp_server_connection_state_string(rdp->state), status); + rdp_state_string(rdp_get_state(rdp)), status); break; default: - WLog_ERR(TAG, "%s: %s state %d", __FUNCTION__, - rdp_server_connection_state_string(rdp->state), rdp->state); + WLog_ERR(TAG, "%s: %s state %d", __FUNCTION__, rdp_state_string(rdp_get_state(rdp)), + rdp_get_state(rdp)); status = -1; break; } @@ -1768,7 +1771,6 @@ rdpRdp* rdp_new(rdpContext* context) InitializeCriticalSection(&rdp->critical); rdp->context = context; - rdp->instance = context->instance; flags = 0; if (context->ServerMode) @@ -1872,17 +1874,8 @@ void rdp_reset(rdpRdp* rdp) settings = rdp->settings; bulk_reset(rdp->bulk); - if (rdp->rc4_decrypt_key) - { - winpr_RC4_Free(rdp->rc4_decrypt_key); - rdp->rc4_decrypt_key = NULL; - } - - if (rdp->rc4_encrypt_key) - { - winpr_RC4_Free(rdp->rc4_encrypt_key); - rdp->rc4_encrypt_key = NULL; - } + rdp_free_rc4_decrypt_keys(rdp); + rdp_free_rc4_encrypt_keys(rdp); if (rdp->fips_encrypt) { @@ -1941,8 +1934,8 @@ void rdp_free(rdpRdp* rdp) if (rdp) { DeleteCriticalSection(&rdp->critical); - winpr_RC4_Free(rdp->rc4_decrypt_key); - winpr_RC4_Free(rdp->rc4_encrypt_key); + rdp_free_rc4_decrypt_keys(rdp); + rdp_free_rc4_encrypt_keys(rdp); winpr_Cipher_Free(rdp->fips_encrypt); winpr_Cipher_Free(rdp->fips_decrypt); freerdp_settings_free(rdp->settings); @@ -1962,3 +1955,37 @@ void rdp_free(rdpRdp* rdp) free(rdp); } } + +BOOL rdp_reset_rc4_encrypt_keys(rdpRdp* rdp) +{ + WINPR_ASSERT(rdp); + rdp_free_rc4_encrypt_keys(rdp); + rdp->rc4_encrypt_key = winpr_RC4_New(rdp->encrypt_key, rdp->rc4_key_len); + + rdp->encrypt_use_count = 0; + return rdp->rc4_encrypt_key != NULL; +} + +void rdp_free_rc4_encrypt_keys(rdpRdp* rdp) +{ + WINPR_ASSERT(rdp); + winpr_RC4_Free(rdp->rc4_encrypt_key); + rdp->rc4_encrypt_key = NULL; +} + +void rdp_free_rc4_decrypt_keys(rdpRdp* rdp) +{ + WINPR_ASSERT(rdp); + winpr_RC4_Free(rdp->rc4_decrypt_key); + rdp->rc4_decrypt_key = NULL; +} + +BOOL rdp_reset_rc4_decrypt_keys(rdpRdp* rdp) +{ + WINPR_ASSERT(rdp); + rdp_free_rc4_decrypt_keys(rdp); + rdp->rc4_decrypt_key = winpr_RC4_New(rdp->decrypt_key, rdp->rc4_key_len); + + rdp->decrypt_use_count = 0; + return rdp->rc4_decrypt_key != NULL; +} diff --git a/libfreerdp/core/rdp.h b/libfreerdp/core/rdp.h index d36a932e35fb..7163adc75a84 100644 --- a/libfreerdp/core/rdp.h +++ b/libfreerdp/core/rdp.h @@ -132,8 +132,7 @@ struct rdp_rdp { - int state; - freerdp* instance; + CONNECTION_STATE state; rdpContext* context; rdpNla* nla; rdpMcs* mcs; @@ -242,4 +241,10 @@ BOOL rdp_decrypt(rdpRdp* rdp, wStream* s, UINT16* pLength, UINT16 securityFlags) BOOL rdp_set_error_info(rdpRdp* rdp, UINT32 errorInfo); BOOL rdp_send_error_info(rdpRdp* rdp); +void rdp_free_rc4_encrypt_keys(rdpRdp* rdp); +BOOL rdp_reset_rc4_encrypt_keys(rdpRdp* rdp); + +void rdp_free_rc4_decrypt_keys(rdpRdp* rdp); +BOOL rdp_reset_rc4_decrypt_keys(rdpRdp* rdp); + #endif /* FREERDP_LIB_CORE_RDP_H */ diff --git a/libfreerdp/core/redirection.c b/libfreerdp/core/redirection.c index 9f94583b86c8..4872d4b8fb17 100644 --- a/libfreerdp/core/redirection.c +++ b/libfreerdp/core/redirection.c @@ -23,10 +23,11 @@ #include #include +#include #include "connection.h" - #include "redirection.h" +#include "utils.h" #define TAG FREERDP_TAG("core.redirection") @@ -35,108 +36,167 @@ struct rdp_redirection UINT32 flags; UINT32 sessionID; BYTE* TsvUrl; - DWORD TsvUrlLength; + UINT32 TsvUrlLength; char* Username; char* Domain; BYTE* Password; - DWORD PasswordLength; + UINT32 PasswordLength; char* TargetFQDN; BYTE* LoadBalanceInfo; - DWORD LoadBalanceInfoLength; + UINT32 LoadBalanceInfoLength; char* TargetNetBiosName; char* TargetNetAddress; UINT32 TargetNetAddressesCount; char** TargetNetAddresses; + UINT32 RedirectionGuidLength; + BYTE* RedirectionGuid; + UINT32 TargetCertificateLength; + BYTE* TargetCertificate; }; -static void rdp_print_redirection_flags(UINT32 flags) +static void redirection_free_array(char*** what, UINT32* count) { - WLog_DBG(TAG, "redirectionFlags = {"); - - if (flags & LB_TARGET_NET_ADDRESS) - WLog_DBG(TAG, "\tLB_TARGET_NET_ADDRESS"); - - if (flags & LB_LOAD_BALANCE_INFO) - WLog_DBG(TAG, "\tLB_LOAD_BALANCE_INFO"); + WINPR_ASSERT(what); + WINPR_ASSERT(count); - if (flags & LB_USERNAME) - WLog_DBG(TAG, "\tLB_USERNAME"); + if (*what) + { + for (UINT32 x = 0; x < *count; x++) + free((*what)[x]); + free(*what); + } - if (flags & LB_DOMAIN) - WLog_DBG(TAG, "\tLB_DOMAIN"); + *what = NULL; + *count = 0; +} - if (flags & LB_PASSWORD) - WLog_DBG(TAG, "\tLB_PASSWORD"); +static void redirection_free_string(char** str) +{ + WINPR_ASSERT(str); + free(*str); + *str = NULL; +} - if (flags & LB_DONTSTOREUSERNAME) - WLog_DBG(TAG, "\tLB_DONTSTOREUSERNAME"); +static void redirection_free_data(BYTE** str, UINT32* length) +{ + WINPR_ASSERT(str); + free(*str); + if (length) + *length = 0; + *str = NULL; +} - if (flags & LB_SMARTCARD_LOGON) - WLog_DBG(TAG, "\tLB_SMARTCARD_LOGON"); +static BOOL redirection_copy_data(BYTE** dst, UINT32* plen, const BYTE* str, UINT32 len) +{ + redirection_free_data(dst, plen); - if (flags & LB_NOREDIRECT) - WLog_DBG(TAG, "\tLB_NOREDIRECT"); + if (!str || (len == 0)) + return TRUE; - if (flags & LB_TARGET_FQDN) - WLog_DBG(TAG, "\tLB_TARGET_FQDN"); + *dst = malloc(len); + if (!*dst) + return FALSE; + memcpy(*dst, str, len); + *plen = len; + return *dst != NULL; +} - if (flags & LB_TARGET_NETBIOS_NAME) - WLog_DBG(TAG, "\tLB_TARGET_NETBIOS_NAME"); +static BOOL freerdp_settings_set_pointer_len(rdpSettings* settings, size_t id, const BYTE* data, + size_t length) +{ + BYTE** pdata = NULL; + UINT32* plen = NULL; + switch (id) + { + case FreeRDP_TargetNetAddress: + pdata = (BYTE**)&settings->TargetNetAddress; + plen = &settings->TargetNetAddressCount; + break; + case FreeRDP_LoadBalanceInfo: + pdata = &settings->LoadBalanceInfo; + plen = &settings->LoadBalanceInfoLength; + break; + case FreeRDP_RedirectionPassword: + pdata = &settings->RedirectionPassword; + plen = &settings->RedirectionPasswordLength; + break; + case FreeRDP_RedirectionTsvUrl: + pdata = &settings->RedirectionTsvUrl; + plen = &settings->RedirectionTsvUrlLength; + break; + case FreeRDP_RedirectionGuid: + pdata = &settings->RedirectionGuid; + plen = &settings->RedirectionGuidLength; + break; + case FreeRDP_RedirectionTargetCertificate: + pdata = &settings->RedirectionTargetCertificate; + plen = &settings->RedirectionTargetCertificateLength; + break; + default: + return FALSE; + } - if (flags & LB_TARGET_NET_ADDRESSES) - WLog_DBG(TAG, "\tLB_TARGET_NET_ADDRESSES"); + return redirection_copy_data(pdata, plen, data, length); +} - if (flags & LB_CLIENT_TSV_URL) - WLog_DBG(TAG, "\tLB_CLIENT_TSV_URL"); +static void rdp_print_redirection_flags(UINT32 flags) +{ + WLog_DBG(TAG, "redirectionFlags = {"); - if (flags & LB_SERVER_TSV_CAPABLE) - WLog_DBG(TAG, "\tLB_SERVER_TSV_CAPABLE"); + for (UINT32 x = 0; x < 32; x++) + { + const UINT32 mask = 1 << x; + if ((flags & mask) != 0) + { + char buffer[64] = { 0 }; + WLog_DBG(TAG, "\t%s", rdp_redirection_flags_to_string(mask, buffer, sizeof(buffer))); + } + } WLog_DBG(TAG, "}"); } static BOOL rdp_redirection_read_unicode_string(wStream* s, char** str, size_t maxLength) { - UINT32 length; - WCHAR* wstr = NULL; + UINT32 length = 0; + const WCHAR* wstr = NULL; - if (Stream_GetRemainingLength(s) < 4) - { - WLog_ERR(TAG, "rdp_redirection_read_string failure: cannot read length"); + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) return FALSE; - } Stream_Read_UINT32(s, length); if ((length % 2) || length < 2 || length > maxLength) { - WLog_ERR(TAG, - "rdp_redirection_read_string failure: invalid unicode string length: %" PRIu32 "", + WLog_ERR(TAG, "[%s] failure: invalid unicode string length: %" PRIu32 "", __FUNCTION__, length); return FALSE; } - if (Stream_GetRemainingLength(s) < length) + if (!Stream_CheckAndLogRequiredLength(TAG, s, length)) { - WLog_ERR(TAG, - "rdp_redirection_read_string failure: insufficient stream length (%" PRIu32 - " bytes required)", - length); + WLog_ERR(TAG, "[%s] failure: insufficient stream length (%" PRIu32 " bytes required)", + __FUNCTION__, length); return FALSE; } - wstr = (WCHAR*)Stream_Pointer(s); + wstr = (const WCHAR*)Stream_Pointer(s); if (wstr[length / 2 - 1]) { - WLog_ERR(TAG, "rdp_redirection_read_string failure: unterminated unicode string"); + WLog_ERR(TAG, "[%s] failure: unterminated unicode string", __FUNCTION__); return FALSE; } - if (ConvertFromUnicode(CP_UTF8, 0, wstr, -1, str, 0, NULL, NULL) < 1) + redirection_free_string(str); { - WLog_ERR(TAG, "rdp_redirection_read_string failure: string conversion failed"); - return FALSE; + const int res = + ConvertFromUnicode(CP_UTF8, 0, wstr, length / sizeof(WCHAR), str, 0, NULL, NULL); + if ((res < 0) || !(*str)) + { + WLog_ERR(TAG, "[%s] failure: string conversion failed", __FUNCTION__); + return FALSE; + } } Stream_Seek(s, length); @@ -145,23 +205,40 @@ static BOOL rdp_redirection_read_unicode_string(wStream* s, char** str, size_t m int rdp_redirection_apply_settings(rdpRdp* rdp) { - rdpSettings* settings = rdp->settings; - rdpRedirection* redirection = rdp->redirection; + rdpSettings* settings = NULL; + rdpRedirection* redirection = NULL; + + WINPR_ASSERT(rdp); + + settings = rdp->settings; + WINPR_ASSERT(settings); + + redirection = rdp->redirection; + WINPR_ASSERT(redirection); + + { + char buffer[2048] = { 0 }; + WLog_DBG(TAG, "RedirectionFlags=%s", + rdp_redirection_flags_to_string(redirection->flags, buffer, sizeof(buffer))); + } + settings->RedirectionFlags = redirection->flags; settings->RedirectedSessionId = redirection->sessionID; + if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESS) + { + if (!freerdp_settings_set_string(settings, FreeRDP_TargetNetAddress, + redirection->TargetNetAddress)) + return -1; + } + if (settings->RedirectionFlags & LB_LOAD_BALANCE_INFO) { /* LoadBalanceInfo may not contain a null terminator */ - free(settings->LoadBalanceInfo); - settings->LoadBalanceInfoLength = redirection->LoadBalanceInfoLength; - settings->LoadBalanceInfo = (BYTE*)malloc(settings->LoadBalanceInfoLength); - - if (!settings->LoadBalanceInfo) + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo, + redirection->LoadBalanceInfo, + redirection->LoadBalanceInfoLength)) return -1; - - CopyMemory(settings->LoadBalanceInfo, redirection->LoadBalanceInfo, - settings->LoadBalanceInfoLength); } else { @@ -169,128 +246,138 @@ int rdp_redirection_apply_settings(rdpRdp* rdp) * Free previous LoadBalanceInfo, if any, otherwise it may end up * being reused for the redirected session, which is not what we want. */ - free(settings->LoadBalanceInfo); - settings->LoadBalanceInfo = NULL; - settings->LoadBalanceInfoLength = 0; + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_LoadBalanceInfo, NULL, 0)) + return -1; } - if (settings->RedirectionFlags & LB_TARGET_FQDN) + if (settings->RedirectionFlags & LB_USERNAME) { - free(settings->RedirectionTargetFQDN); - settings->RedirectionTargetFQDN = _strdup(redirection->TargetFQDN); - - if (!settings->RedirectionTargetFQDN) + if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionUsername, + redirection->Username)) return -1; } - if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESS) + if (settings->RedirectionFlags & LB_DOMAIN) { - free(settings->TargetNetAddress); - settings->TargetNetAddress = _strdup(redirection->TargetNetAddress); - - if (!settings->TargetNetAddress) + if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionDomain, redirection->Domain)) return -1; } - if (settings->RedirectionFlags & LB_TARGET_NETBIOS_NAME) + if (settings->RedirectionFlags & LB_PASSWORD) { - free(settings->RedirectionTargetNetBiosName); - settings->RedirectionTargetNetBiosName = _strdup(redirection->TargetNetBiosName); - - if (!settings->RedirectionTargetNetBiosName) + /* Password may be a cookie without a null terminator */ + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionPassword, + redirection->Password, redirection->PasswordLength)) return -1; } - if (settings->RedirectionFlags & LB_USERNAME) + if (settings->RedirectionFlags & LB_DONTSTOREUSERNAME) { - free(settings->RedirectionUsername); - settings->RedirectionUsername = _strdup(redirection->Username); + // TODO + } - if (!settings->RedirectionUsername) - return -1; + if (settings->RedirectionFlags & LB_SMARTCARD_LOGON) + { + // TODO } - if (settings->RedirectionFlags & LB_DOMAIN) + if (settings->RedirectionFlags & LB_NOREDIRECT) { - free(settings->RedirectionDomain); - settings->RedirectionDomain = _strdup(redirection->Domain); + // TODO + } - if (!settings->RedirectionDomain) + if (settings->RedirectionFlags & LB_TARGET_FQDN) + { + if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionTargetFQDN, + redirection->TargetFQDN)) return -1; } - if (settings->RedirectionFlags & LB_PASSWORD) + if (settings->RedirectionFlags & LB_TARGET_NETBIOS_NAME) { - /* Password may be a cookie without a null terminator */ - free(settings->RedirectionPassword); - settings->RedirectionPasswordLength = redirection->PasswordLength; - /* For security reasons we'll allocate an additional zero WCHAR at the - * end of the buffer that is not included in RedirectionPasswordLength - */ - settings->RedirectionPassword = - (BYTE*)calloc(1, settings->RedirectionPasswordLength + sizeof(WCHAR)); - - if (!settings->RedirectionPassword) + if (!freerdp_settings_set_string(settings, FreeRDP_RedirectionTargetNetBiosName, + redirection->TargetNetBiosName)) return -1; + } - CopyMemory(settings->RedirectionPassword, redirection->Password, - settings->RedirectionPasswordLength); + if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESSES) + { + if (!freerdp_target_net_addresses_copy(settings, redirection->TargetNetAddresses, + redirection->TargetNetAddressesCount)) + return -1; } if (settings->RedirectionFlags & LB_CLIENT_TSV_URL) { /* TsvUrl may not contain a null terminator */ - free(settings->RedirectionTsvUrl); - settings->RedirectionTsvUrlLength = redirection->TsvUrlLength; - settings->RedirectionTsvUrl = (BYTE*)malloc(settings->RedirectionTsvUrlLength); - - if (!settings->RedirectionTsvUrl) + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionTsvUrl, + redirection->TsvUrl, redirection->TsvUrlLength)) return -1; + } - CopyMemory(settings->RedirectionTsvUrl, redirection->TsvUrl, - settings->RedirectionTsvUrlLength); + if (settings->RedirectionFlags & LB_SERVER_TSV_CAPABLE) + { + // TODO } - if (settings->RedirectionFlags & LB_TARGET_NET_ADDRESSES) + if (settings->RedirectionFlags & LB_PASSWORD_IS_PK_ENCRYPTED) { - UINT32 i; - freerdp_target_net_addresses_free(settings); - settings->TargetNetAddressCount = redirection->TargetNetAddressesCount; - settings->TargetNetAddresses = - (char**)calloc(settings->TargetNetAddressCount, sizeof(char*)); + // TODO + } - if (!settings->TargetNetAddresses) - { - settings->TargetNetAddressCount = 0; + if (settings->RedirectionFlags & LB_REDIRECTION_GUID) + { + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionGuid, + redirection->RedirectionGuid, + redirection->RedirectionGuidLength)) return -1; - } + } - for (i = 0; i < settings->TargetNetAddressCount; i++) - { - settings->TargetNetAddresses[i] = _strdup(redirection->TargetNetAddresses[i]); + if (settings->RedirectionFlags & LB_TARGET_CERTIFICATE) + { + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RedirectionTargetCertificate, + redirection->TargetCertificate, + redirection->TargetCertificateLength)) + return -1; + } - if (!settings->TargetNetAddresses[i]) - { - UINT32 j; + return 0; +} - for (j = 0; j < i; j++) - free(settings->TargetNetAddresses[j]); +static BOOL rdp_redirection_read_data(UINT32 flag, wStream* s, UINT32* pLength, BYTE** pData) +{ + char buffer[64] = { 0 }; - return -1; - } - } - } + WINPR_ASSERT(pLength); + WINPR_ASSERT(pData); - return 0; + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + return FALSE; + + Stream_Read_UINT32(s, *pLength); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, *pLength)) + return FALSE; + + redirection_free_data(pData, NULL); + *pData = (BYTE*)malloc(*pLength); + + if (!*pData) + return FALSE; + + Stream_Read(s, *pData, *pLength); + WLog_DBG(TAG, "%s:", rdp_redirection_flags_to_string(flag, buffer, sizeof(buffer))); + winpr_HexDump(TAG, WLOG_DEBUG, *pData, *pLength); + return TRUE; } -static BOOL rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s) +static int rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s) { - UINT16 flags; - UINT16 length; + UINT16 flags = 0; + UINT16 length = 0; rdpRedirection* redirection = rdp->redirection; - if (Stream_GetRemainingLength(s) < 12) + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) return -1; Stream_Read_UINT16(s, flags); /* flags (2 bytes) */ @@ -329,23 +416,9 @@ static BOOL rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s) * 0010 34 30 32 36 34 33 32 2e 31 35 36 32 39 2e 30 30 4026432.15629.00 * 0020 30 30 0d 0a 00.. */ - if (Stream_GetRemainingLength(s) < 4) + if (!rdp_redirection_read_data(LB_LOAD_BALANCE_INFO, s, &redirection->LoadBalanceInfoLength, + &redirection->LoadBalanceInfo)) return -1; - - Stream_Read_UINT32(s, redirection->LoadBalanceInfoLength); - - if (Stream_GetRemainingLength(s) < redirection->LoadBalanceInfoLength) - return -1; - - redirection->LoadBalanceInfo = (BYTE*)malloc(redirection->LoadBalanceInfoLength); - - if (!redirection->LoadBalanceInfo) - return -1; - - Stream_Read(s, redirection->LoadBalanceInfo, redirection->LoadBalanceInfoLength); - WLog_DBG(TAG, "loadBalanceInfo:"); - winpr_HexDump(TAG, WLOG_DEBUG, redirection->LoadBalanceInfo, - redirection->LoadBalanceInfoLength); } if (redirection->flags & LB_USERNAME) @@ -359,7 +432,7 @@ static BOOL rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s) if (redirection->flags & LB_DOMAIN) { if (!rdp_redirection_read_unicode_string(s, &(redirection->Domain), 52)) - return FALSE; + return -1; WLog_DBG(TAG, "Domain: %s", redirection->Domain); } @@ -387,32 +460,24 @@ static BOOL rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s) * Notwithstanding the above, we'll allocated an additional zero WCHAR at the * end of the buffer which won't get counted in PasswordLength. */ - if (Stream_GetRemainingLength(s) < 4) + if (!rdp_redirection_read_data(LB_PASSWORD, s, &redirection->PasswordLength, + &redirection->Password)) return -1; - Stream_Read_UINT32(s, redirection->PasswordLength); - /* [MS-RDPBCGR] specifies 512 bytes as the upper limit for the password length * including the null terminatior(s). This should also be enough for the unknown * password cookie format (see previous comment). */ + if ((redirection->flags & LB_PASSWORD_IS_PK_ENCRYPTED) == 0) + { + const size_t charLen = redirection->PasswordLength / sizeof(WCHAR); + if (redirection->PasswordLength > LB_PASSWORD_MAX_LENGTH) + return -1; - if (Stream_GetRemainingLength(s) < redirection->PasswordLength) - return -1; - - if (redirection->PasswordLength > LB_PASSWORD_MAX_LENGTH) - return -1; - - redirection->Password = (BYTE*)calloc(1, redirection->PasswordLength + sizeof(WCHAR)); - - if (!redirection->Password) - return -1; - - Stream_Read(s, redirection->Password, redirection->PasswordLength); - WLog_DBG(TAG, "PasswordCookie:"); -#if defined(WITH_DEBUG_REDIR) - winpr_HexDump(TAG, WLOG_DEBUG, redirection->Password, redirection->PasswordLength); -#endif + /* Ensure the text password is '\0' terminated */ + if (_wcsnlen((const WCHAR*)redirection->Password, charLen) == charLen) + return -1; + } } if (redirection->flags & LB_TARGET_FQDN) @@ -433,31 +498,33 @@ static BOOL rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s) if (redirection->flags & LB_CLIENT_TSV_URL) { - if (Stream_GetRemainingLength(s) < 4) + if (!rdp_redirection_read_data(LB_CLIENT_TSV_URL, s, &redirection->TsvUrlLength, + &redirection->TsvUrl)) return -1; + } - Stream_Read_UINT32(s, redirection->TsvUrlLength); - - if (Stream_GetRemainingLength(s) < redirection->TsvUrlLength) + if (redirection->flags & LB_REDIRECTION_GUID) + { + if (!rdp_redirection_read_data(LB_REDIRECTION_GUID, s, &redirection->RedirectionGuidLength, + &redirection->RedirectionGuid)) return -1; + } - redirection->TsvUrl = (BYTE*)malloc(redirection->TsvUrlLength); - - if (!redirection->TsvUrl) + if (redirection->flags & LB_TARGET_CERTIFICATE) + { + if (!rdp_redirection_read_data(LB_TARGET_CERTIFICATE, s, + &redirection->TargetCertificateLength, + &redirection->TargetCertificate)) return -1; - - Stream_Read(s, redirection->TsvUrl, redirection->TsvUrlLength); - WLog_DBG(TAG, "TsvUrl:"); - winpr_HexDump(TAG, WLOG_DEBUG, redirection->TsvUrl, redirection->TsvUrlLength); } if (redirection->flags & LB_TARGET_NET_ADDRESSES) { - int i; - UINT32 count; - UINT32 targetNetAddressesLength; + size_t i = 0; + UINT32 count = 0; + UINT32 targetNetAddressesLength = 0; - if (Stream_GetRemainingLength(s) < 8) + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) return -1; Stream_Read_UINT32(s, targetNetAddressesLength); @@ -466,16 +533,17 @@ static BOOL rdp_recv_server_redirection_pdu(rdpRdp* rdp, wStream* s) redirection->TargetNetAddresses = (char**)calloc(count, sizeof(char*)); if (!redirection->TargetNetAddresses) - return FALSE; + return -1; - WLog_DBG(TAG, "TargetNetAddressesCount: %" PRIu32 "", redirection->TargetNetAddressesCount); + WLog_DBG(TAG, "TargetNetAddressesCount: %" PRIu32 "", count); - for (i = 0; i < (int)count; i++) + for (i = 0; i < count; i++) { if (!rdp_redirection_read_unicode_string(s, &(redirection->TargetNetAddresses[i]), 80)) - return FALSE; + return -1; - WLog_DBG(TAG, "TargetNetAddresses[%d]: %s", i, redirection->TargetNetAddresses[i]); + WLog_DBG(TAG, "TargetNetAddresses[%" PRIuz "]: %s", i, + redirection->TargetNetAddresses[i]); } } @@ -512,7 +580,7 @@ int rdp_recv_enhanced_security_redirection_packet(rdpRdp* rdp, wStream* s) return status; } -rdpRedirection* redirection_new() +rdpRedirection* redirection_new(void) { rdpRedirection* redirection; redirection = (rdpRedirection*)calloc(1, sizeof(rdpRedirection)); @@ -528,26 +596,19 @@ void redirection_free(rdpRedirection* redirection) { if (redirection) { - free(redirection->TsvUrl); - free(redirection->Username); - free(redirection->Domain); - free(redirection->TargetFQDN); - free(redirection->TargetNetBiosName); - free(redirection->TargetNetAddress); - free(redirection->LoadBalanceInfo); - free(redirection->Password); - - if (redirection->TargetNetAddresses) - { - int i; - - for (i = 0; i < (int)redirection->TargetNetAddressesCount; i++) - { - free(redirection->TargetNetAddresses[i]); - } - - free(redirection->TargetNetAddresses); - } + redirection_free_data(&redirection->TsvUrl, &redirection->TsvUrlLength); + redirection_free_string(&redirection->Username); + redirection_free_string(&redirection->Domain); + redirection_free_string(&redirection->TargetFQDN); + redirection_free_string(&redirection->TargetNetBiosName); + redirection_free_string(&redirection->TargetNetAddress); + redirection_free_data(&redirection->LoadBalanceInfo, &redirection->LoadBalanceInfoLength); + redirection_free_data(&redirection->Password, &redirection->PasswordLength); + redirection_free_data(&redirection->RedirectionGuid, &redirection->RedirectionGuidLength); + redirection_free_data(&redirection->TargetCertificate, + &redirection->TargetCertificateLength); + redirection_free_array(&redirection->TargetNetAddresses, + &redirection->TargetNetAddressesCount); free(redirection); } diff --git a/libfreerdp/core/security.c b/libfreerdp/core/security.c index 7caf5a842e18..17811c747e69 100644 --- a/libfreerdp/core/security.c +++ b/libfreerdp/core/security.c @@ -719,18 +719,19 @@ BOOL security_encrypt(BYTE* data, size_t length, rdpRdp* rdp) { BOOL rc = FALSE; EnterCriticalSection(&rdp->critical); + if (!rdp->rc4_encrypt_key) + { + WLog_ERR(TAG, "[%s] rdp->rc4_encrypt_key=%p", __FUNCTION__, rdp->rc4_encrypt_key); + goto fail; + } + if (rdp->encrypt_use_count >= 4096) { if (!security_key_update(rdp->encrypt_key, rdp->encrypt_update_key, rdp->rc4_key_len, rdp)) goto fail; - winpr_RC4_Free(rdp->rc4_encrypt_key); - rdp->rc4_encrypt_key = winpr_RC4_New(rdp->encrypt_key, rdp->rc4_key_len); - - if (!rdp->rc4_encrypt_key) + if (!rdp_reset_rc4_encrypt_keys(rdp)) goto fail; - - rdp->encrypt_use_count = 0; } if (!winpr_RC4_Update(rdp->rc4_encrypt_key, length, data, data)) @@ -748,21 +749,19 @@ BOOL security_decrypt(BYTE* data, size_t length, rdpRdp* rdp) { BOOL rc = FALSE; EnterCriticalSection(&rdp->critical); - if (rdp->rc4_decrypt_key == NULL) + if (!rdp->rc4_decrypt_key) + { + WLog_ERR(TAG, "[%s] rdp->rc4_decrypt_key=%p", __FUNCTION__, rdp->rc4_decrypt_key); goto fail; + } if (rdp->decrypt_use_count >= 4096) { if (!security_key_update(rdp->decrypt_key, rdp->decrypt_update_key, rdp->rc4_key_len, rdp)) goto fail; - winpr_RC4_Free(rdp->rc4_decrypt_key); - rdp->rc4_decrypt_key = winpr_RC4_New(rdp->decrypt_key, rdp->rc4_key_len); - - if (!rdp->rc4_decrypt_key) + if (!rdp_reset_rc4_decrypt_keys(rdp)) goto fail; - - rdp->decrypt_use_count = 0; } if (!winpr_RC4_Update(rdp->rc4_decrypt_key, length, data, data)) diff --git a/libfreerdp/core/server.c b/libfreerdp/core/server.c index 484ed8b96944..b8298a22a84d 100644 --- a/libfreerdp/core/server.c +++ b/libfreerdp/core/server.c @@ -158,6 +158,7 @@ static BOOL wts_read_drdynvc_capabilities_response(rdpPeerChannel* channel, UINT static BOOL wts_read_drdynvc_create_response(rdpPeerChannel* channel, wStream* s, UINT32 length) { UINT32 CreationStatus; + BOOL status = TRUE; if (length < 4) return FALSE; @@ -176,7 +177,12 @@ static BOOL wts_read_drdynvc_create_response(rdpPeerChannel* channel, wStream* s channel->dvc_open_state = DVC_OPEN_STATE_SUCCEEDED; } - return TRUE; + IFCALLRET(channel->vcm->dvc_creation_status, status, channel->vcm->dvc_creation_status_userdata, + channel->channelId, (INT32)CreationStatus); + if (!status) + WLog_ERR(TAG, "vcm->dvc_creation_status failed!"); + + return status; } static BOOL wts_read_drdynvc_data_first(rdpPeerChannel* channel, wStream* s, int cbLen, @@ -285,9 +291,27 @@ static BOOL wts_read_drdynvc_pdu(rdpPeerChannel* channel) return wts_read_drdynvc_create_response(dvc, channel->receiveData, length); case DATA_FIRST_PDU: + if (dvc->dvc_open_state != DVC_OPEN_STATE_SUCCEEDED) + { + WLog_ERR(TAG, + "ChannelId %" PRIu32 " did not open successfully. " + "Ignoring DYNVC_DATA_FIRST PDU", + ChannelId); + return TRUE; + } + return wts_read_drdynvc_data_first(dvc, channel->receiveData, Sp, length); case DATA_PDU: + if (dvc->dvc_open_state != DVC_OPEN_STATE_SUCCEEDED) + { + WLog_ERR(TAG, + "ChannelId %" PRIu32 " did not open successfully. " + "Ignoring DYNVC_DATA PDU", + ChannelId); + return TRUE; + } + return wts_read_drdynvc_data(dvc, channel->receiveData, length); case CLOSE_REQUEST_PDU: @@ -469,7 +493,7 @@ BOOL WTSVirtualChannelManagerCheckFileDescriptor(HANDLE hServer) { ULONG written; vcm->drdynvc_channel = channel; - dynvc_caps = 0x00010050; /* DYNVC_CAPS_VERSION1 (4 bytes) */ + Data_Write_UINT32(&dynvc_caps, 0x00010050); /* DYNVC_CAPS_VERSION1 (4 bytes) */ if (!WTSVirtualChannelWrite(channel, (PCHAR)&dynvc_caps, sizeof(dynvc_caps), &written)) return FALSE; @@ -578,6 +602,17 @@ BYTE WTSVirtualChannelManagerGetDrdynvcState(HANDLE hServer) return vcm->drdynvc_state; } +void WTSVirtualChannelManagerSetDVCCreationCallback(HANDLE hServer, psDVCCreationStatusCallback cb, + void* userdata) +{ + WTSVirtualChannelManager* vcm = hServer; + + WINPR_ASSERT(vcm); + + vcm->dvc_creation_status = cb; + vcm->dvc_creation_status_userdata = userdata; +} + UINT16 WTSChannelGetId(freerdp_peer* client, const char* channel_name) { rdpMcsChannel* channel; @@ -593,6 +628,15 @@ UINT16 WTSChannelGetId(freerdp_peer* client, const char* channel_name) return channel->ChannelId; } +UINT32 WTSChannelGetIdByHandle(HANDLE hChannelHandle) +{ + rdpPeerChannel* channel = hChannelHandle; + + WINPR_ASSERT(channel); + + return channel->channelId; +} + BOOL WTSChannelSetHandleByName(freerdp_peer* client, const char* channel_name, void* handle) { rdpMcsChannel* channel; @@ -670,6 +714,43 @@ const char* WTSChannelGetName(freerdp_peer* client, UINT16 channel_id) return (const char*)channel->Name; } +INT64 WTSChannelGetOptions(freerdp_peer* client, UINT16 channel_id) +{ + rdpMcsChannel* channel; + + if (!client || !client->context || !client->context->rdp) + return -1; + + channel = wts_get_joined_channel_by_id(client->context->rdp->mcs, channel_id); + + if (!channel) + return -1; + + return (INT64)channel->options; +} + +char** WTSGetAcceptedChannelNames(freerdp_peer* client, size_t* count) +{ + rdpMcs* mcs; + char** names; + UINT32 index; + + if (!client || !client->context || !count) + return NULL; + + mcs = client->context->rdp->mcs; + *count = mcs->channelCount; + + names = (char**)calloc(mcs->channelCount, sizeof(char*)); + if (!names) + return NULL; + + for (index = 0; index < mcs->channelCount; index++) + names[index] = mcs->channels[index].Name; + + return names; +} + BOOL WINAPI FreeRDP_WTSStartRemoteControlSessionW(LPWSTR pTargetServerName, ULONG TargetLogonId, BYTE HotkeyVk, USHORT HotkeyModifiers) { diff --git a/libfreerdp/core/server.h b/libfreerdp/core/server.h index 3e2552ee38a2..ea1415c386bb 100644 --- a/libfreerdp/core/server.h +++ b/libfreerdp/core/server.h @@ -86,6 +86,9 @@ struct WTSVirtualChannelManager BYTE drdynvc_state; LONG dvc_channel_id_seq; + psDVCCreationStatusCallback dvc_creation_status; + void* dvc_creation_status_userdata; + wArrayList* dynamicVirtualChannels; }; diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index eb425dd958cd..76b0e790b40b 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -401,13 +401,9 @@ rdpSettings* freerdp_settings_new(DWORD flags) if (!settings->ClientProductId) goto out_fail; - settings->ClientHostname = calloc(1, 32); - - if (!settings->ClientHostname) + if (!freerdp_settings_set_string(settings, FreeRDP_ClientHostname, settings->ComputerName)) goto out_fail; - gethostname(settings->ClientHostname, 31); - settings->ClientHostname[31] = 0; settings->ColorPointerFlag = TRUE; settings->LargePointerFlag = (LARGE_POINTER_FLAG_96x96 | LARGE_POINTER_FLAG_384x384); settings->PointerCacheSize = 20; @@ -492,6 +488,7 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->GatewayRpcTransport = TRUE; settings->GatewayHttpTransport = TRUE; settings->GatewayUdpTransport = TRUE; + settings->GatewayHttpUseWebsockets = TRUE; settings->FastPathInput = TRUE; settings->FastPathOutput = TRUE; settings->LongCredentialsSupported = TRUE; @@ -547,11 +544,13 @@ rdpSettings* freerdp_settings_new(DWORD flags) if (!settings->DynamicChannelArray) goto out_fail; - settings->TcpKeepAlive = TRUE; - settings->TcpKeepAliveRetries = 3; - settings->TcpKeepAliveDelay = 5; - settings->TcpKeepAliveInterval = 2; - settings->TcpAckTimeout = 9000; + if (!freerdp_settings_set_bool(settings, FreeRDP_TcpKeepAlive, TRUE) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveRetries, 3) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveDelay, 5) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveInterval, 2) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpAckTimeout, 9000) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpConnectTimeout, 15000)) + goto out_fail; if (!settings->ServerMode) { @@ -600,6 +599,9 @@ rdpSettings* freerdp_settings_new(DWORD flags) settings->TlsSecLevel = 1; settings->OrderSupport = calloc(1, 32); + freerdp_settings_set_uint16(settings, FreeRDP_TLSMinVersion, TLS1_VERSION); + freerdp_settings_set_uint16(settings, FreeRDP_TLSMaxVersion, 0); + if (!settings->OrderSupport) goto out_fail; diff --git a/libfreerdp/core/surface.c b/libfreerdp/core/surface.c index d5c709885509..e288799f1602 100644 --- a/libfreerdp/core/surface.c +++ b/libfreerdp/core/surface.c @@ -21,6 +21,8 @@ #include "config.h" #endif +#include + #include #include @@ -35,7 +37,11 @@ static BOOL update_recv_surfcmd_bitmap_header_ex(wStream* s, TS_COMPRESSED_BITMA return FALSE; if (Stream_GetRemainingLength(s) < 24) + { + WLog_ERR(TAG, "got %" PRIuz ", expected %" PRIuz " bytes", Stream_GetRemainingLength(s), + 24); return FALSE; + } Stream_Read_UINT32(s, header->highUniqueId); Stream_Read_UINT32(s, header->lowUniqueId); @@ -52,7 +58,11 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp) return FALSE; if (Stream_GetRemainingLength(s) < 12) + { + WLog_ERR(TAG, "got %" PRIuz ", expected %" PRIuz " bytes", Stream_GetRemainingLength(s), + 12); return FALSE; + } Stream_Read_UINT8(s, bmp->bpp); Stream_Read_UINT8(s, bmp->flags); @@ -62,6 +72,13 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp) Stream_Read_UINT16(s, bmp->height); Stream_Read_UINT32(s, bmp->bitmapDataLength); + if ((bmp->width == 0) || (bmp->height == 0)) + { + WLog_ERR(TAG, "invalid size value width=%" PRIu16 ", height=%" PRIu16, bmp->width, + bmp->height); + return FALSE; + } + if ((bmp->bpp < 1) || (bmp->bpp > 32)) { WLog_ERR(TAG, "invalid bpp value %" PRIu32 "", bmp->bpp); @@ -77,7 +94,11 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp) } if (Stream_GetRemainingLength(s) < bmp->bitmapDataLength) + { + WLog_ERR(TAG, "expected bitmapDataLength %" PRIu32 ", not enough data", + bmp->bitmapDataLength); return FALSE; + } pos = Stream_GetPosition(s) + bmp->bitmapDataLength; bmp->bitmapData = Stream_Pointer(s); @@ -85,12 +106,48 @@ static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp) return TRUE; } +static BOOL update_recv_surfcmd_is_rect_valid(const rdpContext* context, + const SURFACE_BITS_COMMAND* cmd) +{ + assert(context); + assert(context->settings); + assert(cmd); + + /* We need a rectangle with left/top being smaller than right/bottom. + * Also do not allow empty rectangles. */ + if ((cmd->destTop >= cmd->destBottom) || (cmd->destLeft >= cmd->destRight)) + { + WLog_WARN(TAG, + "Empty surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16 + "x%" PRIu16, + cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom); + return FALSE; + } + + /* The rectangle needs to fit into our session size */ + if ((cmd->destRight > context->settings->DesktopWidth) || + (cmd->destBottom > context->settings->DesktopHeight)) + { + WLog_WARN(TAG, + "Invalid surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16 + "x%" PRIu16 " does not fit %" PRIu32 "x%" PRIu32, + cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, + context->settings->DesktopWidth, context->settings->DesktopHeight); + return FALSE; + } + + return TRUE; +} + static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT16 cmdType) { SURFACE_BITS_COMMAND cmd = { 0 }; if (Stream_GetRemainingLength(s) < 8) + { + WLog_ERR(TAG, "got %" PRIuz ", expected %" PRIuz " bytes", Stream_GetRemainingLength(s), 8); goto fail; + } cmd.cmdType = cmdType; Stream_Read_UINT16(s, cmd.destLeft); @@ -98,6 +155,9 @@ static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT Stream_Read_UINT16(s, cmd.destRight); Stream_Read_UINT16(s, cmd.destBottom); + if (!update_recv_surfcmd_is_rect_valid(update->context, &cmd)) + goto fail; + if (!update_recv_surfcmd_bitmap_ex(s, &cmd.bmp)) goto fail; @@ -107,7 +167,12 @@ static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT goto fail; } - return update->SurfaceBits(update->context, &cmd); + if (!update->SurfaceBits(update->context, &cmd)) + { + WLog_DBG(TAG, "update->SurfaceBits implementation failed"); + goto fail; + } + return TRUE; fail: return FALSE; } @@ -116,11 +181,21 @@ static BOOL update_recv_surfcmd_frame_marker(rdpUpdate* update, wStream* s) { SURFACE_FRAME_MARKER marker; - if (Stream_GetRemainingLength(s) < 6) + if (Stream_GetRemainingLength(s) < 2) + { + WLog_ERR(TAG, "got %" PRIuz ", expected %" PRIuz " bytes", Stream_GetRemainingLength(s), 6); return FALSE; + } Stream_Read_UINT16(s, marker.frameAction); - Stream_Read_UINT32(s, marker.frameId); + if (Stream_GetRemainingLength(s) < 4) + WLog_WARN(TAG, + "[SERVER-BUG]: got %" PRIuz ", expected %" PRIuz + " bytes. [MS-RDPBCGR] 2.2.9.2.3 Frame Marker Command (TS_FRAME_MARKER) is " + "missing frameId, ignoring", + Stream_GetRemainingLength(s), 4); + else + Stream_Read_UINT32(s, marker.frameId); WLog_Print(update->log, WLOG_DEBUG, "SurfaceFrameMarker: action: %s (%" PRIu32 ") id: %" PRIu32 "", (!marker.frameAction) ? "Begin" : "End", marker.frameAction, marker.frameId); @@ -131,7 +206,13 @@ static BOOL update_recv_surfcmd_frame_marker(rdpUpdate* update, wStream* s) return FALSE; } - return update->SurfaceFrameMarker(update->context, &marker); + if (!update->SurfaceFrameMarker(update->context, &marker)) + { + WLog_DBG(TAG, "update->SurfaceFrameMarker implementation failed"); + return FALSE; + } + + return TRUE; } int update_recv_surfcmds(rdpUpdate* update, wStream* s) @@ -201,10 +282,15 @@ static BOOL update_write_surfcmd_bitmap_ex(wStream* s, const TS_BITMAP_DATA_EX* if (!Stream_EnsureRemainingCapacity(s, 12)) return FALSE; + if (bmp->codecID > UINT8_MAX) + { + WLog_ERR(TAG, "Invalid TS_BITMAP_DATA_EX::codecID=0x%04" PRIx16 "", bmp->codecID); + return FALSE; + } Stream_Write_UINT8(s, bmp->bpp); Stream_Write_UINT8(s, bmp->flags); Stream_Write_UINT8(s, 0); /* reserved1, reserved2 */ - Stream_Write_UINT8(s, bmp->codecID); + Stream_Write_UINT8(s, (UINT8)bmp->codecID); Stream_Write_UINT16(s, bmp->width); Stream_Write_UINT16(s, bmp->height); Stream_Write_UINT32(s, bmp->bitmapDataLength); diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c index ef5f7b1939f3..0d0641b823b4 100644 --- a/libfreerdp/core/tcp.c +++ b/libfreerdp/core/tcp.c @@ -801,14 +801,14 @@ static BOOL freerdp_tcp_is_hostname_resolvable(rdpContext* context, const char* } static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct sockaddr* addr, - socklen_t addrlen, int timeout) + socklen_t addrlen, UINT32 timeout) { BOOL rc = FALSE; HANDLE handles[2]; int status = 0; int count = 0; u_long arg = 0; - DWORD tout = (timeout > 0) ? (DWORD)timeout * 1000U : INFINITE; + DWORD tout = (timeout > 0) ? timeout : INFINITE; handles[count] = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -844,12 +844,7 @@ static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct status = WaitForMultipleObjects(count, handles, FALSE, tout); if (WAIT_OBJECT_0 != status) - { - if (status == WAIT_OBJECT_0 + 1) - freerdp_set_last_error_log(context, FREERDP_ERROR_CONNECT_CANCELLED); - goto fail; - } status = recv(sockfd, NULL, 0, 0); @@ -894,7 +889,7 @@ static void peer_free(t_peer* peer) } static int freerdp_tcp_connect_multi(rdpContext* context, char** hostnames, UINT32* ports, - UINT32 count, int port, int timeout) + UINT32 count, UINT16 port, UINT32 timeout) { UINT32 index; UINT32 sindex = count; @@ -1235,9 +1230,6 @@ int freerdp_tcp_connect(rdpContext* context, rdpSettings* settings, const char* if (WaitForSingleObject(context->abortEvent, 0) == WAIT_OBJECT_0) { close(sockfd); - - freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_CANCELLED); - return -1; } diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index ff35a7ec8801..a85c2ffa6755 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -76,6 +76,7 @@ static const size_t bool_list_indices[] = { FreeRDP_GfxSmallCache, FreeRDP_GfxThinClient, FreeRDP_GrabKeyboard, + FreeRDP_GrabMouse, FreeRDP_HasExtendedMouseEvent, FreeRDP_HasHorizontalWheel, FreeRDP_HasMonitorAttributes, @@ -173,6 +174,8 @@ static const size_t bool_list_indices[] = { static const size_t uint16_list_indices[] = { FreeRDP_DesktopOrientation, FreeRDP_ProxyPort, + FreeRDP_TLSMaxVersion, + FreeRDP_TLSMinVersion, }; #define have_uint32_list_indices @@ -257,8 +260,10 @@ static const size_t uint32_list_indices[] = { FreeRDP_RedirectedSessionId, FreeRDP_RedirectionAcceptedCertLength, FreeRDP_RedirectionFlags, + FreeRDP_RedirectionGuidLength, FreeRDP_RedirectionPasswordLength, FreeRDP_RedirectionPreferType, + FreeRDP_RedirectionTargetCertificateLength, FreeRDP_RedirectionTsvUrlLength, FreeRDP_RemoteAppNumIconCacheEntries, FreeRDP_RemoteAppNumIconCaches, @@ -282,6 +287,7 @@ static const size_t uint32_list_indices[] = { FreeRDP_StaticChannelCount, FreeRDP_TargetNetAddressCount, FreeRDP_TcpAckTimeout, + FreeRDP_TcpConnectTimeout, FreeRDP_TcpKeepAliveDelay, FreeRDP_TcpKeepAliveInterval, FreeRDP_TcpKeepAliveRetries, @@ -391,7 +397,9 @@ static const size_t pointer_list_indices[] = { FreeRDP_RdpServerCertificate, FreeRDP_RdpServerRsaKey, FreeRDP_ReceivedCapabilities, + FreeRDP_RedirectionGuid, FreeRDP_RedirectionPassword, + FreeRDP_RedirectionTargetCertificate, FreeRDP_RedirectionTsvUrl, FreeRDP_ServerAutoReconnectCookie, FreeRDP_ServerCertificate, diff --git a/libfreerdp/core/timezone.c b/libfreerdp/core/timezone.c index 9de268719492..f6894d0c0904 100644 --- a/libfreerdp/core/timezone.c +++ b/libfreerdp/core/timezone.c @@ -22,12 +22,13 @@ #endif #include +#include #include #include "timezone.h" -static void rdp_read_system_time(wStream* s, SYSTEMTIME* system_time); -static void rdp_write_system_time(wStream* s, SYSTEMTIME* system_time); +static BOOL rdp_read_system_time(wStream* s, SYSTEMTIME* system_time); +static BOOL rdp_write_system_time(wStream* s, const SYSTEMTIME* system_time); /** * Read SYSTEM_TIME structure (TS_SYSTEMTIME).\n @@ -36,8 +37,13 @@ static void rdp_write_system_time(wStream* s, SYSTEMTIME* system_time); * @param system_time system time structure */ -void rdp_read_system_time(wStream* s, SYSTEMTIME* system_time) +BOOL rdp_read_system_time(wStream* s, SYSTEMTIME* system_time) { + WINPR_ASSERT(system_time); + + if (Stream_GetRemainingLength(s) < 16ull) + return FALSE; + Stream_Read_UINT16(s, system_time->wYear); /* wYear, must be set to 0 */ Stream_Read_UINT16(s, system_time->wMonth); /* wMonth */ Stream_Read_UINT16(s, system_time->wDayOfWeek); /* wDayOfWeek */ @@ -46,6 +52,7 @@ void rdp_read_system_time(wStream* s, SYSTEMTIME* system_time) Stream_Read_UINT16(s, system_time->wMinute); /* wMinute */ Stream_Read_UINT16(s, system_time->wSecond); /* wSecond */ Stream_Read_UINT16(s, system_time->wMilliseconds); /* wMilliseconds */ + return TRUE; } /** @@ -55,8 +62,12 @@ void rdp_read_system_time(wStream* s, SYSTEMTIME* system_time) * @param system_time system time structure */ -void rdp_write_system_time(wStream* s, SYSTEMTIME* system_time) +BOOL rdp_write_system_time(wStream* s, const SYSTEMTIME* system_time) { + WINPR_ASSERT(system_time); + if (!Stream_EnsureRemainingCapacity(s, 16ull)) + return FALSE; + Stream_Write_UINT16(s, system_time->wYear); /* wYear, must be set to 0 */ Stream_Write_UINT16(s, system_time->wMonth); /* wMonth */ Stream_Write_UINT16(s, system_time->wDayOfWeek); /* wDayOfWeek */ @@ -70,6 +81,7 @@ void rdp_write_system_time(wStream* s, SYSTEMTIME* system_time) system_time->wYear, system_time->wMonth, system_time->wDayOfWeek, system_time->wDay, system_time->wHour, system_time->wMinute, system_time->wSecond, system_time->wMilliseconds); + return TRUE; } /** @@ -81,7 +93,7 @@ void rdp_write_system_time(wStream* s, SYSTEMTIME* system_time) BOOL rdp_read_client_time_zone(wStream* s, rdpSettings* settings) { - LPTIME_ZONE_INFORMATION tz; + LPTIME_ZONE_INFORMATION tz = { 0 }; if (!s || !settings) return FALSE; @@ -97,11 +109,13 @@ BOOL rdp_read_client_time_zone(wStream* s, rdpSettings* settings) Stream_Read_UINT32(s, tz->Bias); /* Bias */ /* standardName (64 bytes) */ Stream_Read(s, tz->StandardName, sizeof(tz->StandardName)); - rdp_read_system_time(s, &tz->StandardDate); /* StandardDate */ + if (!rdp_read_system_time(s, &tz->StandardDate)) /* StandardDate */ + return FALSE; Stream_Read_UINT32(s, tz->StandardBias); /* StandardBias */ /* daylightName (64 bytes) */ Stream_Read(s, tz->DaylightName, sizeof(tz->DaylightName)); - rdp_read_system_time(s, &tz->DaylightDate); /* DaylightDate */ + if (!rdp_read_system_time(s, &tz->DaylightDate)) /* DaylightDate */ + return FALSE; Stream_Read_UINT32(s, tz->DaylightBias); /* DaylightBias */ return TRUE; } @@ -115,19 +129,26 @@ BOOL rdp_read_client_time_zone(wStream* s, rdpSettings* settings) BOOL rdp_write_client_time_zone(wStream* s, rdpSettings* settings) { - LPTIME_ZONE_INFORMATION tz; + LPTIME_ZONE_INFORMATION tz = { 0 }; + + WINPR_ASSERT(settings); tz = settings->ClientTimeZone; if (!tz) return FALSE; GetTimeZoneInformation(tz); + if (!Stream_EnsureRemainingCapacity(s, 4ull + sizeof(tz->StandardName))) + return FALSE; + /* Bias */ Stream_Write_UINT32(s, tz->Bias); /* standardName (64 bytes) */ Stream_Write(s, tz->StandardName, sizeof(tz->StandardName)); /* StandardDate */ - rdp_write_system_time(s, &tz->StandardDate); + if (!rdp_write_system_time(s, &tz->StandardDate)) + return FALSE; + #ifdef WITH_DEBUG_TIMEZONE WLog_DBG(TIMEZONE_TAG, "bias=%" PRId32 "", tz->Bias); WLog_DBG(TIMEZONE_TAG, "StandardName:"); @@ -139,14 +160,19 @@ BOOL rdp_write_client_time_zone(wStream* s, rdpSettings* settings) #endif /* Note that StandardBias is ignored if no valid standardDate is provided. */ /* StandardBias */ + if (!Stream_EnsureRemainingCapacity(s, 4ull + sizeof(tz->DaylightName))) + return FALSE; Stream_Write_UINT32(s, tz->StandardBias); DEBUG_TIMEZONE("StandardBias=%" PRId32 "", tz->StandardBias); /* daylightName (64 bytes) */ Stream_Write(s, tz->DaylightName, sizeof(tz->DaylightName)); /* DaylightDate */ - rdp_write_system_time(s, &tz->DaylightDate); + if (!rdp_write_system_time(s, &tz->DaylightDate)) + return FALSE; /* Note that DaylightBias is ignored if no valid daylightDate is provided. */ /* DaylightBias */ + if (!Stream_EnsureRemainingCapacity(s, 4ull)) + return FALSE; Stream_Write_UINT32(s, tz->DaylightBias); DEBUG_TIMEZONE("DaylightBias=%" PRId32 "", tz->DaylightBias); return TRUE; diff --git a/libfreerdp/core/update.c b/libfreerdp/core/update.c index ebb82fc2df7e..448726ecf77d 100644 --- a/libfreerdp/core/update.c +++ b/libfreerdp/core/update.c @@ -99,6 +99,13 @@ static BOOL update_read_bitmap_data(rdpUpdate* update, wStream* s, BITMAP_DATA* Stream_Read_UINT16(s, bitmapData->flags); Stream_Read_UINT16(s, bitmapData->bitmapLength); + if ((bitmapData->width == 0) || (bitmapData->height == 0)) + { + WLog_ERR(TAG, "Invalid BITMAP_DATA: width=%" PRIu16 ", height=%" PRIu16, bitmapData->width, + bitmapData->height); + return FALSE; + } + if (bitmapData->flags & BITMAP_COMPRESSION) { if (!(bitmapData->flags & NO_BITMAP_COMPRESSION_HDR)) @@ -924,6 +931,7 @@ static BOOL _update_begin_paint(rdpContext* context) return FALSE; Stream_SealLength(s); + Stream_GetLength(s, update->offsetOrders); Stream_Seek(s, 2); /* numberOrders (2 bytes) */ update->combineUpdates = TRUE; update->numberOrders = 0; @@ -934,16 +942,14 @@ static BOOL _update_begin_paint(rdpContext* context) static BOOL _update_end_paint(rdpContext* context) { wStream* s; - int headerLength; rdpUpdate* update = context->update; if (!update->us) return FALSE; s = update->us; - headerLength = Stream_Length(s); Stream_SealLength(s); - Stream_SetPosition(s, headerLength); + Stream_SetPosition(s, update->offsetOrders); Stream_Write_UINT16(s, update->numberOrders); /* numberOrders (2 bytes) */ Stream_SetPosition(s, Stream_Length(s)); @@ -955,6 +961,7 @@ static BOOL _update_end_paint(rdpContext* context) update->combineUpdates = FALSE; update->numberOrders = 0; + update->offsetOrders = 0; update->us = NULL; Stream_Free(s, TRUE); return TRUE; diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c new file mode 100644 index 000000000000..15cd655981cf --- /dev/null +++ b/libfreerdp/core/utils.c @@ -0,0 +1,68 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Terminal Server Gateway (utils) + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include +#define TAG FREERDP_TAG("core.gateway.utils") + +#include "utils.h" + +BOOL utils_str_copy(const char* value, char** dst) +{ + WINPR_ASSERT(dst); + + free(*dst); + *dst = NULL; + if (!value) + return TRUE; + + (*dst) = _strdup(value); + return (*dst) != NULL; +} + +BOOL utils_abort_connect(rdpContext* context) +{ + WINPR_ASSERT(context); + if (context->abortEvent) + return SetEvent(context->abortEvent); + return FALSE; +} + +BOOL utils_reset_abort(rdpContext* context) +{ + WINPR_ASSERT(context); + return ResetEvent(context->abortEvent); +} + +BOOL utils_str_is_empty(const char* str) +{ + if (!str) + return TRUE; + if (strlen(str) == 0) + return TRUE; + return FALSE; +} diff --git a/libfreerdp/core/utils.h b/libfreerdp/core/utils.h new file mode 100644 index 000000000000..432951812f5e --- /dev/null +++ b/libfreerdp/core/utils.h @@ -0,0 +1,33 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Terminal Server Gateway (utils) + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_LIB_CORE_UTILS_H +#define FREERDP_LIB_CORE_UTILS_H + +#include +#include + +BOOL utils_reset_abort(rdpContext* context); +BOOL utils_abort_connect(rdpContext* context); + +BOOL utils_str_is_empty(const char* str); +BOOL utils_str_copy(const char* value, char** dst); + +#endif /* FREERDP_LIB_CORE_UTILS_H */ diff --git a/libfreerdp/core/window.c b/libfreerdp/core/window.c index 51411c8c26f6..cb0acb9a8bf7 100644 --- a/libfreerdp/core/window.c +++ b/libfreerdp/core/window.c @@ -359,33 +359,31 @@ static BOOL update_read_window_state_order(wStream* s, WINDOW_ORDER_INFO* orderI Stream_Read_UINT16(s, windowState->numWindowRects); /* numWindowRects (2 bytes) */ - if (windowState->numWindowRects == 0) + if (windowState->numWindowRects > 0) { - return TRUE; - } + size = sizeof(RECTANGLE_16) * windowState->numWindowRects; + newRect = (RECTANGLE_16*)realloc(windowState->windowRects, size); - size = sizeof(RECTANGLE_16) * windowState->numWindowRects; - newRect = (RECTANGLE_16*)realloc(windowState->windowRects, size); - - if (!newRect) - { - free(windowState->windowRects); - windowState->windowRects = NULL; - return FALSE; - } + if (!newRect) + { + free(windowState->windowRects); + windowState->windowRects = NULL; + return FALSE; + } - windowState->windowRects = newRect; + windowState->windowRects = newRect; - if (Stream_GetRemainingLength(s) < 8 * windowState->numWindowRects) - return FALSE; + if (Stream_GetRemainingLength(s) < 8 * windowState->numWindowRects) + return FALSE; - /* windowRects */ - for (i = 0; i < windowState->numWindowRects; i++) - { - Stream_Read_UINT16(s, windowState->windowRects[i].left); /* left (2 bytes) */ - Stream_Read_UINT16(s, windowState->windowRects[i].top); /* top (2 bytes) */ - Stream_Read_UINT16(s, windowState->windowRects[i].right); /* right (2 bytes) */ - Stream_Read_UINT16(s, windowState->windowRects[i].bottom); /* bottom (2 bytes) */ + /* windowRects */ + for (i = 0; i < windowState->numWindowRects; i++) + { + Stream_Read_UINT16(s, windowState->windowRects[i].left); /* left (2 bytes) */ + Stream_Read_UINT16(s, windowState->windowRects[i].top); /* top (2 bytes) */ + Stream_Read_UINT16(s, windowState->windowRects[i].right); /* right (2 bytes) */ + Stream_Read_UINT16(s, windowState->windowRects[i].bottom); /* bottom (2 bytes) */ + } } } diff --git a/libfreerdp/crypto/crypto.c b/libfreerdp/crypto/crypto.c index 82df956c2669..69255c94530f 100644 --- a/libfreerdp/crypto/crypto.c +++ b/libfreerdp/crypto/crypto.c @@ -21,8 +21,10 @@ #include "config.h" #endif +#include + #include -#include +#include #include #include @@ -943,3 +945,56 @@ void crypto_cert_print_info(X509* xcert) free(issuer); free(subject); } + +WINPR_MD_TYPE crypto_cert_get_signature_alg(X509* xcert) +{ + WINPR_ASSERT(xcert); + + EVP_PKEY* evp = X509_get_pubkey(xcert); + WINPR_ASSERT(evp); + + int hash_nid = 0; + const int res = EVP_PKEY_get_default_digest_nid(evp, &hash_nid); + EVP_PKEY_free(evp); + if (res <= 0) + return WINPR_MD_NONE; + + switch (hash_nid) + { + case NID_md2: + return WINPR_MD_MD2; + case NID_md4: + return WINPR_MD_MD4; + case NID_md5: + return WINPR_MD_MD5; + case NID_sha1: + return WINPR_MD_SHA1; + case NID_sha224: + return WINPR_MD_SHA224; + case NID_sha256: + return WINPR_MD_SHA256; + case NID_sha384: + return WINPR_MD_SHA384; + case NID_sha512: + return WINPR_MD_SHA512; + case NID_ripemd160: + return WINPR_MD_RIPEMD160; +#if (OPENSSL_VERSION_NUMBER >= 0x1010101fL) && !defined(LIBRESSL_VERSION_NUMBER) + case NID_sha3_224: + return WINPR_MD_SHA3_224; + case NID_sha3_256: + return WINPR_MD_SHA3_256; + case NID_sha3_384: + return WINPR_MD_SHA3_384; + case NID_sha3_512: + return WINPR_MD_SHA3_512; + case NID_shake128: + return WINPR_MD_SHAKE128; + case NID_shake256: + return WINPR_MD_SHAKE256; +#endif + case NID_undef: + default: + return WINPR_MD_NONE; + } +} diff --git a/libfreerdp/crypto/opensslcompat.c b/libfreerdp/crypto/opensslcompat.c index 701902dc29ee..314d6b51f61d 100644 --- a/libfreerdp/crypto/opensslcompat.c +++ b/libfreerdp/crypto/opensslcompat.c @@ -19,7 +19,8 @@ #include "opensslcompat.h" -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) BIO_METHOD* BIO_meth_new(int type, const char* name) { diff --git a/libfreerdp/crypto/opensslcompat.h b/libfreerdp/crypto/opensslcompat.h index 169e8e4c28f8..3ad5b6bd48a9 100644 --- a/libfreerdp/crypto/opensslcompat.h +++ b/libfreerdp/crypto/opensslcompat.h @@ -30,7 +30,8 @@ #include -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) #include #include diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index d72998ab216a..29a17e2dcc33 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -77,6 +77,7 @@ struct _BIO_RDP_TLS }; typedef struct _BIO_RDP_TLS BIO_RDP_TLS; +static BOOL tls_prep(rdpTls* tls, BIO* underlying, int options, BOOL clientMode); static int tls_verify_certificate(rdpTls* tls, CryptoCert cert, const char* hostname, UINT16 port); static void tls_print_certificate_name_mismatch_error(const char* hostname, UINT16 port, const char* common_name, char** alt_names, @@ -323,7 +324,8 @@ static long bio_rdp_tls_ctrl(BIO* bio, int cmd, long num, void* ptr) case BIO_CTRL_PUSH: if (next_bio && (next_bio != ssl_rbio)) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) SSL_set_bio(tls->ssl, next_bio, next_bio); CRYPTO_add(&(bio->next_bio->references), 1, CRYPTO_LOCK_BIO); #else @@ -347,7 +349,8 @@ static long bio_rdp_tls_ctrl(BIO* bio, int cmd, long num, void* ptr) if (ssl_rbio != ssl_wbio) BIO_free_all(ssl_wbio); -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) if (next_bio) CRYPTO_add(&(bio->next_bio->references), -1, CRYPTO_LOCK_BIO); @@ -387,7 +390,8 @@ static long bio_rdp_tls_ctrl(BIO* bio, int cmd, long num, void* ptr) BIO_push(ssl_rbio, next_bio); BIO_set_next(bio, ssl_rbio); -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) CRYPTO_add(&(ssl_rbio->references), 1, CRYPTO_LOCK_BIO); #else BIO_up_ref(ssl_rbio); @@ -613,7 +617,25 @@ static SecPkgContext_Bindings* tls_get_channel_bindings(X509* cert) SEC_CHANNEL_BINDINGS* ChannelBindings; SecPkgContext_Bindings* ContextBindings; const size_t PrefixLength = strnlen(TLS_SERVER_END_POINT, ARRAYSIZE(TLS_SERVER_END_POINT)); - BYTE* CertificateHash = crypto_cert_hash(cert, "sha256", &CertificateHashLength); + + /* See https://www.rfc-editor.org/rfc/rfc5929 for details about hashes */ + WINPR_MD_TYPE alg = crypto_cert_get_signature_alg(cert); + const char* hash; + switch (alg) + { + + case WINPR_MD_MD5: + case WINPR_MD_SHA1: + hash = winpr_md_type_to_string(WINPR_MD_SHA256); + break; + default: + hash = winpr_md_type_to_string(alg); + break; + } + if (!hash) + return NULL; + + BYTE* CertificateHash = crypto_cert_hash(cert, hash, &CertificateHashLength); if (!CertificateHash) return NULL; @@ -664,8 +686,18 @@ static BOOL tls_prepare(rdpTls* tls, BIO* underlying, SSL_METHOD* method, int op SSL_CTX_set_options(tls->ctx, options); SSL_CTX_set_read_ahead(tls->ctx, 1); #if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) - SSL_CTX_set_min_proto_version(tls->ctx, TLS1_VERSION); /* min version */ - SSL_CTX_set_max_proto_version(tls->ctx, 0); /* highest supported version by library */ + UINT16 version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMinVersion); + if (!SSL_CTX_set_min_proto_version(tls->ctx, version)) + { + WLog_ERR(TAG, "SSL_CTX_set_min_proto_version %s failed", version); + return FALSE; + } + version = freerdp_settings_get_uint16(settings, FreeRDP_TLSMaxVersion); + if (!SSL_CTX_set_max_proto_version(tls->ctx, version)) + { + WLog_ERR(TAG, "SSL_CTX_set_max_proto_version %s failed", version); + return FALSE; + } #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) SSL_CTX_set_security_level(tls->ctx, settings->TlsSecLevel); @@ -852,6 +884,18 @@ int tls_connect(rdpTls* tls, BIO* underlying) * support empty fragments. This needs to be disabled. */ options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + + if (!tls_prep(tls, underlying, options, TRUE)) + return 0; + +#if !defined(OPENSSL_NO_TLSEXT) && !defined(LIBRESSL_VERSION_NUMBER) + SSL_set_tlsext_host_name(tls->ssl, tls->hostname); +#endif + return tls_do_handshake(tls, TRUE); +} + +BOOL tls_prep(rdpTls* tls, BIO* underlying, int options, BOOL clientMode) +{ #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) /** * disable SSLv2 and SSLv3 @@ -859,16 +903,10 @@ int tls_connect(rdpTls* tls, BIO* underlying) options |= SSL_OP_NO_SSLv2; options |= SSL_OP_NO_SSLv3; - if (!tls_prepare(tls, underlying, SSLv23_client_method(), options, TRUE)) + return tls_prepare(tls, underlying, SSLv23_client_method(), options, clientMode); #else - if (!tls_prepare(tls, underlying, TLS_client_method(), options, TRUE)) -#endif - return FALSE; - -#if !defined(OPENSSL_NO_TLSEXT) && !defined(LIBRESSL_VERSION_NUMBER) - SSL_set_tlsext_host_name(tls->ssl, tls->hostname); + return tls_prepare(tls, underlying, TLS_client_method(), options, clientMode); #endif - return tls_do_handshake(tls, TRUE); } #if defined(MICROSOFT_IOS_SNI_BUG) && !defined(OPENSSL_NO_TLSEXT) && \ diff --git a/libfreerdp/gdi/bitmap.c b/libfreerdp/gdi/bitmap.c index 8fde46556b53..cac815881615 100644 --- a/libfreerdp/gdi/bitmap.c +++ b/libfreerdp/gdi/bitmap.c @@ -148,7 +148,7 @@ HGDI_BITMAP gdi_CreateCompatibleBitmap(HGDI_DC hdc, UINT32 nWidth, UINT32 nHeigh hBitmap->width = nWidth; hBitmap->height = nHeight; hBitmap->data = - _aligned_malloc(nWidth * nHeight * GetBytesPerPixel(hBitmap->format) * 1ULL, 16); + _aligned_malloc(1ull * nWidth * nHeight * GetBytesPerPixel(hBitmap->format), 16); hBitmap->free = _aligned_free; if (!hBitmap->data) diff --git a/libfreerdp/gdi/gdi.c b/libfreerdp/gdi/gdi.c index 5ca905b01efd..37a010431405 100644 --- a/libfreerdp/gdi/gdi.c +++ b/libfreerdp/gdi/gdi.c @@ -997,16 +997,46 @@ static BOOL gdi_surface_frame_marker(rdpContext* context, return TRUE; } +static BOOL intersect_rect(const rdpGdi* gdi, const SURFACE_BITS_COMMAND* cmd, RECTANGLE_16* prect) +{ + const UINT32 w = (const UINT32)gdi->width; + const UINT32 h = (const UINT32)gdi->height; + + if (cmd->destLeft > w) + return FALSE; + if (cmd->destRight > w) + return FALSE; + if (cmd->destLeft > cmd->destRight) + return FALSE; + if (cmd->destRight > UINT16_MAX) + return FALSE; + + if (cmd->destTop > h) + return FALSE; + if (cmd->destBottom > h) + return FALSE; + if (cmd->destTop > cmd->destBottom) + return FALSE; + if (cmd->destBottom > UINT16_MAX) + return FALSE; + + prect->left = (const UINT16)cmd->destLeft; + prect->top = (const UINT16)cmd->destTop; + prect->right = MIN((UINT16)cmd->destRight, prect->left + cmd->bmp.width); + prect->bottom = MIN((UINT16)cmd->destBottom, prect->top + cmd->bmp.height); + return TRUE; +} + static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cmd) { BOOL result = FALSE; DWORD format; - rdpGdi* gdi; + rdpGdi* gdi = NULL; size_t size; REGION16 region; - RECTANGLE_16 cmdRect; - UINT32 i, nbRects; - const RECTANGLE_16* rects; + RECTANGLE_16 cmdRect = { 0 }; + UINT32 nbRects; + const RECTANGLE_16* rects = NULL; if (!context || !cmd) return FALSE; @@ -1020,16 +1050,15 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, cmd->bmp.bpp, cmd->bmp.flags, cmd->bmp.codecID, cmd->bmp.width, cmd->bmp.height, cmd->bmp.bitmapDataLength); region16_init(®ion); - cmdRect.left = cmd->destLeft; - cmdRect.top = cmd->destTop; - cmdRect.right = cmdRect.left + cmd->bmp.width; - cmdRect.bottom = cmdRect.top + cmd->bmp.height; + + if (!intersect_rect(gdi, cmd, &cmdRect)) + goto out; switch (cmd->bmp.codecID) { case RDP_CODEC_ID_REMOTEFX: if (!rfx_process_message(context->codecs->rfx, cmd->bmp.bitmapData, - cmd->bmp.bitmapDataLength, cmd->destLeft, cmd->destTop, + cmd->bmp.bitmapDataLength, cmdRect.left, cmdRect.top, gdi->primary_buffer, gdi->dstFormat, gdi->stride, gdi->height, ®ion)) { @@ -1042,11 +1071,11 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm case RDP_CODEC_ID_NSCODEC: format = gdi->dstFormat; - if (!nsc_process_message(context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, - cmd->bmp.height, cmd->bmp.bitmapData, - cmd->bmp.bitmapDataLength, gdi->primary_buffer, format, - gdi->stride, cmd->destLeft, cmd->destTop, cmd->bmp.width, - cmd->bmp.height, FREERDP_FLIP_VERTICAL)) + if (!nsc_process_message( + context->codecs->nsc, cmd->bmp.bpp, cmd->bmp.width, cmd->bmp.height, + cmd->bmp.bitmapData, cmd->bmp.bitmapDataLength, gdi->primary_buffer, format, + gdi->stride, cmdRect.left, cmdRect.top, cmdRect.right - cmdRect.left, + cmdRect.bottom - cmdRect.top, FREERDP_FLIP_VERTICAL)) { WLog_ERR(TAG, "Failed to process NSCodec message"); goto out; @@ -1057,17 +1086,18 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm case RDP_CODEC_ID_NONE: format = gdi_get_pixel_format(cmd->bmp.bpp); - size = cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format) * 1ULL; + size = 1ull * cmd->bmp.width * cmd->bmp.height * GetBytesPerPixel(format); if (size > cmd->bmp.bitmapDataLength) { WLog_ERR(TAG, "Short nocodec message: got %" PRIu32 " bytes, require %" PRIuz, cmd->bmp.bitmapDataLength, size); goto out; } - if (!freerdp_image_copy(gdi->primary_buffer, gdi->dstFormat, gdi->stride, cmd->destLeft, - cmd->destTop, cmd->bmp.width, cmd->bmp.height, - cmd->bmp.bitmapData, format, 0, 0, 0, &gdi->palette, - FREERDP_FLIP_VERTICAL)) + + if (!freerdp_image_copy(gdi->primary_buffer, gdi->dstFormat, gdi->stride, cmdRect.left, + cmdRect.top, cmdRect.right - cmdRect.left, + cmdRect.bottom - cmdRect.top, cmd->bmp.bitmapData, format, 0, 0, + 0, &gdi->palette, FREERDP_FLIP_VERTICAL)) { WLog_ERR(TAG, "Failed to process nocodec message"); goto out; @@ -1084,7 +1114,7 @@ static BOOL gdi_surface_bits(rdpContext* context, const SURFACE_BITS_COMMAND* cm if (!(rects = region16_rects(®ion, &nbRects))) goto out; - for (i = 0; i < nbRects; i++) + for (UINT32 i = 0; i < nbRects; i++) { UINT32 left = rects[i].left; UINT32 top = rects[i].top; diff --git a/libfreerdp/gdi/gfx.c b/libfreerdp/gdi/gfx.c index a3b7505c51ba..3970715e0187 100644 --- a/libfreerdp/gdi/gfx.c +++ b/libfreerdp/gdi/gfx.c @@ -726,7 +726,7 @@ static UINT gdi_SurfaceCommand_Alpha(rdpGdi* gdi, RdpgfxClientContext* context, { UINT32 x, y; - if (Stream_GetRemainingLength(&s) < cmd->height * cmd->width * 1ULL) + if (Stream_GetRemainingLength(&s) < 1ull * cmd->height * cmd->width) return ERROR_INVALID_DATA; for (y = cmd->top; y < cmd->top + cmd->height; y++) @@ -1025,7 +1025,7 @@ static UINT gdi_CreateSurface(RdpgfxClientContext* context, } surface->scanline = gfx_align_scanline(surface->width * 4UL, 16); - surface->data = (BYTE*)_aligned_malloc(surface->scanline * surface->height * 1ULL, 16); + surface->data = (BYTE*)_aligned_malloc(1ull * surface->scanline * surface->height, 16); if (!surface->data) { @@ -1079,6 +1079,28 @@ static UINT gdi_DeleteSurface(RdpgfxClientContext* context, return rc; } +static BOOL intersect_rect(const RECTANGLE_16* rect, const gdiGfxSurface* surface, + RECTANGLE_16* prect) +{ + WINPR_ASSERT(rect); + WINPR_ASSERT(surface); + WINPR_ASSERT(prect); + + if (rect->left > rect->right) + return FALSE; + if (rect->left > surface->width) + return FALSE; + if (rect->top > rect->bottom) + return FALSE; + if (rect->top > surface->height) + return FALSE; + prect->left = rect->left; + prect->top = rect->top; + prect->right = MIN(rect->right, surface->width); + prect->bottom = MIN(rect->bottom, surface->height); + return TRUE; +} + /** * Function description * @@ -1087,40 +1109,36 @@ static UINT gdi_DeleteSurface(RdpgfxClientContext* context, static UINT gdi_SolidFill(RdpgfxClientContext* context, const RDPGFX_SOLID_FILL_PDU* solidFill) { UINT status = ERROR_INTERNAL_ERROR; - UINT16 index; - UINT32 color; - BYTE a, r, g, b; - UINT32 nWidth, nHeight; - RECTANGLE_16* rect; - gdiGfxSurface* surface; - RECTANGLE_16 invalidRect; + BYTE a = 0; + RECTANGLE_16 invalidRect = { 0 }; rdpGdi* gdi = (rdpGdi*)context->custom; + EnterCriticalSection(&context->mux); - surface = (gdiGfxSurface*)context->GetSurfaceData(context, solidFill->surfaceId); + + WINPR_ASSERT(context->GetSurfaceData); + gdiGfxSurface* surface = (gdiGfxSurface*)context->GetSurfaceData(context, solidFill->surfaceId); if (!surface) goto fail; - b = solidFill->fillPixel.B; - g = solidFill->fillPixel.G; - r = solidFill->fillPixel.R; - /* a = solidFill->fillPixel.XA; - * Ignore alpha channel, this is a solid fill. */ + const BYTE b = solidFill->fillPixel.B; + const BYTE g = solidFill->fillPixel.G; + const BYTE r = solidFill->fillPixel.R; a = 0xFF; - color = FreeRDPGetColor(surface->format, r, g, b, a); + const UINT32 color = FreeRDPGetColor(surface->format, r, g, b, a); - for (index = 0; index < solidFill->fillRectCount; index++) + for (UINT16 index = 0; index < solidFill->fillRectCount; index++) { - rect = &(solidFill->fillRects[index]); - nWidth = rect->right - rect->left; - nHeight = rect->bottom - rect->top; - invalidRect.left = rect->left; - invalidRect.top = rect->top; - invalidRect.right = rect->right; - invalidRect.bottom = rect->bottom; - - if (!freerdp_image_fill(surface->data, surface->format, surface->scanline, rect->left, - rect->top, nWidth, nHeight, color)) + const RECTANGLE_16* rect = &(solidFill->fillRects[index]); + + if (!intersect_rect(rect, surface, &invalidRect)) + goto fail; + + const UINT32 nWidth = invalidRect.right - invalidRect.left; + const UINT32 nHeight = invalidRect.bottom - invalidRect.top; + + if (!freerdp_image_fill(surface->data, surface->format, surface->scanline, invalidRect.left, + invalidRect.top, nWidth, nHeight, color)) goto fail; region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect); diff --git a/libfreerdp/gdi/graphics.c b/libfreerdp/gdi/graphics.c index c8b5fc9132a3..479eac9b83f3 100644 --- a/libfreerdp/gdi/graphics.c +++ b/libfreerdp/gdi/graphics.c @@ -52,7 +52,7 @@ HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 nWidth, UINT32 nHeight, UINT32 return NULL; nDstStep = nWidth * GetBytesPerPixel(gdi->dstFormat); - pDstData = _aligned_malloc(nHeight * nDstStep * 1ULL, 16); + pDstData = _aligned_malloc(1ull * nHeight * nDstStep, 16); if (!pDstData) return NULL; diff --git a/libfreerdp/gdi/shape.c b/libfreerdp/gdi/shape.c index 6c45e0c92b0b..51a8a36dd251 100644 --- a/libfreerdp/gdi/shape.c +++ b/libfreerdp/gdi/shape.c @@ -158,7 +158,7 @@ BOOL gdi_FillRect(HGDI_DC hdc, const HGDI_RECT rect, HGDI_BRUSH hbr) for (y = 1; y < nHeight; y++) { BYTE* dstp = gdi_get_bitmap_pointer(hdc, nXDest, nYDest + y); - memcpy(dstp, srcp, nWidth * formatSize * 1ULL); + memcpy(dstp, srcp, 1ull * nWidth * formatSize); } break; diff --git a/libfreerdp/gdi/video.c b/libfreerdp/gdi/video.c index 0e12c52e7a6f..8c74f0ae3674 100644 --- a/libfreerdp/gdi/video.c +++ b/libfreerdp/gdi/video.c @@ -66,7 +66,7 @@ static VideoSurface* gdiVideoCreateSurface(VideoClientContext* video, BYTE* data ret->base.w = width; ret->base.h = height; ret->scanline = width * bpp; - ret->image = _aligned_malloc(ret->scanline * height * 1ULL, 16); + ret->image = _aligned_malloc(1ull * ret->scanline * height, 16); if (!ret->image) { diff --git a/libfreerdp/locale/CMakeLists.txt b/libfreerdp/locale/CMakeLists.txt index 1a300f22f2ab..9c74e20dcd9c 100644 --- a/libfreerdp/locale/CMakeLists.txt +++ b/libfreerdp/locale/CMakeLists.txt @@ -38,10 +38,20 @@ set(${MODULE_PREFIX}_SUN_SRCS keyboard_sun.c keyboard_sun.h) +set(${MODULE_PREFIX}_APPLE_SRCS + keyboard_apple.c + keyboard_apple.h) + if(CMAKE_SYSTEM_NAME MATCHES Solaris) set(WITH_SUN true) endif() +if(APPLE AND (NOT IOS)) + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${${MODULE_PREFIX}_APPLE_SRCS}) + find_library(CARBON Carbon) + freerdp_library_add(${CARBON}) +endif() + if(WITH_X11) freerdp_definition_add(-DWITH_X11) freerdp_include_directory_add(${X11_INCLUDE_DIRS}) diff --git a/libfreerdp/locale/keyboard.c b/libfreerdp/locale/keyboard.c index eca0d635cd8c..4c5ae03e49e9 100644 --- a/libfreerdp/locale/keyboard.c +++ b/libfreerdp/locale/keyboard.c @@ -32,6 +32,10 @@ #include "liblocale.h" +#if defined(__MACOSX__) +#include "keyboard_apple.h" +#endif + #ifdef WITH_X11 #include "keyboard_x11.h" @@ -64,6 +68,11 @@ int freerdp_detect_keyboard(DWORD* keyboardLayoutId) *keyboardLayoutId = ((DWORD)GetKeyboardLayout(0) >> 16) & 0x0000FFFF; #endif +#if defined(__MACOSX__) + if (*keyboardLayoutId == 0) + freerdp_detect_keyboard_layout_from_cf(keyboardLayoutId); +#endif + #ifdef WITH_X11 if (*keyboardLayoutId == 0) freerdp_detect_keyboard_layout_from_xkb(keyboardLayoutId); @@ -148,7 +157,7 @@ DWORD freerdp_keyboard_init(DWORD keyboardLayoutId) for (keycode = 0; keycode < ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE); keycode++) { VIRTUAL_SCANCODE_TO_X11_KEYCODE - [RDP_SCANCODE_CODE(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode])] + [RDP_SCANCODE_CODE(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode])] [RDP_SCANCODE_EXTENDED(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]) ? 1 : 0] = keycode; } diff --git a/libfreerdp/locale/keyboard_apple.c b/libfreerdp/locale/keyboard_apple.c new file mode 100644 index 000000000000..d0d59216e52e --- /dev/null +++ b/libfreerdp/locale/keyboard_apple.c @@ -0,0 +1,244 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Apple Core Foundation Keyboard Mapping + * + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "liblocale.h" + +#include +#include + +#include "keyboard_apple.h" + +struct KEYBOARD_LAYOUT_MAPPING_ +{ + const char* inputSourceId; /* Apple input source id (com.apple.keylayout or inputmethod) */ + DWORD code; /* mapped rdp keyboard layout id */ +}; +typedef struct KEYBOARD_LAYOUT_MAPPING_ KEYBOARD_LAYOUT_MAPPING; + +static const KEYBOARD_LAYOUT_MAPPING KEYBOARD_MAPPING_TABLE[] = { + { "com.apple.inputmethod.Kotoeri.Japanese", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.FullWidthRoman", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.HalfWidthKana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.Katakana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Katakana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Roman", JAPANESE }, + { "com.apple.inputmethod.kotoeri.Ainu", JAPANESE }, + { "com.apple.keylayout.2SetHangul", KOREAN }, + { "com.apple.keylayout.390Hangul", KOREAN }, + { "com.apple.keylayout.3SetHangul", KOREAN }, + { "com.apple.keylayout.AfghanDari", DARI }, + { "com.apple.keylayout.AfghanPashto", PASHTO }, + { "com.apple.keylayout.AfghanUzbek", UZBEK_LATIN }, + { "com.apple.keylayout.Arabic", ARABIC_EGYPT }, + { "com.apple.keylayout.Arabic-QWERTY", ARABIC_EGYPT }, + { "com.apple.keylayout.ArabicPC", ARABIC_EGYPT }, + { "com.apple.keylayout.Armenian-HMQWERTY", ARMENIAN }, + { "com.apple.keylayout.Armenian-WesternQWERTY", ARMENIAN }, + { "com.apple.keylayout.Australian", ENGLISH_AUSTRALIAN }, + { "com.apple.keylayout.Austrian", GERMAN_AUSTRIAN }, + { "com.apple.keylayout.Azeri", AZERI_LATIN }, + { "com.apple.keylayout.Bangla", BENGALI_INDIA }, + { "com.apple.keylayout.Bangla-QWERTY", BENGALI_INDIA }, + { "com.apple.keylayout.Belgian", DUTCH_BELGIAN }, + { "com.apple.keylayout.Brazilian", PORTUGUESE_BRAZILIAN }, + { "com.apple.keylayout.British", ENGLISH_UNITED_KINGDOM }, + { "com.apple.keylayout.British-PC", ENGLISH_UNITED_KINGDOM }, + { "com.apple.keylayout.Bulgarian", BULGARIAN }, + { "com.apple.keylayout.Bulgarian-Phonetic", BULGARIAN }, + { "com.apple.keylayout.Byelorussian", BELARUSIAN }, + { "com.apple.keylayout.Canadian", ENGLISH_CANADIAN }, + { "com.apple.keylayout.Canadian-CSA", FRENCH_CANADIAN }, + { "com.apple.keylayout.CangjieKeyboard", CHINESE_TAIWAN }, + { "com.apple.keylayout.Cherokee-Nation", CHEROKEE }, + { "com.apple.keylayout.Cherokee-QWERTY", CHEROKEE }, + { "com.apple.keylayout.Colemak", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Croatian", CROATIAN }, + { "com.apple.keylayout.Croatian-PC", CROATIAN }, + { "com.apple.keylayout.Czech", CZECH }, + { "com.apple.keylayout.Czech-QWERTY", CZECH }, + { "com.apple.keylayout.DVORAK-QWERTYCMD", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Danish", DANISH }, + { "com.apple.keylayout.Devanagari", HINDI }, + { "com.apple.keylayout.Devanagari-QWERTY", HINDI }, + { "com.apple.keylayout.Dutch", DUTCH_STANDARD }, + { "com.apple.keylayout.Dvorak", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Dvorak-Left", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Dvorak-Right", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Estonian", ESTONIAN }, + { "com.apple.keylayout.Faroese", FAEROESE }, + { "com.apple.keylayout.Finnish", FINNISH }, + { "com.apple.keylayout.FinnishExtended", FINNISH }, + { "com.apple.keylayout.FinnishSami-PC", FINNISH }, + { "com.apple.keylayout.French", FRENCH_STANDARD }, + { "com.apple.keylayout.French-PC", FRENCH_STANDARD }, + { "com.apple.keylayout.French-numerical", FRENCH_STANDARD }, + { "com.apple.keylayout.GJCRomaja", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Georgian-QWERTY", GEORGIAN }, + { "com.apple.keylayout.German", GERMAN_STANDARD }, + { "com.apple.keylayout.Greek", GREEK }, + { "com.apple.keylayout.GreekPolytonic", GREEK }, + { "com.apple.keylayout.Gujarati", GUJARATI }, + { "com.apple.keylayout.Gujarati-QWERTY", GUJARATI }, + { "com.apple.keylayout.Gurmukhi", PUNJABI }, + { "com.apple.keylayout.Gurmukhi-QWERTY", PUNJABI }, + { "com.apple.keylayout.HNCRomaja", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Hawaiian", HAWAIIAN }, + { "com.apple.keylayout.Hebrew", HEBREW }, + { "com.apple.keylayout.Hebrew-PC", HEBREW }, + { "com.apple.keylayout.Hebrew-QWERTY", HEBREW }, + { "com.apple.keylayout.Hungarian", HUNGARIAN }, + { "com.apple.keylayout.Hungarian-QWERTY", HUNGARIAN }, + { "com.apple.keylayout.Icelandic", ICELANDIC }, + { "com.apple.keylayout.Inuktitut-Nunavut", INUKTITUT }, + { "com.apple.keylayout.Inuktitut-Nutaaq", INUKTITUT }, + { "com.apple.keylayout.Inuktitut-QWERTY", INUKTITUT }, + { "com.apple.keylayout.InuttitutNunavik", INUKTITUT }, + { "com.apple.keylayout.Irish", ENGLISH_IRELAND }, + { "com.apple.keylayout.IrishExtended", IRISH }, + { "com.apple.keylayout.Italian", ITALIAN_STANDARD }, + { "com.apple.keylayout.Italian-Pro", ITALIAN_STANDARD }, + { "com.apple.keylayout.Jawi-QWERTY", ARABIC_SAUDI_ARABIA }, + { "com.apple.keylayout.Kannada", KANNADA }, + { "com.apple.keylayout.Kannada-QWERTY", KANNADA }, + { "com.apple.keylayout.Kazakh", KAZAKH }, + { "com.apple.keylayout.Khmer", KHMER }, + { "com.apple.keylayout.Latvian", LATVIAN }, + { "com.apple.keylayout.Lithuanian", LITHUANIAN }, + { "com.apple.keylayout.Macedonian", MACEDONIAN }, + { "com.apple.keylayout.Malayalam", MALAYALAM }, + { "com.apple.keylayout.Malayalam-QWERTY", MALAYALAM }, + { "com.apple.keylayout.Maltese", MALTESE }, + { "com.apple.keylayout.Maori", MAORI }, + { "com.apple.keylayout.Myanmar-QWERTY", MYANMAR }, + { "com.apple.keylayout.Nepali", NEPALI }, + { "com.apple.keylayout.NorthernSami", SAMI_NORTHERN_NORWAY }, + { "com.apple.keylayout.Norwegian", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.NorwegianExtended", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.NorwegianSami-PC", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.Oriya", ORIYA }, + { "com.apple.keylayout.Persian", FARSI }, + { "com.apple.keylayout.Persian-ISIRI2901", FARSI }, + { "com.apple.keylayout.Polish", POLISH }, + { "com.apple.keylayout.PolishPro", POLISH }, + { "com.apple.keylayout.Portuguese", PORTUGUESE_STANDARD }, + { "com.apple.keylayout.Romanian", ROMANIAN }, + { "com.apple.keylayout.Romanian-Standard", ROMANIAN }, + { "com.apple.keylayout.Russian", RUSSIAN }, + { "com.apple.keylayout.Russian-Phonetic", RUSSIAN }, + { "com.apple.keylayout.RussianWin", RUSSIAN }, + { "com.apple.keylayout.Sami-PC", SAMI_NORTHERN_SWEDEN }, + { "com.apple.keylayout.Serbian", SERBIAN_CYRILLIC_BOSNIA_HERZEGOVINA }, + { "com.apple.keylayout.Serbian-Latin", SERBIAN_LATIN_BOSNIA_HERZEGOVINA }, + { "com.apple.keylayout.Sinhala", SINHALA }, + { "com.apple.keylayout.Sinhala-QWERTY", SINHALA }, + { "com.apple.keylayout.Slovak", SLOVAK }, + { "com.apple.keylayout.Slovak-QWERTY", SLOVAK }, + { "com.apple.keylayout.Slovenian", SLOVENIAN }, + { "com.apple.keylayout.Spanish", SPANISH_TRADITIONAL_SORT }, + { "com.apple.keylayout.Spanish-ISO", SPANISH_MODERN_SORT }, + { "com.apple.keylayout.Swedish", SWEDISH }, + { "com.apple.keylayout.Swedish-Pro", SWEDISH }, + { "com.apple.keylayout.SwedishSami-PC", SWEDISH }, + { "com.apple.keylayout.SwissFrench", FRENCH_SWISS }, + { "com.apple.keylayout.SwissGerman", GERMAN_SWISS }, + { "com.apple.keylayout.Telugu", TELUGU }, + { "com.apple.keylayout.Telugu-QWERTY", TELUGU }, + { "com.apple.keylayout.Thai", THAI }, + { "com.apple.keylayout.Thai-PattaChote", THAI }, + { "com.apple.keylayout.Tibetan-QWERTY", TIBETAN_PRC }, + { "com.apple.keylayout.Tibetan-Wylie", TIBETAN_PRC }, + { "com.apple.keylayout.TibetanOtaniUS", TIBETAN_PRC }, + { "com.apple.keylayout.Turkish", TURKISH }, + { "com.apple.keylayout.Turkish-QWERTY", TURKISH }, + { "com.apple.keylayout.Turkish-QWERTY-PC", TURKISH }, + { "com.apple.keylayout.US", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.USExtended", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.USInternational-PC", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Ukrainian", UKRAINIAN }, + { "com.apple.keylayout.Ukrainian-PC", UKRAINIAN }, + { "com.apple.keylayout.UnicodeHexInput", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Urdu", URDU }, + { "com.apple.keylayout.Uyghur", UIGHUR }, + { "com.apple.keylayout.Vietnamese", VIETNAMESE }, + { "com.apple.keylayout.Welsh", WELSH } +}; + +int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId) +{ + int i; + CFIndex length; + char* inputSourceId = NULL; + CFStringRef inputSourceIdRef; + TISInputSourceRef inputSrc = TISCopyCurrentKeyboardLayoutInputSource(); + if (!inputSrc) + { + DEBUG_KBD("Failed to get current keyboard layout input source!"); + return 0; + } + + /* get current input source id */ + inputSourceIdRef = (CFStringRef)TISGetInputSourceProperty(inputSrc, kTISPropertyInputSourceID); + if (!inputSourceIdRef) + { + DEBUG_KBD("Failed to get input source id!"); + goto done; + } + + /* convert it to a C-string */ + length = CFStringGetLength(inputSourceIdRef); + length = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + inputSourceId = (char*)malloc(length); + if (!inputSourceId) + { + DEBUG_KBD("Failed to allocate string buffer!"); + goto done; + } + + if (!CFStringGetCString(inputSourceIdRef, inputSourceId, length, kCFStringEncodingUTF8)) + { + DEBUG_KBD("Failed to convert CFString to C-string!"); + goto done; + } + + /* Search for the id in the mapping table */ + for (i = 0; i < ARRAYSIZE(KEYBOARD_MAPPING_TABLE); ++i) + { + if (strcmp(inputSourceId, KEYBOARD_MAPPING_TABLE[i].inputSourceId) == 0) + { + *keyboardLayoutId = KEYBOARD_MAPPING_TABLE[i].code; + break; + } + } + +done: + free(inputSourceId); + CFRelease(inputSrc); + if (*keyboardLayoutId > 0) + return *keyboardLayoutId; + + return 0; +} diff --git a/libfreerdp/locale/keyboard_apple.h b/libfreerdp/locale/keyboard_apple.h new file mode 100644 index 000000000000..cd3c3e7beca7 --- /dev/null +++ b/libfreerdp/locale/keyboard_apple.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Apple Core Foundation Keyboard Mapping + * + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_LOCALE_KEYBOARD_APPLE_H +#define FREERDP_LOCALE_KEYBOARD_APPLE_H + +#include + +FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId); + +#endif /* FREERDP_LOCALE_KEYBOARD_APPLE_H */ diff --git a/libfreerdp/locale/locale.c b/libfreerdp/locale/locale.c index 97ae9e469645..e7c6d00782d9 100644 --- a/libfreerdp/locale/locale.c +++ b/libfreerdp/locale/locale.c @@ -3,6 +3,8 @@ * Microsoft Locales * * Copyright 2009-2012 Marc-Andre Moreau + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,22 +23,32 @@ #include "config.h" #endif +#if defined(__APPLE__) +#include +#include +#endif + #include #include #include #include +#include #include #include "liblocale.h" #include +#define LOCALE_LANGUAGE_LEN 6 +#define LOCALE_COUNTRY_LEN 10 + struct _SYSTEM_LOCALE { - char language[4]; /* Two or three letter language code */ - char country[10]; /* Two or three letter country code (Sometimes with Cyrl_ prefix) */ - DWORD code; /* 32-bit unsigned integer corresponding to the locale */ + char language[LOCALE_LANGUAGE_LEN]; /* Two or three letter language code */ + char country[LOCALE_COUNTRY_LEN]; /* Two or three letter country code (Sometimes with Cyrl_ + prefix) */ + DWORD code; /* 32-bit unsigned integer corresponding to the locale */ }; typedef struct _SYSTEM_LOCALE SYSTEM_LOCALE; @@ -642,77 +654,118 @@ static const LOCALE_KEYBOARD_LAYOUTS LOCALE_KEYBOARD_LAYOUTS_TABLE[] = { { XHOSA, { 0x00000409, 0x00000409, 0x0, 0x0, 0x0 } }, }; -static BOOL freerdp_get_system_language_and_country_codes(char* language, char* country) +static BOOL freerdp_get_system_language_and_country_codes(char* language, size_t languageLen, + char* country, size_t countryLen) { - int dot; - DWORD nSize; - int underscore; - char* env_lang = NULL; - LPCSTR lang = "LANG"; - /* LANG = _. */ - nSize = GetEnvironmentVariableA(lang, NULL, 0); - - if (!nSize) - return FALSE; /* LANG environment variable was not set */ - - env_lang = (char*)malloc(nSize); - - if (!env_lang) - return FALSE; + assert(language); + assert(languageLen > 0); + assert(country); + assert(countryLen); - if (GetEnvironmentVariableA(lang, env_lang, nSize) != - nSize - 1) /* Get locale from environment variable LANG */ +#if defined(__APPLE__) { - free(env_lang); - return FALSE; - } + CFIndex strSize; + CFStringRef langRef, countryRef; + CFLocaleRef localeRef = CFLocaleCopyCurrent(); + if (!localeRef) + return FALSE; + + langRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleLanguageCode); + countryRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleCountryCode); + if (!langRef || !countryRef) + { + CFRelease(localeRef); + return FALSE; + } - underscore = strcspn(env_lang, "_"); + if (!CFStringGetCString(langRef, language, languageLen, kCFStringEncodingUTF8) || + !CFStringGetCString(countryRef, country, countryLen, kCFStringEncodingUTF8)) + { + CFRelease(localeRef); + return FALSE; + } - if (underscore > 3) - { - free(env_lang); - return FALSE; /* The language name should not be more than 3 letters long */ + CFRelease(localeRef); + return TRUE; } - else +#else { - /* Get language code */ - strncpy(language, env_lang, underscore); - language[underscore] = '\0'; - } + int dot; + DWORD nSize; + int underscore; + char* env_lang = NULL; + LPCSTR lang = "LANG"; + /* LANG = _. */ + nSize = GetEnvironmentVariableA(lang, NULL, 0); - dot = strcspn(env_lang, "."); + if (!nSize) + return FALSE; /* LANG environment variable was not set */ + + env_lang = (char*)malloc(nSize); + + if (!env_lang) + return FALSE; + + if (GetEnvironmentVariableA(lang, env_lang, nSize) != + nSize - 1) /* Get locale from environment variable LANG */ + { + free(env_lang); + return FALSE; + } + + underscore = strcspn(env_lang, "_"); + + if (underscore > 3) + { + free(env_lang); + return FALSE; /* The language name should not be more than 3 letters long */ + } + else + { + /* Get language code */ + size_t len = MIN(languageLen - 1, underscore); + strncpy(language, env_lang, len); + language[len] = '\0'; + } + + dot = strcspn(env_lang, "."); + + /* Get country code */ + if (dot > underscore) + { + size_t len = MIN(countryLen - 1, dot - underscore - 1); + strncpy(country, &env_lang[underscore + 1], len); + country[len] = '\0'; + } + else + { + free(env_lang); + return FALSE; /* Invalid locale */ + } - /* Get country code */ - if (dot > underscore) - { - strncpy(country, &env_lang[underscore + 1], dot - underscore - 1); - country[dot - underscore - 1] = '\0'; - } - else - { free(env_lang); - return FALSE; /* Invalid locale */ + return TRUE; } - - free(env_lang); - return TRUE; +#endif } -static SYSTEM_LOCALE* freerdp_detect_system_locale(void) +static const SYSTEM_LOCALE* freerdp_detect_system_locale(void) { size_t i; - char language[4]; - char country[10]; - SYSTEM_LOCALE* locale = NULL; - freerdp_get_system_language_and_country_codes(language, country); + char language[LOCALE_LANGUAGE_LEN] = { 0 }; + char country[LOCALE_COUNTRY_LEN] = { 0 }; + const SYSTEM_LOCALE* locale = NULL; + + freerdp_get_system_language_and_country_codes(language, ARRAYSIZE(language), country, + ARRAYSIZE(country)); for (i = 0; i < ARRAYSIZE(SYSTEM_LOCALE_TABLE); i++) { - if ((strcmp(language, SYSTEM_LOCALE_TABLE[i].language) == 0) && - (strcmp(country, SYSTEM_LOCALE_TABLE[i].country) == 0)) + const SYSTEM_LOCALE* current = &SYSTEM_LOCALE_TABLE[i]; + + if ((strcmp(language, current->language) == 0) && (strcmp(country, current->country) == 0)) { - locale = (SYSTEM_LOCALE*)&SYSTEM_LOCALE_TABLE[i]; + locale = current; break; } } @@ -722,7 +775,7 @@ static SYSTEM_LOCALE* freerdp_detect_system_locale(void) DWORD freerdp_get_system_locale_id(void) { - SYSTEM_LOCALE* locale; + const SYSTEM_LOCALE* locale; locale = freerdp_detect_system_locale(); if (locale != NULL) @@ -737,8 +790,10 @@ const char* freerdp_get_system_locale_name_from_id(DWORD localeId) for (index = 0; index < ARRAYSIZE(LOCALE_NAME_TABLE); index++) { - if (localeId == LOCALE_NAME_TABLE[index].localeId) - return LOCALE_NAME_TABLE[index].name; + const LOCALE_NAME* const current = &LOCALE_NAME_TABLE[index]; + + if (localeId == current->localeId) + return current->name; } return NULL; @@ -747,10 +802,12 @@ const char* freerdp_get_system_locale_name_from_id(DWORD localeId) int freerdp_detect_keyboard_layout_from_system_locale(DWORD* keyboardLayoutId) { size_t i, j; - char language[4]; - char country[10]; - SYSTEM_LOCALE* locale; - freerdp_get_system_language_and_country_codes(language, country); + char language[LOCALE_LANGUAGE_LEN] = { 0 }; + char country[LOCALE_COUNTRY_LEN] = { 0 }; + const SYSTEM_LOCALE* locale; + + freerdp_get_system_language_and_country_codes(language, ARRAYSIZE(language), country, + ARRAYSIZE(country)); if ((strcmp(language, "C") == 0) || (strcmp(language, "POSIX") == 0)) { @@ -767,22 +824,24 @@ int freerdp_detect_keyboard_layout_from_system_locale(DWORD* keyboardLayoutId) for (i = 0; i < ARRAYSIZE(LOCALE_KEYBOARD_LAYOUTS_TABLE); i++) { - if (LOCALE_KEYBOARD_LAYOUTS_TABLE[i].locale == locale->code) + const LOCALE_KEYBOARD_LAYOUTS* const current = &LOCALE_KEYBOARD_LAYOUTS_TABLE[i]; + + if (current->locale == locale->code) { /* Locale found in list of default keyboard layouts */ for (j = 0; j < 5; j++) { - if (LOCALE_KEYBOARD_LAYOUTS_TABLE[i].keyboardLayouts[j] == ENGLISH_UNITED_STATES) + if (current->keyboardLayouts[j] == ENGLISH_UNITED_STATES) { continue; /* Skip, try to get a more localized keyboard layout */ } - else if (LOCALE_KEYBOARD_LAYOUTS_TABLE[i].keyboardLayouts[j] == 0) + else if (current->keyboardLayouts[j] == 0) { break; /* No more keyboard layouts */ } else { - *keyboardLayoutId = LOCALE_KEYBOARD_LAYOUTS_TABLE[i].keyboardLayouts[j]; + *keyboardLayoutId = current->keyboardLayouts[j]; return 0; } } diff --git a/libfreerdp/locale/xkb_layout_ids.c b/libfreerdp/locale/xkb_layout_ids.c index 223f7d10cae1..91922194891c 100644 --- a/libfreerdp/locale/xkb_layout_ids.c +++ b/libfreerdp/locale/xkb_layout_ids.c @@ -806,7 +806,7 @@ static const XKB_LAYOUT xkbLayouts[] = { { "sk", KBD_SLOVAK, sk_variants }, /* Slovakia */ { "es", KBD_SPANISH, es_variants }, /* Spain */ { "se", KBD_SWEDISH, se_variants }, /* Sweden */ - { "ch", KBD_SWISS_FRENCH, ch_variants }, /* Switzerland */ + { "ch", KBD_SWISS_GERMAN, ch_variants }, /* Switzerland */ { "sy", KBD_SYRIAC, sy_variants }, /* Syria */ { "tj", 0, tj_variants }, /* Tajikistan */ { "lk", 0, lk_variants }, /* Sri Lanka */ diff --git a/libfreerdp/primitives/prim_copy.c b/libfreerdp/primitives/prim_copy.c index ae841d0c478f..30c9cb15ed90 100644 --- a/libfreerdp/primitives/prim_copy.c +++ b/libfreerdp/primitives/prim_copy.c @@ -60,14 +60,14 @@ static BOOL memory_regions_overlap_2d(const BYTE* p1, int p1Step, int p1Size, co if (p1m <= p2m) { - ULONG_PTR p1mEnd = p1m + (height - 1) * p1Step * 1ULL + width * p1Size * 1ULL; + ULONG_PTR p1mEnd = p1m + 1ull * (height - 1) * p1Step + 1ull * width * p1Size; if (p1mEnd > p2m) return TRUE; } else { - ULONG_PTR p2mEnd = p2m + (height - 1) * p2Step * 1ULL + width * p2Size * 1ULL; + ULONG_PTR p2mEnd = p2m + 1ull * (height - 1) * p2Step + 1ull * width * p2Size; if (p2mEnd > p1m) return TRUE; diff --git a/libfreerdp/primitives/primitives.c b/libfreerdp/primitives/primitives.c index 26c6338f6fa9..6820a5822336 100644 --- a/libfreerdp/primitives/primitives.c +++ b/libfreerdp/primitives/primitives.c @@ -157,7 +157,7 @@ static primitives_YUV_benchmark* primitives_YUV_benchmark_init(primitives_YUV_be if (!buf) goto fail; - winpr_RAND(buf, roi->width * roi->height * 1ULL); + winpr_RAND(buf, 1ull * roi->width * roi->height); ret->steps[i] = roi->width; } @@ -260,7 +260,7 @@ static BOOL primitives_autodetect_best(primitives_t* prims) /* finally compute the results */ *prims = *best->prims; - WLog_INFO(TAG, "primitives autodetect, using %s", best->name); + WLog_DBG(TAG, "primitives autodetect, using %s", best->name); ret = TRUE; out: if (!ret) diff --git a/libfreerdp/utils/CMakeLists.txt b/libfreerdp/utils/CMakeLists.txt index e3a8981d85a3..2ec561d33fd0 100644 --- a/libfreerdp/utils/CMakeLists.txt +++ b/libfreerdp/utils/CMakeLists.txt @@ -20,10 +20,12 @@ set(MODULE_PREFIX "FREERDP_UTILS") set(${MODULE_PREFIX}_SRCS passphrase.c + cliprdr_utils.c pcap.c profiler.c ringbuffer.c signal.c + string.c stopwatch.c) freerdp_module_add(${${MODULE_PREFIX}_SRCS}) diff --git a/libfreerdp/utils/cliprdr_utils.c b/libfreerdp/utils/cliprdr_utils.c new file mode 100644 index 000000000000..7fb99d63a1b6 --- /dev/null +++ b/libfreerdp/utils/cliprdr_utils.c @@ -0,0 +1,235 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Clipboard Virtual Channel Extension + * + * Copyright 2013 Marc-Andre Moreau + * Copyright 2022 Armin Novak +#include +#include + +#include +#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME) + +#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520) +#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024) + +static UINT64 filetime_to_uint64(FILETIME value) +{ + UINT64 converted = 0; + converted |= (UINT32)value.dwHighDateTime; + converted <<= 32; + converted |= (UINT32)value.dwLowDateTime; + return converted; +} + +static FILETIME uint64_to_filetime(UINT64 value) +{ + FILETIME converted; + converted.dwLowDateTime = (UINT32)(value >> 0); + converted.dwHighDateTime = (UINT32)(value >> 32); + return converted; +} + +/** + * Parse a packed file list. + * + * The resulting array must be freed with the `free()` function. + * + * @param [in] format_data packed `CLIPRDR_FILELIST` to parse. + * @param [in] format_data_length length of `format_data` in bytes. + * @param [out] file_descriptor_array parsed array of `FILEDESCRIPTOR` structs. + * @param [out] file_descriptor_count number of elements in `file_descriptor_array`. + * + * @returns 0 on success, otherwise a Win32 error code. + */ +UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length, + FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count) +{ + UINT result = NO_ERROR; + UINT32 i; + UINT32 count = 0; + wStream sbuffer; + wStream* s = &sbuffer; + + if (!format_data || !file_descriptor_array || !file_descriptor_count) + return ERROR_BAD_ARGUMENTS; + + Stream_StaticInit(&sbuffer, format_data, format_data_length); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + if (Stream_GetRemainingLength(s) < 4) + { + WLog_ERR(TAG, "invalid packed file list"); + + result = ERROR_INCORRECT_SIZE; + goto out; + } + + Stream_Read_UINT32(s, count); /* cItems (4 bytes) */ + + if (Stream_GetRemainingLength(s) / CLIPRDR_FILEDESCRIPTOR_SIZE < count) + { + WLog_ERR(TAG, "packed file list is too short: expected %" PRIuz ", have %" PRIuz, + ((size_t)count) * CLIPRDR_FILEDESCRIPTOR_SIZE, Stream_GetRemainingLength(s)); + + result = ERROR_INCORRECT_SIZE; + goto out; + } + + *file_descriptor_count = count; + *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW)); + if (!*file_descriptor_array) + { + result = ERROR_NOT_ENOUGH_MEMORY; + goto out; + } + + for (i = 0; i < count; i++) + { + UINT64 tmp; + FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]); + + Stream_Read_UINT32(s, file->dwFlags); /* flags (4 bytes) */ + Stream_Read_UINT32(s, file->clsid.Data1); + Stream_Read_UINT16(s, file->clsid.Data2); + Stream_Read_UINT16(s, file->clsid.Data3); + Stream_Read(s, &file->clsid.Data4, sizeof(file->clsid.Data4)); + Stream_Read_INT32(s, file->sizel.cx); + Stream_Read_INT32(s, file->sizel.cy); + Stream_Read_INT32(s, file->pointl.x); + Stream_Read_INT32(s, file->pointl.y); + Stream_Read_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ + Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */ + file->ftCreationTime = uint64_to_filetime(tmp); + Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */ + file->ftLastAccessTime = uint64_to_filetime(tmp); + Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */ + file->ftLastWriteTime = uint64_to_filetime(tmp); + Stream_Read_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ + Stream_Read_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ + Stream_Read_UTF16_String(s, file->cFileName, + ARRAYSIZE(file->cFileName)); /* cFileName (520 bytes) */ + } + + if (Stream_GetRemainingLength(s) > 0) + WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes", + Stream_GetRemainingLength(s)); +out: + + return result; +} + +/** + * Serialize a packed file list. + * + * The resulting format data must be freed with the `free()` function. + * + * @param [in] file_descriptor_array array of `FILEDESCRIPTOR` structs to serialize. + * @param [in] file_descriptor_count number of elements in `file_descriptor_array`. + * @param [out] format_data serialized CLIPRDR_FILELIST. + * @param [out] format_data_length length of `format_data` in bytes. + * + * @returns 0 on success, otherwise a Win32 error code. + */ +UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length) +{ + return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array, + file_descriptor_count, format_data, format_data_length); +} + +UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array, + UINT32 file_descriptor_count, BYTE** format_data, + UINT32* format_data_length) +{ + UINT result = NO_ERROR; + UINT32 i; + size_t len; + wStream* s = NULL; + + if (!file_descriptor_array || !format_data || !format_data_length) + return ERROR_BAD_ARGUMENTS; + + if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0) + { + WLog_WARN(TAG, "No file clipboard support annouonced!"); + return ERROR_BAD_ARGUMENTS; + } + + s = Stream_New(NULL, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */ + + for (i = 0; i < file_descriptor_count; i++) + { + int c; + UINT64 lastWriteTime; + const FILEDESCRIPTORW* file = &file_descriptor_array[i]; + + /* + * There is a known issue with Windows server getting stuck in + * an infinite loop when downloading files that are larger than + * 2 gigabytes. Do not allow clients to send such file lists. + * + * https://support.microsoft.com/en-us/help/2258090 + */ + if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0) + { + if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE)) + { + WLog_ERR(TAG, "cliprdr does not support files over 2 GB"); + result = ERROR_FILE_TOO_LARGE; + goto error; + } + } + + Stream_Write_UINT32(s, file->dwFlags); /* flags (4 bytes) */ + Stream_Zero(s, 32); /* reserved1 (32 bytes) */ + Stream_Write_UINT32(s, file->dwFileAttributes); /* fileAttributes (4 bytes) */ + Stream_Zero(s, 16); /* reserved2 (16 bytes) */ + lastWriteTime = filetime_to_uint64(file->ftLastWriteTime); + Stream_Write_UINT64(s, lastWriteTime); /* lastWriteTime (8 bytes) */ + Stream_Write_UINT32(s, file->nFileSizeHigh); /* fileSizeHigh (4 bytes) */ + Stream_Write_UINT32(s, file->nFileSizeLow); /* fileSizeLow (4 bytes) */ + for (c = 0; c < 260; c++) /* cFileName (520 bytes) */ + Stream_Write_UINT16(s, file->cFileName[c]); + } + + Stream_SealLength(s); + + Stream_GetBuffer(s, *format_data); + Stream_GetLength(s, len); + if (len > UINT32_MAX) + goto error; + + *format_data_length = (UINT32)len; + + Stream_Free(s, FALSE); + + return result; + +error: + Stream_Free(s, TRUE); + + return result; +} diff --git a/libfreerdp/utils/signal.c b/libfreerdp/utils/signal.c index 0510ef4eecb9..b529b7823c4e 100644 --- a/libfreerdp/utils/signal.c +++ b/libfreerdp/utils/signal.c @@ -43,6 +43,7 @@ int freerdp_handle_signals(void) #else #include +#include volatile sig_atomic_t terminal_needs_reset = 0; int terminal_fildes = 0; @@ -53,8 +54,15 @@ static void fatal_handler(int signum) { struct sigaction default_sigaction; sigset_t this_mask; - WLog_INFO(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum); + static BOOL recursive = FALSE; + if (!recursive) + { + recursive = TRUE; + WLog_ERR(TAG, "Caught signal '%s' [%d]", strsignal(signum), signum); + + winpr_log_backtrace(TAG, WLOG_ERROR, 20); + } if (terminal_needs_reset) tcsetattr(terminal_fildes, TCSAFLUSH, &orig_flags); diff --git a/libfreerdp/utils/string.c b/libfreerdp/utils/string.c new file mode 100644 index 000000000000..3ddb4b7fbbb7 --- /dev/null +++ b/libfreerdp/utils/string.c @@ -0,0 +1,62 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * + * String Utils - Helper functions converting something to string + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +char* rdp_redirection_flags_to_string(UINT32 flags, char* buffer, size_t size) +{ + size_t x; + struct map_t + { + UINT32 flag; + const char* name; + }; + const struct map_t map[] = { + { LB_TARGET_NET_ADDRESS, "LB_TARGET_NET_ADDRESS" }, + { LB_LOAD_BALANCE_INFO, "LB_LOAD_BALANCE_INFO" }, + { LB_USERNAME, "LB_USERNAME" }, + { LB_DOMAIN, "LB_DOMAIN" }, + { LB_PASSWORD, "LB_PASSWORD" }, + { LB_DONTSTOREUSERNAME, "LB_DONTSTOREUSERNAME" }, + { LB_SMARTCARD_LOGON, "LB_SMARTCARD_LOGON" }, + { LB_NOREDIRECT, "LB_NOREDIRECT" }, + { LB_TARGET_FQDN, "LB_TARGET_FQDN" }, + { LB_TARGET_NETBIOS_NAME, "LB_TARGET_NETBIOS_NAME" }, + { LB_TARGET_NET_ADDRESSES, "LB_TARGET_NET_ADDRESSES" }, + { LB_CLIENT_TSV_URL, "LB_CLIENT_TSV_URL" }, + { LB_SERVER_TSV_CAPABLE, "LB_SERVER_TSV_CAPABLE" }, + { LB_PASSWORD_IS_PK_ENCRYPTED, "LB_PASSWORD_IS_PK_ENCRYPTED" }, + { LB_REDIRECTION_GUID, "LB_REDIRECTION_GUID" }, + { LB_TARGET_CERTIFICATE, "LB_TARGET_CERTIFICATE" }, + }; + + for (x = 0; x < ARRAYSIZE(map); x++) + { + const struct map_t* cur = &map[x]; + if (flags & cur->flag) + { + if (!winpr_str_append(cur->name, buffer, size, "|")) + return NULL; + } + } + return buffer; +} diff --git a/packaging/flatpak/build-bundle.sh b/packaging/flatpak/build-bundle.sh new file mode 100755 index 000000000000..ed4b5a8aa45e --- /dev/null +++ b/packaging/flatpak/build-bundle.sh @@ -0,0 +1,33 @@ +#!/bin/bash -xe +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +MANIFEST=com.freerdp.FreeRDP + +BUILD_BASE=$(mktemp -d) +if [ $# -gt 0 ]; +then + BUILD_BASE=$1 +fi + +echo "Using $BUILD_BASE as temporary build directory" +REPO=$BUILD_BASE/repo +BUILD=$BUILD_BASE/build +STATE=$BUILD_BASE/state + +BUILDER=$(which flatpak-builder) +if [ ! -x "$BUILDER" ]; +then + echo "command 'flatpak-builder' could not be found, please install and add to PATH" + exit 1 +fi + +FLATPAK=$(which flatpak) +if [ ! -x "$FLATPAK" ]; +then + echo "command 'flatpak' could not be found, please install and add to PATH" + exit 1 +fi + +flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo --user +flatpak-builder -v --repo=$REPO --state-dir=$STATE $BUILD $SCRIPT_DIR/$MANIFEST.json --force-clean --user --install-deps-from=flathub +flatpak build-bundle -v $REPO $MANIFEST.flatpak $MANIFEST --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo diff --git a/packaging/flatpak/com.freerdp.FreeRDP.json b/packaging/flatpak/com.freerdp.FreeRDP.json index 815b914aadd5..38c5f17e0cb8 100644 --- a/packaging/flatpak/com.freerdp.FreeRDP.json +++ b/packaging/flatpak/com.freerdp.FreeRDP.json @@ -1,29 +1,45 @@ { "app-id": "com.freerdp.FreeRDP", "runtime": "org.freedesktop.Platform", - "runtime-version": "19.08", + "runtime-version": "22.08", "sdk": "org.freedesktop.Sdk", "build-options" : { - "cflags": "-O2 -g", - "cxxflags": "-O2 -g" + "cflags": "-O3", + "cxxflags": "-O3" }, "command": "freerdp.sh", "cleanup": [ + "*.la", + "/include", + "/lib/cmake", + "/lib/pkgconfig" ], "finish-args": [ /* X11 + XShm access */ "--share=ipc", "--socket=x11", - "--socket=fallback-x11", /* Needs to talk to the network */ "--share=network", /* Play sounds redirected from guests */ "--socket=pulseaudio", + "--socket=cups", /* Wayland access */ "--socket=wayland", /* Allow rw access to download folder */ "--filesystem=xdg-download" ], + "add-build-extensions": { + "org.freedesktop.Platform.ffmpeg-full": { + "directory": "lib/ffmpeg", + "version": "22.08", + "add-ld-path": "." + }, + "org.freedesktop.Platform.openh264": { + "directory": "lib/openh264", + "version": "2.3.0", + "add-ld-path": "." + } + }, "modules": [ { /** @@ -33,10 +49,14 @@ "sources": [ { "type": "archive", - "url": "https://xorg.freedesktop.org/releases/individual/app/xprop-1.2.3.tar.bz2", - "sha256": "d22afb28c86d85fff10a50156a7d0fa930c80ae865d70b26d805fd28a17a521b" + "url": "https://xorg.freedesktop.org/releases/individual/app/xprop-1.2.5.tar.bz2", + "sha256": "9b92ed0316bf2486121d8bac88bd1878f16b43bd335f18009b1f941f1eca93a1" } - ] + ], + "cleanup": [ + "/share/doc", + "/share/man" + ] }, { "name": "libusb", @@ -45,10 +65,13 @@ { "type": "git", "url": "https://github.com/libusb/libusb.git", - "tag": "v1.0.22", - "commit": "0034b2afdcdb1614e78edaa2a9e22d5936aeae5d" + "tag": "v1.0.26" } - ] + ], + "cleanup": [ + "/share/doc", + "/share/man" + ] }, { "name": "pcsc", @@ -63,16 +86,74 @@ { "type": "git", "url": "https://github.com/LudovicRousseau/PCSC.git", - "tag": "pcsc-1.8.24", - "commit": "73d95ada3221c060cbd7b6aa2375453f9d0e359b" + "tag": "1.9.9" } + ], + "cleanup": [ + "/share/doc", + "/share/man" + ] + }, + { + "name": "openssl111", + "buildsystem": "simple", + "build-commands": [ + "./config --prefix=/app", + "make -j $FLATPAK_BUILDER_N_JOBS", + "make install" + ], + "cleanup": [ + "/share/doc", + "/share/man", + "/bin" + ], + "sources": [ + { + "type": "archive", + "url": "https://www.openssl.org/source/openssl-1.1.1q.tar.gz", + "sha256": "d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca" + } + ] + }, + { + "name": "freerdp", + "buildsystem": "cmake-ninja", + "cleanup": [ + ], + "config-opts": [ + "-DCMAKE_VERBOSE_MAKEFILE=ON", + "-DCMAKE_BUILD_TYPE:STRING=Release", + "-DCMAKE_INSTALL_LIBDIR:PATH=lib", + "-DCHANNEL_TSMF:BOOL=OFF", + "-DCHANNEL_URBDRC:BOOL=ON", + "-DBUILD_TESTING:BOOL=OFF", + "-DWITH_MANPAGES:BOOL=OFF", + "-DWITH_GSSAPI:BOOL=OFF", + "-DWITH_PCSC:BOOL=ON", + "-DWITH_PKCS11:BOOL=ON", + "-DWITH_SWSCALE:BOOL=ON", + "-DWITH_SERVER:BOOL=ON", + "-DWITH_SAMPLE:BOOL=ON", + "-DWITH_CUPS:BOOL=ON", + "-DWITH_FFMPEG:BOOL=ON", + "-DWITH_DSP_FFMPEG:BOOL=ON", + "-DWITH_OSS:BOOL=OFF", + "-DWITH_PULSE:BOOL=ON" + ], + "sources": [ + { + "type": "dir", + "path": "../.." + } ] }, - { + { "name": "wrapper", "buildsystem": "simple", "build-commands": [ - "install -D freerdp.sh /app/bin/freerdp.sh" + "install -D freerdp.sh /app/bin/freerdp.sh", + "mkdir -p /app/lib/ffmpeg", + "mkdir -p /app/lib/openh264" ], "sources": [ { @@ -80,36 +161,6 @@ "path": "freerdp.sh" } ] - }, - { - "name": "freerdp", - "buildsystem": "cmake-ninja", - "cleanup": [ - ], - "config-opts": [ - "-DCMAKE_BUILD_TYPE:STRING=Release", - "-DCMAKE_INSTALL_LIBDIR:PATH=lib", - "-DCHANNEL_TSMF:BOOL=ON", - "-DCHANNEL_URBDRC:BOOL=ON", - "-DBUILD_TESTING:BOOL=OFF", - "-DWITH_MANPAGES:BOOL=OFF", - "-DWITH_GSSAPI:BOOL=OFF", - "-DWITH_PCSC:BOOL=ON", - "-DWITH_SERVER:BOOL=ON", - "-DWITH_SAMPLE:BOOL=ON", - "-DWITH_CUPS:BOOL=ON", - "-DWITH_FFMPEG:BOOL=ON", - "-DWITH_DSP_FFMPEG:BOOL=ON", - "-DWITH_OSS:BOOL=OFF", - "-DWITH_PULSE:BOOL=ON" - ], - "sources": [ - { - "type": "git", - "url": "https://github.com/FreeRDP/FreeRDP.git", - "branch": "master" - } - ] - } + } ] } diff --git a/packaging/rpm/freerdp-nightly.spec b/packaging/rpm/freerdp-nightly.spec index f17bfac4cffb..b213f624c2c1 100644 --- a/packaging/rpm/freerdp-nightly.spec +++ b/packaging/rpm/freerdp-nightly.spec @@ -86,6 +86,10 @@ BuildRequires: libjpeg-turbo-devel BuildRequires: libwayland-client-devel %endif +%if 0%{?fedora} >= 36 || 0%{?rhel} >= 9 +BuildRequires: ffmpeg-free-devel +%endif + BuildRoot: %{_tmppath}/%{name}-%{version}-build %description @@ -116,7 +120,7 @@ cp %{_topdir}/SOURCES/source_version freerdp-nightly-%{version}/.source_version -DWITH_CUPS=ON \ -DWITH_PCSC=ON \ -DWITH_JPEG=ON \ -%if %{defined suse_version} +%if 0%{?fedora} >= 36 || 0%{?rhel} >= 9 || 0%{?suse_version} -DWITH_FFMPEG=ON \ -DWITH_DSP_FFMPEG=ON \ %endif diff --git a/scripts/OpenSSL-DownloadAndBuild.command b/scripts/OpenSSL-DownloadAndBuild.command index 1aa03a0af0f2..5de1a31dc8b8 100755 --- a/scripts/OpenSSL-DownloadAndBuild.command +++ b/scripts/OpenSSL-DownloadAndBuild.command @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -xe # # Copyright 2015 Thincast Technologies GmbH # @@ -9,19 +9,19 @@ ## Settings # openssl version to use -OPENSSLVERSION="1.0.2q" -SHA256SUM="5744cfcbcec2b1b48629f7354203bc1e5e9b5466998bbccc5b5fcde3b18eb684" +OPENSSLVERSION="3.0.2" +SHA256SUM="98e91ccead4d4756ae3c9cde5e09191a8e586d9f4d50838e7ec09d6411dfdb63" # SDK version to use - if not set latest version found is used SDK_VERSION="" # Minimum SDK version the application supports -MIN_SDK_VERSION="10.0" +MIN_SDK_VERSION="15.0" ## Defaults INSTALLDIR="external" # Architectures to build -ARCHS="i386 x86_64 armv7 armv7s arm64" +ARCHS="arm64 x86_64" # Use default SDK version if not set if [ -z ${SDK_VERSION} ]; then @@ -30,9 +30,6 @@ fi CORES=`sysctl hw.ncpu | awk '{print $2}'` MAKEOPTS="-j $CORES" -# disable parallell builds since openssl build -# fails sometimes -MAKEOPTS="" DEVELOPER=`xcode-select -print-path` if [ ! -d "$DEVELOPER" ]; then @@ -44,16 +41,6 @@ if [ ! -d "$DEVELOPER" ]; then exit 1 fi -function run { - "$@" - local status=$? - if [ $status -ne 0 ]; then - echo "error with $@" >&2 - exit $status - fi - return $status -} - # Functions function buildArch(){ ARCH=$1 @@ -61,36 +48,38 @@ function buildArch(){ then PLATFORM="iPhoneSimulator" else - run sed -ie "s!static volatile sig_atomic_t intr_signal;!static volatile intr_signal;!" "crypto/ui/ui_openssl.c" + sed -ie "s!static volatile sig_atomic_t intr_signal;!static volatile intr_signal;!" "crypto/ui/ui_openssl.c" PLATFORM="iPhoneOS" fi - run export CROSS_TOP="${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer" - run export CROSS_SDK="${PLATFORM}${SDK_VERSION}.sdk" - run export BUILD_TOOLS="${DEVELOPER}" - run export CC="${BUILD_TOOLS}/usr/bin/gcc -arch ${ARCH}" + export CROSS_TOP="${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer" + export CROSS_SDK="${PLATFORM}${SDK_VERSION}.sdk" + export BUILD_TOOLS="${DEVELOPER}" + export CC="${BUILD_TOOLS}/usr/bin/gcc -arch ${ARCH}" if [ ! -z $MIN_SDK_VERSION ]; then - run export CC="$CC -miphoneos-version-min=${MIN_SDK_VERSION}" + export CC="$CC -miphoneos-version-min=${MIN_SDK_VERSION}" fi echo "Building openssl-${OPENSSLVERSION} for ${PLATFORM} ${SDK_VERSION} ${ARCH} (min SDK set: ${MIN_SDK_VERSION:-"none"})" LOGFILE="BuildLog.darwin-${ARCH}.txt" echo -n " Please wait ..." if [[ "$OPENSSLVERSION" =~ 1.0.0. ]]; then - run ./Configure BSD-generic32 > "${LOGFILE}" 2>&1 + CONFIG_ARGS=BSD-generic32 elif [ "${ARCH}" == "x86_64" ]; then - run ./Configure darwin64-x86_64-cc > "${LOGFILE}" 2>&1 + CONFIG_ARGS=darwin64-x86_64-cc elif [ "${ARCH}" == "i386" ]; then - run ./Configure iphoneos-cross no-asm > "${LOGFILE}" 2>&1 + CONFIG_ARGS="iphoneos-cross no-asm" else - run ./Configure iphoneos-cross > "${LOGFILE}" 2>&1 + CONFIG_ARGS=iphoneos-cross fi - run make ${MAKEOPTS} >> ${LOGFILE} 2>&1 + ./Configure $CONFIG_ARGS 2>&1 | tee ${LOGFILE} + + make ${MAKEOPTS} 2>&1 | tee ${LOGFILE} echo " Done. Build log saved in ${LOGFILE}" - run cp libcrypto.a ../../lib/libcrypto_${ARCH}.a - run cp libssl.a ../../lib/libssl_${ARCH}.a - run make clean >/dev/null 2>&1 + cp libcrypto.a ../../lib/libcrypto_${ARCH}.a + cp libssl.a ../../lib/libssl_${ARCH}.a + make clean 2>&1 | tee ${LOGFILE} } # main @@ -104,14 +93,14 @@ fi cd $INSTALLDIR if [ ! -d openssl ];then - run mkdir openssl + mkdir openssl fi -run cd openssl +cd openssl CS=`shasum -a 256 "openssl-$OPENSSLVERSION.tar.gz" | cut -d ' ' -f1` if [ ! "$CS" = "$SHA256SUM" ]; then echo "Downloading OpenSSL Version $OPENSSLVERSION ..." - run rm -f "openssl-$OPENSSLVERSION.tar.gz" - run curl -o "openssl-$OPENSSLVERSION.tar.gz" https://www.openssl.org/source/openssl-$OPENSSLVERSION.tar.gz + rm -f "openssl-$OPENSSLVERSION.tar.gz" + curl -o "openssl-$OPENSSLVERSION.tar.gz" https://www.openssl.org/source/openssl-$OPENSSLVERSION.tar.gz CS=`shasum -a 256 "openssl-$OPENSSLVERSION.tar.gz" | cut -d ' ' -f1` if [ ! "$CS" = "$SHA256SUM" ]; then @@ -121,19 +110,19 @@ if [ ! "$CS" = "$SHA256SUM" ]; then fi # remove old build dir -run rm -rf openssltmp -run mkdir openssltmp -run cd openssltmp +rm -rf openssltmp +mkdir openssltmp +cd openssltmp echo "Unpacking OpenSSL ..." -run tar xfz "../openssl-$OPENSSLVERSION.tar.gz" +tar xfz "../openssl-$OPENSSLVERSION.tar.gz" if [ ! $? = 0 ]; then echo "Unpacking failed." exit 1 fi echo -run cd "openssl-$OPENSSLVERSION" +cd "openssl-$OPENSSLVERSION" case `pwd` in *\ * ) @@ -143,22 +132,22 @@ case `pwd` in esac # Cleanup old build artifacts -run rm -rf ../../include -run mkdir -p ../../include +rm -rf ../../include +mkdir -p ../../include -run rm -rf ../../lib -run mkdir -p ../../lib +rm -rf ../../lib +mkdir -p ../../lib for i in ${ARCHS}; do buildArch $i done echo "Copying header files ..." -run cp -r include/ ../../include/ +cp -r include/ ../../include/ echo echo "Combining to unversal binary" -run lipo -create ../../lib/libcrypto_*.a -o ../../lib/libcrypto.a -run lipo -create ../../lib/libssl_*.a -o ../../lib/libssl.a +lipo -create ../../lib/libcrypto_*.a -o ../../lib/libcrypto.a +lipo -create ../../lib/libssl_*.a -o ../../lib/libssl.a echo "Finished. Please verify the contens of the openssl folder in \"$INSTALLDIR\"" diff --git a/scripts/android-build-32.conf b/scripts/android-build-32.conf index 4bafb2388ed8..d9e425017d00 100644 --- a/scripts/android-build-32.conf +++ b/scripts/android-build-32.conf @@ -9,16 +9,20 @@ # If you require support for older NDK API levels, # create seperate configurations for each NDK API level # and architecture you want to support. -WITH_JPEG=0 -WITH_OPENH264=1 +WITH_OPENH264=0 WITH_OPENSSL=1 +WITH_FFMPEG=1 BUILD_DEPS=1 DEPS_ONLY=0 NDK_TARGET=21 +WITH_MEDIACODEC=1 -JPEG_TAG=master -OPENH264_TAG=v1.8.0 # NOTE: NDK r15c or earlier needed in --openh624-ndk for v1.8.0 -OPENSSL_TAG=OpenSSL_1_1_1j +OPENH264_TAG=v2.3.1 +OPENH264_HASH=453afa66dacb560bc5fd0468aabee90c483741571bca820a39a1c07f0362dc32 +OPENSSL_TAG=openssl-1.1.1q +OPENSSL_HASH=d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca +FFMPEG_TAG=n4.4.1 +FFMPEG_HASH=82b43cc67296bcd01a59ae6b327cdb50121d3a9e35f41a30de1edd71bb4a6666 SRC_DIR=$SCRIPT_PATH/.. BUILD_DST=$SCRIPT_PATH/../client/Android/Studio/freeRDPCore/src/main/jniLibs diff --git a/scripts/android-build-64.conf b/scripts/android-build-64.conf index a3dcf711d434..fa0a80bb94a6 100644 --- a/scripts/android-build-64.conf +++ b/scripts/android-build-64.conf @@ -9,16 +9,20 @@ # If you require support for older NDK API levels, # create seperate configurations for each NDK API level # and architecture you want to support. -WITH_JPEG=0 WITH_OPENH264=1 WITH_OPENSSL=1 +WITH_FFMPEG=1 BUILD_DEPS=1 DEPS_ONLY=0 NDK_TARGET=21 +WITH_MEDIACODEC=1 -JPEG_TAG=master -OPENH264_TAG=v1.8.0 # NOTE: NDK r15c or earlier needed in --openh624-ndk for v1.8.0 -OPENSSL_TAG=OpenSSL_1_1_1j +OPENH264_TAG=v2.3.1 +OPENH264_HASH=453afa66dacb560bc5fd0468aabee90c483741571bca820a39a1c07f0362dc32 +OPENSSL_TAG=openssl-1.1.1q +OPENSSL_HASH=d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca +FFMPEG_TAG=n4.4.1 +FFMPEG_HASH=82b43cc67296bcd01a59ae6b327cdb50121d3a9e35f41a30de1edd71bb4a6666 SRC_DIR=$SCRIPT_PATH/.. BUILD_DST=$SCRIPT_PATH/../client/Android/Studio/freeRDPCore/src/main/jniLibs diff --git a/scripts/android-build-common.sh b/scripts/android-build-common.sh index 11fda6765ff4..2e58331a9ab9 100644 --- a/scripts/android-build-common.sh +++ b/scripts/android-build-common.sh @@ -1,8 +1,20 @@ -#!/bin/bash +#!/bin/bash -x SCRIPT_PATH=$(dirname "${BASH_SOURCE[0]}") SCRIPT_PATH=$(realpath "$SCRIPT_PATH") +FIND_ARGS="-type f -print -quit" +case "$(uname -s)" in + + Darwin) + FIND_ARGS="-perm +111 $FIND_ARGS" + ;; + + *) + FIND_ARGS="-executable $FIND_ARGS" + ;; +esac + if [ -z $BUILD_ARCH ]; then BUILD_ARCH="armeabi-v7a x86 x86_64 arm64-v8a" fi @@ -12,7 +24,7 @@ if [ -z $NDK_TARGET ]; then fi if [ -z $CMAKE_PROGRAM ]; then - CMAKE_PROGRAM=$(find $ANDROID_SDK/cmake -name cmake -type f -executable -print -quit) + CMAKE_PROGRAM=$(find $ANDROID_SDK/cmake -name cmake $FIND_ARGS) fi if [ -z $CCACHE ]; then @@ -61,6 +73,8 @@ function common_help { echo " SCM_URL=$SCM_URL" echo " --tag The SCM branch or tag to check out" echo " SCM_TAG=$SCM_TAG" + echo " --hash The SCM commit or hash to check out" + echo " SCM_HASH=$SCM_HASH" echo " --clean Clean the destination before build" echo " --help Display this help" exit 0 @@ -82,10 +96,10 @@ function common_parse_arguments { do key="$1" case $key in - --conf) - source "$2" - shift - ;; + --conf) + source "$2" || exit 1 + shift + ;; --target) NDK_TARGET="$2" @@ -99,7 +113,7 @@ function common_parse_arguments { --sdk) ANDROID_SDK="$2" - CMAKE_PROGRAM=$(find $ANDROID_SDK/cmake -name cmake -type f -executable -print -quit) + CMAKE_PROGRAM=$(find $ANDROID_SDK/cmake -name cmake $FIND_ARGS) shift ;; @@ -128,6 +142,11 @@ function common_parse_arguments { shift ;; + --hash) + SCM_HASH="$2" + shift + ;; + --clean) CLEAN_BUILD_DIR=1 shift @@ -181,6 +200,12 @@ function common_check_requirements { exit 1 fi + if [[ -z $SCM_HASH ]]; + then + echo "SCM HASH not defined! Define SCM_HASH" + exit 1 + fi + if [[ -z $NDK_TARGET ]]; then echo "Android platform NDK_TARGET not defined" @@ -192,12 +217,20 @@ function common_check_requirements { else echo "ndk-build not found in NDK directory $ANDROID_NDK" echo "assuming ndk-build is in path..." - NDK_BUILD=ndk-build + NDK_BUILD=$(which ndk-build) + if [ -z $NDK_BUILD ]; then + echo "ndk-build not found in $ANDROID_NDK and not in PATH" + exit 1 + fi fi - if [ -z $CMAKE_PROGRAM ]; then - CMAKE_PROGRAM=$(find $ANDROID_SDK/cmake -name cmake -type f -executable -print -quit) - fi + if [ -z $CMAKE_PROGRAM ]; then + CMAKE_PROGRAM=$(find $ANDROID_SDK/cmake -name cmake $FIND_ARGS) + if [ -z $CMAKE_PROGRAM ]; then + echo "CMake not found in $ANDROID_SDK, install CMake from the android SDK!" + exit 1 + fi + fi for CMD in make git $CMAKE_PROGRAM $NDK_BUILD do @@ -218,7 +251,7 @@ function common_check_requirements { } function common_update { - if [ $# -ne 3 ]; + if [ $# -ne 4 ]; then echo "Invalid arguments to update function $@" exit 1 @@ -226,19 +259,22 @@ function common_update { SCM_URL=$1 SCM_TAG=$2 BUILD_SRC=$3 + SCM_HASH=$4 echo "Preparing checkout..." BASE=$(pwd) CACHE=$SCRIPT_PATH/../cache common_run mkdir -p $CACHE TARFILE="$CACHE/$SCM_TAG.tar.gz" - - + if [[ ! -f "$TARFILE" ]]; then - common_run wget -O "$TARFILE" "$SCM_URL/archive/$SCM_TAG.tar.gz" + common_run wget -O "$TARFILE" "$SCM_URL/$SCM_TAG.tar.gz" fi + echo "$SCM_HASH $TARFILE" > $TARFILE.sha256sum + common_run sha256sum -c $TARFILE.sha256sum + if [[ -d $BUILD_SRC ]]; then common_run rm -rf $BUILD_SRC diff --git a/scripts/android-build-ffmpeg.sh b/scripts/android-build-ffmpeg.sh new file mode 100755 index 000000000000..206e728c1023 --- /dev/null +++ b/scripts/android-build-ffmpeg.sh @@ -0,0 +1,179 @@ +#!/bin/bash + +SCM_URL=https://github.com/FFmpeg/FFmpeg/archive +SCM_TAG=n4.4.1 +SCM_HASH=82b43cc67296bcd01a59ae6b327cdb50121d3a9e35f41a30de1edd71bb4a6666 + +OLD_PATH=$PATH + +source $(dirname "${BASH_SOURCE[0]}")/android-build-common.sh + +function get_toolchain() { + HOST_OS=$(uname -s) + case ${HOST_OS} in + Darwin) HOST_OS=darwin;; + Linux) HOST_OS=linux;; + FreeBsd) HOST_OS=freebsd;; + CYGWIN*|*_NT-*) HOST_OS=cygwin;; + esac + + HOST_ARCH=$(uname -m) + case ${HOST_ARCH} in + i?86) HOST_ARCH=x86;; + x86_64|amd64) HOST_ARCH=x86_64;; + esac + + echo "${HOST_OS}-${HOST_ARCH}" +} + +function get_build_host() { + case ${ARCH} in + armeabi-v7a) + echo "arm-linux-androideabi" + ;; + arm64-v8a) + echo "aarch64-linux-android" + ;; + x86) + echo "i686-linux-android" + ;; + x86_64) + echo "x86_64-linux-android" + ;; + esac +} + +function get_clang_target_host() { + case ${ARCH} in + armeabi-v7a) + echo "armv7a-linux-androideabi${NDK_TARGET}" + ;; + arm64-v8a) + echo "aarch64-linux-android${NDK_TARGET}" + ;; + x86) + echo "i686-linux-android${NDK_TARGET}" + ;; + x86_64) + echo "x86_64-linux-android${NDK_TARGET}" + ;; + esac +} + +function get_arch_specific_ldflags() { + case ${ARCH} in + armeabi-v7a) + echo "-march=armv7-a -mfpu=neon -mfloat-abi=softfp -Wl,--fix-cortex-a8" + ;; + arm64-v8a) + echo "-march=armv8-a" + ;; + x86) + echo "-march=i686" + ;; + x86_64) + echo "-march=x86-64" + ;; + esac +} + +function set_toolchain_clang_paths { + TOOLCHAIN=$(get_toolchain) + + common_run export PATH=$PATH:${ANDROID_NDK}/toolchains/llvm/prebuilt/${TOOLCHAIN}/bin + AR=llvm-ar + NM=llvm-nm + RANLIB=llvm-ranlib + STRIP=llvm-strip + CC=$(get_clang_target_host)-clang + CXX=$(get_clang_target_host)-clang++ + + case ${ARCH} in + arm64-v8a) + common_run export ac_cv_c_bigendian=no + ;; + esac +} + +function build { + echo "Building FFmpeg architecture $1..." + BASE=$(pwd) + common_run cd $BUILD_SRC + + BUILD_HOST=$(get_build_host) + set_toolchain_clang_paths + LDFLAGS=$(get_arch_specific_ldflags) + + PATH=$ANDROID_NDK:$PATH + common_run ./configure \ + --cross-prefix="${BUILD_HOST}-" \ + --sysroot="${ANDROID_NDK}/toolchains/llvm/prebuilt/${TOOLCHAIN}/sysroot" \ + --arch="${TARGET_ARCH}" \ + --cpu="${TARGET_CPU}" \ + --cc="${CC}" \ + --cxx="${CXX}" \ + --ar="${AR}" \ + --nm="${NM}" \ + --ranlib="${RANLIB}" \ + --strip="${STRIP}" \ + --extra-ldflags="${LDFLAGS}" \ + --prefix="${BUILD_DST}/${ARCH}" \ + --pkg-config="${HOST_PKG_CONFIG_PATH}" \ + --target-os=android \ + ${ARCH_OPTIONS} \ + --enable-cross-compile \ + --enable-pic \ + --enable-jni --enable-mediacodec \ + --enable-shared \ + --disable-stripping \ + --disable-programs --disable-doc --disable-avdevice --disable-avfilter --disable-avformat + + common_run make clean + common_run make -j + common_run make install +} + +# Run the main program. +common_parse_arguments $@ +common_check_requirements +common_update $SCM_URL $SCM_TAG $BUILD_SRC $SCM_HASH + +HOST_PKG_CONFIG_PATH=`command -v pkg-config` +if [ -z ${HOST_PKG_CONFIG_PATH} ]; then + echo "(*) pkg-config command not found\n" + exit 1 +fi + + +for ARCH in $BUILD_ARCH +do + case ${ARCH} in + armeabi-v7a) + TARGET_CPU="armv7-a" + TARGET_ARCH="armv7-a" + ARCH_OPTIONS=" --enable-neon --enable-asm --enable-inline-asm" + ;; + arm64-v8a) + TARGET_CPU="armv8-a" + TARGET_ARCH="aarch64" + ARCH_OPTIONS=" --enable-neon --enable-asm --enable-inline-asm" + ;; + x86) + TARGET_CPU="i686" + TARGET_ARCH="i686" + + # asm disabled due to this ticker https://trac.ffmpeg.org/ticket/4928 + ARCH_OPTIONS=" --disable-neon --disable-asm --disable-inline-asm" + ;; + x86_64) + TARGET_CPU="x86_64" + TARGET_ARCH="x86_64" + ARCH_OPTIONS=" --disable-neon --enable-asm --enable-inline-asm" + ;; + esac + + build $ARCH + common_run cp -L $BUILD_DST/$ARCH/lib/*.so $BUILD_DST/$ARCH/ + + common_run export PATH=$OLD_PATH +done diff --git a/scripts/android-build-freerdp.sh b/scripts/android-build-freerdp.sh index 40f26f2f2af7..0caab58f3c46 100755 --- a/scripts/android-build-freerdp.sh +++ b/scripts/android-build-freerdp.sh @@ -1,12 +1,15 @@ #!/bin/bash -JPEG_TAG=master -OPENH264_TAG=master -OPENSSL_TAG=master +OPENH264_TAG=v2.3.1 +OPENH264_HASH=453afa66dacb560bc5fd0468aabee90c483741571bca820a39a1c07f0362dc32 +OPENSSL_TAG=openssl-1.1.1s +OPENSSL_HASH=c5ac01e760ee6ff0dab61d6b2bbd30146724d063eb322180c6f18a6f74e4b6aa +FFMPEG_TAG=n4.4.1 +FFMPEG_HASH=82b43cc67296bcd01a59ae6b327cdb50121d3a9e35f41a30de1edd71bb4a6666 -WITH_JPEG=0 WITH_OPENH264=0 WITH_OPENSSL=0 +WITH_FFMPEG=0 SRC_DIR=$(dirname "${BASH_SOURCE[0]}") SRC_DIR=$(realpath "$SRC_DIR") @@ -30,10 +33,6 @@ do SRC_DIR="$2" shift ;; - --jpeg) - WITH_JPEG=1 - shift - ;; --openh264) WITH_OPENH264=1 shift @@ -43,6 +42,10 @@ do ANDROID_NDK_OPENH264=$1 shift ;; + --ffmpeg) + WITH_FFMPEG=1 + shift + ;; --openssl) WITH_OPENSSL=1 shift @@ -71,6 +74,12 @@ do done common_parse_arguments $REMAINING +if [ -z ${WITH_MEDIACODEC+x} ]; +then + common_run echo "WITH_MEDIACODEC unset, defining WITH_MEDIACODEC=1" + WITH_MEDIACODEC=1 +fi + # clean up top if [ -d $BUILD_SRC ]; then @@ -90,79 +99,92 @@ CMAKE_CMD_ARGS="-DANDROID_NDK=$ANDROID_NDK \ -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \ -DFREERDP_EXTERNAL_PATH=$BUILD_DST \ + -DWITH_CLIENT_SDL=OFF \ -DCMAKE_MAKE_PROGRAM=make" BASE=$(pwd) for ARCH in $BUILD_ARCH do - # build dependencies. - if [ $WITH_JPEG -ne 0 ]; - then - if [ $BUILD_DEPS -ne 0 ]; - then - common_run bash $SCRIPT_PATH/android-build-jpeg.sh \ - --src $BUILD_SRC/jpeg --dst $BUILD_DST \ - --ndk $ANDROID_NDK \ - --arch $ARCH \ - --target $NDK_TARGET \ - --tag $JPEG_TAG - fi - CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_JPEG=ON" - else - CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_JPEG=OFF" - fi - if [ $WITH_OPENH264 -ne 0 ]; - then - if [ -z "$ANDROID_NDK_OPENH264" ] - then - echo - echo "Warning: Missing openh264-ndk, using $ANDROID_NDK" >&2 - echo - ANDROID_NDK_OPENH264=$ANDROID_NDK - fi - if [ $BUILD_DEPS -ne 0 ]; - then - common_run bash $SCRIPT_PATH/android-build-openh264.sh \ - --src $BUILD_SRC/openh264 --dst $BUILD_DST \ - --sdk "$ANDROID_SDK" \ - --ndk "$ANDROID_NDK_OPENH264" \ - --arch $ARCH \ - --target $NDK_TARGET \ - --tag $OPENH264_TAG - fi - CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_OPENH264=ON" - else - CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_OPENH264=OFF" - fi - if [ $WITH_OPENSSL -ne 0 ]; - then - if [ $BUILD_DEPS -ne 0 ]; - then - common_run bash $SCRIPT_PATH/android-build-openssl.sh \ - --src $BUILD_SRC/openssl --dst $BUILD_DST \ - --sdk "$ANDROID_SDK" \ - --ndk $ANDROID_NDK \ - --arch $ARCH \ - --target $NDK_TARGET \ - --tag $OPENSSL_TAG - fi - fi - - # Build and install the library. - if [ $DEPS_ONLY -eq 0 ]; - then - common_run cd $BASE - common_run mkdir -p $BUILD_SRC/freerdp-build/$ARCH - common_run cd $BUILD_SRC/freerdp-build/$ARCH - common_run export ANDROID_NDK=$ANDROID_NDK - common_run $CMAKE_PROGRAM $CMAKE_CMD_ARGS \ - -DANDROID_ABI=$ARCH \ - -DCMAKE_INSTALL_PREFIX=$BUILD_DST/$ARCH \ - -DCMAKE_INSTALL_LIBDIR=. \ - $SRC_DIR - echo $(pwd) - common_run cmake --build . --target install - fi + # build dependencies. + if [ $WITH_OPENH264 -ne 0 ]; + then + if [ -z "$ANDROID_NDK_OPENH264" ] + then + echo + echo "Warning: Missing openh264-ndk, using $ANDROID_NDK" >&2 + echo + ANDROID_NDK_OPENH264=$ANDROID_NDK + fi + if [ $BUILD_DEPS -ne 0 ]; + then + common_run bash $SCRIPT_PATH/android-build-openh264.sh \ + --src $BUILD_SRC/openh264 --dst $BUILD_DST \ + --sdk "$ANDROID_SDK" \ + --ndk "$ANDROID_NDK_OPENH264" \ + --arch $ARCH \ + --target $NDK_TARGET \ + --tag $OPENH264_TAG \ + --hash $OPENH264_HASH + fi + CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_OPENH264=ON" + else + CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_OPENH264=OFF" + fi + + if [ $WITH_MEDIACODEC -ne 0 ]; + then + CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_MEDIACODEC=ON" + else + CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_MEDIACODEC=OFF" + fi + + if [ $WITH_FFMPEG -ne 0 ]; + then + if [ $BUILD_DEPS -ne 0 ]; + then + common_run bash $SCRIPT_PATH/android-build-ffmpeg.sh \ + --src $BUILD_SRC/ffmpeg --dst $BUILD_DST \ + --sdk "$ANDROID_SDK" \ + --ndk "$ANDROID_NDK" \ + --arch $ARCH \ + --target $NDK_TARGET \ + --tag $FFMPEG_TAG \ + --hash $FFMPEG_HASH + fi + CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_FFMPEG=ON -DWITH_SWCALE=ON" + else + CMAKE_CMD_ARGS="$CMAKE_CMD_ARGS -DWITH_FFMPEG=OFF" + fi + if [ $WITH_OPENSSL -ne 0 ]; + then + if [ $BUILD_DEPS -ne 0 ]; + then + common_run bash $SCRIPT_PATH/android-build-openssl.sh \ + --src $BUILD_SRC/openssl --dst $BUILD_DST \ + --sdk "$ANDROID_SDK" \ + --ndk $ANDROID_NDK \ + --arch $ARCH \ + --target $NDK_TARGET \ + --tag $OPENSSL_TAG \ + --hash $OPENSSL_HASH + fi + fi + + # Build and install the library. + if [ $DEPS_ONLY -eq 0 ]; + then + common_run cd $BASE + common_run mkdir -p $BUILD_SRC/freerdp-build/$ARCH + common_run cd $BUILD_SRC/freerdp-build/$ARCH + common_run export ANDROID_NDK=$ANDROID_NDK + common_run $CMAKE_PROGRAM $CMAKE_CMD_ARGS \ + -DANDROID_ABI=$ARCH \ + -DCMAKE_INSTALL_PREFIX=$BUILD_DST/$ARCH \ + -DCMAKE_INSTALL_LIBDIR=. \ + $SRC_DIR + echo $(pwd) + common_run $CMAKE_PROGRAM --build . --target install + fi done echo "Successfully build library for architectures $BUILD_ARCH" diff --git a/scripts/android-build-jpeg.sh b/scripts/android-build-jpeg.sh deleted file mode 100755 index dafcf893ac4a..000000000000 --- a/scripts/android-build-jpeg.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -SCM_URL=https://github.com/akallabeth/jpeg8d -SCM_TAG=master - -source $(dirname "${BASH_SOURCE[0]}")/android-build-common.sh - -function usage { - echo $0 [arguments] - echo "\tThe script checks out the OpenH264 git repository" - echo "\tto a local source directory, builds and installs" - echo "\tthe library for all architectures defined to" - echo "\tthe destination directory." - echo "" - echo "\t[-s|--source-dir ]" - echo "\t[-d|--destination-dir ]" - echo "\t[-a|--arch ]" - echo "\t[-t|--tag ]" - echo "\t[--scm-url ]" - echo "\t[--ndk ]" - echo "\t[--sdk ]" - exit 1 -} - -function build { - echo "Building architectures $BUILD_ARCH..." - BASE=$(pwd) - common_run cd $BUILD_SRC - common_run $NDK_BUILD V=1 APP_ABI="${BUILD_ARCH}" NDK_TOOLCHAIN_VERSION=4.9 -j clean - common_run $NDK_BUILD V=1 APP_ABI="${BUILD_ARCH}" NDK_TOOLCHAIN_VERSION=4.9 -j - common_run cd $BASE -} - -# Run the main program. -common_parse_arguments $@ -common_check_requirements -common_update $SCM_URL $SCM_TAG $BUILD_SRC - -build - -common_copy $BUILD_SRC $BUILD_DST diff --git a/scripts/android-build-openh264.sh b/scripts/android-build-openh264.sh index 5e9abed2c182..44d39f660adc 100755 --- a/scripts/android-build-openh264.sh +++ b/scripts/android-build-openh264.sh @@ -1,6 +1,7 @@ #!/bin/bash -SCM_URL=https://github.com/cisco/openh264 -SCM_TAG=master +SCM_URL=https://github.com/cisco/openh264/archive +SCM_TAG=v2.3.1 +SCM_HASH=453afa66dacb560bc5fd0468aabee90c483741571bca820a39a1c07f0362dc32 source $(dirname "${BASH_SOURCE[0]}")/android-build-common.sh @@ -9,8 +10,8 @@ function build { BASE=$(pwd) common_run cd $BUILD_SRC PATH=$ANDROID_NDK:$PATH - MAKE="make PATH=$PATH ENABLEPIC=Yes OS=android NDKROOT=$ANDROID_NDK TARGET=android-$2 NDKLEVEL=$2 ARCH=$1 -j libraries" - + MAKE="make PATH=$PATH ENABLEPIC=Yes OS=android NDKROOT=$ANDROID_NDK NDK_TOOLCHAIN_VERSION=clang TARGET=android-$2 NDKLEVEL=$2 ARCH=$1 -j libraries" + common_run export QUIET_AR="$CCACHE " common_run export QUIET_ASM="$CCACHE " common_run export QUIET_CC="$CCACHE " @@ -26,7 +27,7 @@ function build { # Run the main program. common_parse_arguments $@ common_check_requirements -common_update $SCM_URL $SCM_TAG $BUILD_SRC +common_update $SCM_URL $SCM_TAG $BUILD_SRC $SCM_HASH for ARCH in $BUILD_ARCH diff --git a/scripts/android-build-openssl.sh b/scripts/android-build-openssl.sh index 834e53a21f27..d5c815741f47 100755 --- a/scripts/android-build-openssl.sh +++ b/scripts/android-build-openssl.sh @@ -1,7 +1,8 @@ #!/bin/bash -SCM_URL=https://github.com/openssl/openssl -SCM_TAG=master +SCM_URL=https://www.openssl.org/source +SCM_TAG=OpenSSL_1_1_1q +SCM_HASH=d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca COMPILER=4.9 @@ -43,7 +44,7 @@ function build { # Run the main program. common_parse_arguments $@ common_check_requirements -common_update $SCM_URL $SCM_TAG $BUILD_SRC +common_update $SCM_URL $SCM_TAG $BUILD_SRC $SCM_HASH ORG_PATH=$PATH for ARCH in $BUILD_ARCH @@ -74,4 +75,3 @@ then common_run mkdir -p $BUILD_DST/$ARCH/include fi common_run cp -L -R $BUILD_SRC/include/openssl $BUILD_DST/$ARCH/include/ - diff --git a/scripts/android-build-release.conf b/scripts/android-build-release.conf index 51ca44df9324..90059e49cccc 100644 --- a/scripts/android-build-release.conf +++ b/scripts/android-build-release.conf @@ -9,16 +9,20 @@ # If you require support for older NDK API levels, # create seperate configurations for each NDK API level # and architecture you want to support. -WITH_JPEG=0 -WITH_OPENH264=1 +WITH_OPENH264=0 WITH_OPENSSL=1 +WITH_FFMPEG=1 BUILD_DEPS=1 DEPS_ONLY=0 -NDK_TARGET=21 +NDK_TARGET=23 +WITH_MEDIACODEC=1 -JPEG_TAG=master -OPENH264_TAG=v1.8.0 # NOTE: NDK r15c or earlier needed in --openh624-ndk for v1.8.0 -OPENSSL_TAG=OpenSSL_1_1_1h +OPENH264_TAG=v2.3.1 +OPENH264_HASH=453afa66dacb560bc5fd0468aabee90c483741571bca820a39a1c07f0362dc32 +OPENSSL_TAG=openssl-1.1.1q +OPENSSL_HASH=d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca +FFMPEG_TAG=n4.4.1 +FFMPEG_HASH=82b43cc67296bcd01a59ae6b327cdb50121d3a9e35f41a30de1edd71bb4a6666 SRC_DIR=$SCRIPT_PATH/.. BUILD_DST=$SCRIPT_PATH/../client/Android/Studio/freeRDPCore/src/main/jniLibs diff --git a/scripts/android-build.conf b/scripts/android-build.conf index c8eb40c42c06..4bafc1e289ea 100644 --- a/scripts/android-build.conf +++ b/scripts/android-build.conf @@ -9,16 +9,20 @@ # If you require support for older NDK API levels, # create seperate configurations for each NDK API level # and architecture you want to support. -WITH_JPEG=0 WITH_OPENH264=0 WITH_OPENSSL=1 +WITH_FFMPEG=0 BUILD_DEPS=1 DEPS_ONLY=0 -NDK_TARGET=26 +NDK_TARGET=21 +WITH_MEDIACODEC=1 -JPEG_TAG=master -OPENH264_TAG=v1.8.0 # NOTE: NDK r15c or earlier needed in --openh624-ndk for v1.8.0 -OPENSSL_TAG=OpenSSL_1_1_1j +OPENH264_TAG=v2.3.1 +OPENH264_HASH=453afa66dacb560bc5fd0468aabee90c483741571bca820a39a1c07f0362dc32 +OPENSSL_TAG=openssl-1.1.1q +OPENSSL_HASH=d7939ce614029cdff0b6c20f0e2e5703158a489a72b2507b8bd51bf8c8fd10ca +FFMPEG_TAG=n4.4.1 +FFMPEG_HASH=82b43cc67296bcd01a59ae6b327cdb50121d3a9e35f41a30de1edd71bb4a6666 SRC_DIR=$SCRIPT_PATH/.. BUILD_DST=$SCRIPT_PATH/../client/Android/Studio/freeRDPCore/src/main/jniLibs @@ -26,4 +30,4 @@ BUILD_SRC=$SRC_DIR/build CMAKE_BUILD_TYPE=Debug -BUILD_ARCH="armeabi-v7a x86 arm64-v8a x86_64" +BUILD_ARCH="armeabi-v7a arm64-v8a" diff --git a/server/proxy/modules/capture/CMakeLists.txt b/server/proxy/modules/capture/CMakeLists.txt index 80ba3b77e1c6..4004aaa96437 100644 --- a/server/proxy/modules/capture/CMakeLists.txt +++ b/server/proxy/modules/capture/CMakeLists.txt @@ -17,17 +17,19 @@ # limitations under the License. # -set(PLUGIN_NAME "proxy-capture-plugin") +# deactivated: does not work -add_library(${PLUGIN_NAME} MODULE - cap_main.c - cap_config.c - cap_config.h - cap_protocol.c - cap_protocol.h -) - -set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "") -set_target_properties(${PLUGIN_NAME} PROPERTIES NO_SONAME 1) -set_target_properties(${PLUGIN_NAME} PROPERTIES -LIBRARY_OUTPUT_DIRECTORY "${FREERDP_PROXY_PLUGINDIR}") +#set(PLUGIN_NAME "proxy-capture-plugin") +# +#add_library(${PLUGIN_NAME} MODULE +# cap_main.c +# cap_config.c +# cap_config.h +# cap_protocol.c +# cap_protocol.h +#) +# +#set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "") +#set_target_properties(${PLUGIN_NAME} PROPERTIES NO_SONAME 1) +#set_target_properties(${PLUGIN_NAME} PROPERTIES +#LIBRARY_OUTPUT_DIRECTORY "${FREERDP_PROXY_PLUGINDIR}") diff --git a/server/proxy/pf_context.h b/server/proxy/pf_context.h index 7c49fa722f19..2a5b7a9ee5b3 100644 --- a/server/proxy/pf_context.h +++ b/server/proxy/pf_context.h @@ -94,6 +94,9 @@ struct p_client_context UINT64 frames_count; wHashTable* vc_ids; /* channel_name -> channel_id map */ + + BOOL input_state_sync_pending; + UINT32 input_state; }; typedef struct p_client_context pClientContext; diff --git a/server/proxy/pf_input.c b/server/proxy/pf_input.c index 2a9baaa9162d..9703ac0dae76 100644 --- a/server/proxy/pf_input.c +++ b/server/proxy/pf_input.c @@ -23,11 +23,28 @@ #include "pf_context.h" #include "pf_modules.h" +static BOOL pf_server_check_and_sync_input_state(pClientContext* pc) +{ + if (freerdp_get_state(&pc->context) < CONNECTION_STATE_ACTIVE) + return FALSE; + if (pc->input_state_sync_pending) + { + BOOL rc = freerdp_input_send_synchronize_event(pc->context.input, pc->input_state); + if (rc) + pc->input_state_sync_pending = FALSE; + } + return TRUE; +} + static BOOL pf_server_synchronize_event(rdpInput* input, UINT32 flags) { pServerContext* ps = (pServerContext*)input->context; pClientContext* pc = ps->pdata->pc; - return freerdp_input_send_synchronize_event(pc->context.input, flags); + + pc->input_state = flags; + pc->input_state_sync_pending = TRUE; + pf_server_check_and_sync_input_state(pc); + return TRUE; } static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) @@ -37,6 +54,9 @@ static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) proxyConfig* config = ps->pdata->config; proxyKeyboardEventInfo event; + if (!pf_server_check_and_sync_input_state(pc)) + return TRUE; + if (!config->Keyboard) return TRUE; @@ -55,6 +75,9 @@ static BOOL pf_server_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT pClientContext* pc = ps->pdata->pc; proxyConfig* config = ps->pdata->config; + if (!pf_server_check_and_sync_input_state(pc)) + return TRUE; + if (!config->Keyboard) return TRUE; @@ -68,6 +91,9 @@ static BOOL pf_server_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT1 proxyConfig* config = ps->pdata->config; proxyMouseEventInfo event; + if (!pf_server_check_and_sync_input_state(pc)) + return TRUE; + if (!config->Mouse) return TRUE; @@ -87,6 +113,9 @@ static BOOL pf_server_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 pClientContext* pc = ps->pdata->pc; proxyConfig* config = ps->pdata->config; + if (!pf_server_check_and_sync_input_state(pc)) + return TRUE; + if (!config->Mouse) return TRUE; diff --git a/server/proxy/pf_server.c b/server/proxy/pf_server.c index bfeeb61363ca..2ef54184a55c 100644 --- a/server/proxy/pf_server.c +++ b/server/proxy/pf_server.c @@ -118,7 +118,7 @@ static BOOL pf_server_get_target_info(rdpContext* context, rdpSettings* settings return FALSE; } - settings->ServerPort = config->TargetPort > 0 ? 3389 : settings->ServerPort; + settings->ServerPort = config->TargetPort > 0 ? config->TargetPort : 3389; return TRUE; } diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index e6bc67b6ed39..0a1519ba283a 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -596,11 +596,12 @@ shadow_client_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, static BOOL shadow_are_caps_filtered(const rdpSettings* settings, UINT32 caps) { const UINT32 filter = settings->GfxCapsFilter; - const UINT32 capList[] = { - RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, RDPGFX_CAPVERSION_10, - RDPGFX_CAPVERSION_101, RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, - RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, RDPGFX_CAPVERSION_106 - }; + const UINT32 capList[] = { RDPGFX_CAPVERSION_8, RDPGFX_CAPVERSION_81, + RDPGFX_CAPVERSION_10, RDPGFX_CAPVERSION_101, + RDPGFX_CAPVERSION_102, RDPGFX_CAPVERSION_103, + RDPGFX_CAPVERSION_104, RDPGFX_CAPVERSION_105, + RDPGFX_CAPVERSION_106, RDPGFX_CAPVERSION_106_ERR, + RDPGFX_CAPVERSION_107 }; UINT32 x; for (x = 0; x < ARRAYSIZE(capList); x++) @@ -682,10 +683,19 @@ static UINT shadow_client_rdpgfx_caps_advertise(RdpgfxServerContext* context, if (!shadow_client_refresh_rect(&client->context, 0, NULL)) return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, + capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_107, &rc)) + return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106, &rc)) return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, + capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_106_ERR, + &rc)) + return rc; + if (shadow_client_caps_test_version(context, h264, capsAdvertise->capsSets, capsAdvertise->capsSetCount, RDPGFX_CAPVERSION_105, &rc)) return rc; diff --git a/uwac/CMakeLists.txt b/uwac/CMakeLists.txt index 8ebd4952fedb..c97cd4f4c104 100644 --- a/uwac/CMakeLists.txt +++ b/uwac/CMakeLists.txt @@ -17,12 +17,20 @@ # Soname versioning set(UWAC_VERSION_MAJOR "0") -set(UWAC_VERSION_MINOR "1") -set(UWAC_VERSION_REVISION "1") +set(UWAC_VERSION_MINOR "2") +set(UWAC_VERSION_REVISION "0") set(UWAC_VERSION "${UWAC_VERSION_MAJOR}.${UWAC_VERSION_MINOR}.${UWAC_VERSION_REVISION}") set(UWAC_VERSION_FULL "${UWAC_VERSION}") set(UWAC_API_VERSION "${UWAC_VERSION_MAJOR}") +option(HAVE_PIXMAN_REGION "Use PIXMAN or FreeRDP for region calculations" OFF) + +if (HAVE_PIXMAN_REGION) + include(FindPkgConfig) + pkg_check_modules(pixman REQUIRED pixman-1) + include_directories(${pixman_INCLUDE_DIRS}) +endif() + add_subdirectory(include) add_subdirectory(libuwac) diff --git a/uwac/include/uwac/uwac.h b/uwac/include/uwac/uwac.h index 8bd37d44de92..a86b48b26b0a 100644 --- a/uwac/include/uwac/uwac.h +++ b/uwac/include/uwac/uwac.h @@ -107,7 +107,9 @@ enum UWAC_EVENT_CLIPBOARD_OFFER, UWAC_EVENT_OUTPUT_GEOMETRY, UWAC_EVENT_KEYBOARD_MODIFIERS, - UWAC_EVENT_POINTER_AXIS_DISCRETE + UWAC_EVENT_POINTER_AXIS_DISCRETE, + UWAC_EVENT_POINTER_FRAME, + UWAC_EVENT_POINTER_SOURCE }; /** @brief window states */ @@ -195,6 +197,23 @@ struct uwac_pointer_axis_event }; typedef struct uwac_pointer_axis_event UwacPointerAxisEvent; +struct uwac_pointer_frame_event +{ + int type; + UwacWindow* window; + UwacSeat* seat; +}; +typedef struct uwac_pointer_frame_event UwacPointerFrameEvent; + +struct uwac_pointer_source_event +{ + int type; + UwacWindow* window; + UwacSeat* seat; + enum wl_pointer_axis_source axis_source; +}; +typedef struct uwac_pointer_source_event UwacPointerSourceEvent; + struct uwac_touch_frame_event { int type; @@ -286,6 +305,8 @@ union uwac_event { UwacPointerMotionEvent mouse_motion; UwacPointerButtonEvent mouse_button; UwacPointerAxisEvent mouse_axis; + UwacPointerFrameEvent mouse_frame; + UwacPointerSourceEvent mouse_source; UwacKeyboardEnterLeaveEvent keyboard_enter_leave; UwacKeyboardModifiersEvent keyboard_modifiers; UwacClipboardEvent clipboard; @@ -551,10 +572,18 @@ extern "C" UWAC_API void UwacWindowSetTitle(UwacWindow* window, const char* name); /** + * Sets the app id of the UwacWindow * - * @param display - * @param timeout - * @return + * @param window the UwacWindow + * @param app_id app id + */ + UWAC_API void UwacWindowSetAppId(UwacWindow* window, const char* app_id); + + /** Dispatch the display + * + * @param display The display to dispatch + * @param timeout The maximum time to wait in milliseconds (-1 == infinite). + * @return 1 for success, 0 if display not running, -1 on failure */ UWAC_API int UwacDisplayDispatch(UwacDisplay* display, int timeout); diff --git a/uwac/libuwac/CMakeLists.txt b/uwac/libuwac/CMakeLists.txt index cfadba8ae150..f66a8c4aacd9 100644 --- a/uwac/libuwac/CMakeLists.txt +++ b/uwac/libuwac/CMakeLists.txt @@ -70,13 +70,17 @@ set(${MODULE_PREFIX}_SRCS add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) - set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${UWAC_API_VERSION}) if (WITH_LIBRARY_VERSIONING) set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${UWAC_VERSION} SOVERSION ${UWAC_API_VERSION}) endif() -target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} ${PRIVATE_KEYWORD} ${WAYLAND_LIBS} ${XKBCOMMON_LIBS} ${EPOLLSHIM_LIBS} freerdp) +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} ${PRIVATE_KEYWORD} ${WAYLAND_LIBS} ${XKBCOMMON_LIBS} ${EPOLLSHIM_LIBS}) +if (HAVE_PIXMAN_REGION) + target_link_libraries(${MODULE_NAME} PRIVATE ${pixman_LINK_LIBRARIES}) +else() + target_link_libraries(${MODULE_NAME} PRIVATE freerdp) +endif() install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} EXPORT uwac) diff --git a/uwac/libuwac/uwac-clipboard.c b/uwac/libuwac/uwac-clipboard.c index 1d5c8fbf0002..6811bfb95abd 100644 --- a/uwac/libuwac/uwac-clipboard.c +++ b/uwac/libuwac/uwac-clipboard.c @@ -54,7 +54,7 @@ static void data_offer_offer(void* data, struct wl_data_offer* data_offer, else { event->seat = seat; - sprintf_s(event->mime, sizeof(event->mime), "%s", offered_mime_type); + snprintf(event->mime, sizeof(event->mime), "%s", offered_mime_type); } } } diff --git a/uwac/libuwac/uwac-input.c b/uwac/libuwac/uwac-input.c index 1e02001672f4..0d448c3c58e3 100644 --- a/uwac/libuwac/uwac-input.c +++ b/uwac/libuwac/uwac-input.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -71,8 +72,8 @@ static struct wl_buffer* create_pointer_buffer(UwacSeat* seat, const void* src, wl_shm_pool_destroy(pool); if (munmap(data, size) < 0) - fprintf(stderr, "%s: munmap(%p, %" PRIuz ") failed with [%d] %s\n", __FUNCTION__, data, - size, errno, strerror(errno)); + fprintf(stderr, "%s: munmap(%p, %z) failed with [%d] %s\n", __FUNCTION__, data, size, errno, + strerror(errno)); error_mmap: close(fd); @@ -136,7 +137,7 @@ static UwacReturnCode set_cursor_image(UwacSeat* seat, uint32_t serial) if (surface && buffer_add_listener_success > -1) { - wl_surface_attach(surface, buffer, -x, -y); + wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage(surface, 0, 0, image->width, image->height); wl_surface_commit(surface); } @@ -731,6 +732,7 @@ static void pointer_handle_enter(void* data, struct wl_pointer* pointer, uint32_ } input->display->serial = serial; + input->display->pointer_focus_serial = serial; window = wl_surface_get_user_data(surface); if (window) window->pointer_enter_serial = serial; @@ -844,12 +846,37 @@ static void pointer_handle_axis(void* data, struct wl_pointer* pointer, uint32_t static void pointer_frame(void* data, struct wl_pointer* wl_pointer) { - /*UwacSeat *seat = data;*/ + UwacPointerFrameEvent* event; + UwacSeat* seat = data; + UwacWindow* window = seat->pointer_focus; + + if (!window) + return; + + event = (UwacPointerFrameEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_POINTER_FRAME); + if (!event) + return; + + event->seat = seat; + event->window = window; } static void pointer_axis_source(void* data, struct wl_pointer* wl_pointer, uint32_t axis_source) { - /*UwacSeat *seat = data;*/ + UwacPointerSourceEvent* event; + UwacSeat* seat = data; + UwacWindow* window = seat->pointer_focus; + + if (!window) + return; + + event = (UwacPointerSourceEvent*)UwacDisplayNewEvent(seat->display, UWAC_EVENT_POINTER_SOURCE); + if (!event) + return; + + event->seat = seat; + event->window = window; + event->axis_source = axis_source; } static void pointer_axis_stop(void* data, struct wl_pointer* wl_pointer, uint32_t time, @@ -1161,5 +1188,5 @@ UwacReturnCode UwacSeatSetMouseCursor(UwacSeat* seat, const void* data, size_t l { seat->pointer_type = 1; } - return set_cursor_image(seat, seat->display->serial); + return set_cursor_image(seat, seat->display->pointer_focus_serial); } diff --git a/uwac/libuwac/uwac-priv.h b/uwac/libuwac/uwac-priv.h index aed38986afe3..69556bfc6174 100644 --- a/uwac/libuwac/uwac-priv.h +++ b/uwac/libuwac/uwac-priv.h @@ -121,6 +121,7 @@ struct uwac_display bool running; UwacTask dispatch_fd_task; uint32_t serial; + uint32_t pointer_focus_serial; struct wl_list windows; @@ -237,8 +238,8 @@ struct uwac_window struct wl_region* opaque_region; struct wl_region* input_region; - SSIZE_T drawingBufferIdx; - SSIZE_T pendingBufferIdx; + ssize_t drawingBufferIdx; + ssize_t pendingBufferIdx; struct wl_surface* surface; struct wl_shell_surface* shell_surface; struct xdg_surface* xdg_surface; @@ -255,6 +256,14 @@ struct uwac_window int pointer_current_cursor; }; +/**@brief data to pass to wl_buffer release listener */ +struct uwac_buffer_release_data +{ + UwacWindow* window; + int bufferIdx; +}; +typedef struct uwac_buffer_release_data UwacBufferReleaseData; + /* in uwa-display.c */ UwacEvent* UwacDisplayNewEvent(UwacDisplay* d, int type); int UwacDisplayWatchFd(UwacDisplay* display, int fd, uint32_t events, UwacTask* task); diff --git a/uwac/libuwac/uwac-window.c b/uwac/libuwac/uwac-window.c index bf70af2f1d87..c1e2b798378a 100644 --- a/uwac/libuwac/uwac-window.c +++ b/uwac/libuwac/uwac-window.c @@ -46,7 +46,8 @@ static int bppFromShmFormat(enum wl_shm_format format) static void buffer_release(void* data, struct wl_buffer* buffer) { - UwacBuffer* uwacBuffer = (UwacBuffer*)data; + UwacBufferReleaseData* releaseData = data; + UwacBuffer* uwacBuffer = &releaseData->window->buffers[releaseData->bufferIdx]; uwacBuffer->used = false; } @@ -64,7 +65,10 @@ static void UwacWindowDestroyBuffers(UwacWindow* w) #else region16_uninit(&buffer->damage); #endif + UwacBufferReleaseData* releaseData = + (UwacBufferReleaseData*)wl_buffer_get_user_data(buffer->wayland_buffer); wl_buffer_destroy(buffer->wayland_buffer); + free(releaseData); munmap(buffer->data, buffer->size); } @@ -316,14 +320,14 @@ int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32 w->buffers = newBuffers; memset(w->buffers + w->nbuffers, 0, sizeof(UwacBuffer) * nbuffers); - fd = uwac_create_anonymous_file(allocSize * nbuffers * 1ULL); + fd = uwac_create_anonymous_file(1ull * allocSize * nbuffers); if (fd < 0) { return UWAC_ERROR_INTERNAL; } - data = mmap(NULL, allocSize * nbuffers * 1ULL, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + data = mmap(NULL, 1ull * allocSize * nbuffers, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { @@ -335,14 +339,15 @@ int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32 if (!pool) { - munmap(data, allocSize * nbuffers * 1ULL); + munmap(data, 1ull * allocSize * nbuffers); ret = UWAC_ERROR_NOMEMORY; goto error_mmap; } for (i = 0; i < nbuffers; i++) { - UwacBuffer* buffer = &w->buffers[w->nbuffers + i]; + int bufferIdx = w->nbuffers + i; + UwacBuffer* buffer = &w->buffers[bufferIdx]; #ifdef HAVE_PIXMAN_REGION pixman_region32_init(&buffer->damage); #else @@ -352,7 +357,10 @@ int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32 buffer->size = allocSize; buffer->wayland_buffer = wl_shm_pool_create_buffer(pool, allocSize * i, width, height, w->stride, format); - wl_buffer_add_listener(buffer->wayland_buffer, &buffer_listener, buffer); + UwacBufferReleaseData* listener_data = xmalloc(sizeof(UwacBufferReleaseData)); + listener_data->window = w; + listener_data->bufferIdx = bufferIdx; + wl_buffer_add_listener(buffer->wayland_buffer, &buffer_listener, listener_data); } wl_shm_pool_destroy(pool); @@ -362,9 +370,9 @@ int UwacWindowShmAllocBuffers(UwacWindow* w, int nbuffers, int allocSize, uint32 return ret; } -static UwacBuffer* UwacWindowFindFreeBuffer(UwacWindow* w, SSIZE_T* index) +static UwacBuffer* UwacWindowFindFreeBuffer(UwacWindow* w, ssize_t* index) { - SSIZE_T i; + ssize_t i; int ret; if (index) @@ -629,7 +637,7 @@ static const struct wl_callback_listener frame_listener = { frame_done_cb }; #ifdef HAVE_PIXMAN_REGION static void damage_surface(UwacWindow* window, UwacBuffer* buffer) { - UINT32 nrects, i; + uint32_t nrects, i; const pixman_box32_t* box = pixman_region32_rectangles(&buffer->damage, &nrects); for (i = 0; i < nrects; i++, box++) @@ -681,7 +689,7 @@ static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t tim UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - UwacBuffer* buf = window->drawingBuffer; + UwacBuffer* buf = &window->buffers[window->drawingBufferIdx]; if (!pixman_region32_union_rect(&buf->damage, &buf->damage, x, y, width, height)) return UWAC_ERROR_INTERNAL; @@ -756,7 +764,7 @@ UwacReturnCode UwacWindowSubmitBuffer(UwacWindow* window, bool copyContentForNex if (copyContentForNextFrame) memcpy(nextDrawingBuffer->data, pendingBuffer->data, - window->stride * window->height * 1ULL); + 1ull * window->stride * window->height); UwacSubmitBufferPtr(window, pendingBuffer); return UWAC_SUCCESS; @@ -809,3 +817,9 @@ void UwacWindowSetTitle(UwacWindow* window, const char* name) else if (window->shell_surface) wl_shell_surface_set_title(window->shell_surface, name); } + +void UwacWindowSetAppId(UwacWindow* window, const char* app_id) +{ + if (window->xdg_toplevel) + xdg_toplevel_set_app_id(window->xdg_toplevel, app_id); +} diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt index 0cb8845b0d60..03f458e1d92e 100644 --- a/winpr/CMakeLists.txt +++ b/winpr/CMakeLists.txt @@ -21,6 +21,12 @@ project(WinPR C) set(CMAKE_COLOR_MAKEFILE ON) +option(WITH_VERBOSE_WINPR_ASSERT "Compile with verbose WINPR_ASSERT." ON) + +if (WITH_VERBOSE_WINPR_ASSERT) + add_definitions(-DWITH_VERBOSE_WINPR_ASSERT) +endif() + if(FREERDP_VERSION) set(FREERDP_BUILD 1) endif() @@ -51,7 +57,7 @@ if (NOT WIN32) endif() # Soname versioning -set(RAW_VERSION_STRING "2.4.0") +set(RAW_VERSION_STRING "2.11.8") if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag") file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) elseif(USE_VERSION_FROM_GIT_TAG) @@ -76,10 +82,24 @@ else() set(WINPR_VERSION_FULL "${WINPR_VERSION}") endif() +option(WITH_INTERNAL_MD4 "Use compiled in md4 hash functions instead of OpenSSL/MBedTLS" OFF) +option(WITH_INTERNAL_MD5 "Use compiled in md5 hash functions instead of OpenSSL/MBedTLS" OFF) if(NOT IOS) check_include_files(stdbool.h WINPR_HAVE_STDBOOL_H) check_include_files(stdint.h WINPR_HAVE_STDINT_H) check_include_files(inttypes.h WINPR_HAVE_INTTYPES_H) + check_include_files(execinfo.h HAVE_EXECINFO_HEADER) + if (HAVE_EXECINFO_HEADER) + check_symbol_exists(backtrace execinfo.h HAVE_EXECINFO_BACKTRACE) + check_symbol_exists(backtrace_symbols execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS) + check_symbol_exists(backtrace_symbols_fd execinfo.h HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD) + + # Some implementations (e.g. Android NDK API < 33) provide execinfo.h but do not define + # the backtrace functions. Disable detection for these cases + if (HAVE_EXECINFO_BACKTRACE AND HAVE_EXECINFO_BACKTRACE_SYMBOLS AND HAVE_EXECINFO_BACKTRACE_SYMBOLS_FD) + set(HAVE_EXECINFO_H ON) + endif() + endif() else(NOT IOS) set(WINPR_HAVE_STDBOOL_H 1) set(WINPR_HAVE_STDINT_H 1) @@ -101,13 +121,16 @@ else() if(NOT IOS) check_include_files(fcntl.h HAVE_FCNTL_H) check_include_files(unistd.h HAVE_UNISTD_H) - check_include_files(execinfo.h HAVE_EXECINFO_H) check_include_files(inttypes.h HAVE_INTTYPES_H) check_include_files(sys/modem.h HAVE_SYS_MODEM_H) check_include_files(sys/filio.h HAVE_SYS_FILIO_H) check_include_files(sys/sockio.h HAVE_SYS_SOCKIO_H) check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H) check_include_files(sys/select.h HAVE_SYS_SELECT_H) + check_include_files(unwind.h HAVE_UNWIND_H) + if (HAVE_UNWIND_H) + add_definitions(-DHAVE_UNWIND_H) + endif() else() set(HAVE_FCNTL_H 1) set(HAVE_UNISTD_H 1) @@ -121,7 +144,7 @@ else() set(HAVE_TM_GMTOFF 1) endif() - if(NOT WIN32) + if(NOT WIN32 AND NOT IOS) CHECK_SYMBOL_EXISTS(pthread_mutex_timedlock pthread.h HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) if (NOT HAVE_PTHREAD_MUTEX_TIMEDLOCK_SYMBOL) CHECK_LIBRARY_EXISTS(pthread pthread_mutex_timedlock "" HAVE_PTHREAD_MUTEX_TIMEDLOCK_LIB) diff --git a/winpr/include/winpr/assert.h b/winpr/include/winpr/assert.h new file mode 100644 index 000000000000..0cef84132892 --- /dev/null +++ b/winpr/include/winpr/assert.h @@ -0,0 +1,59 @@ +/** + * WinPR: Windows Portable Runtime + * Runtime ASSERT macros + * + * Copyright 2021 Armin Novak + * Copyright 2021 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_ASSERT_H +#define WINPR_ASSERT_H + +#include +#include +#include +#include +#include + +#if defined(WITH_VERBOSE_WINPR_ASSERT) && (WITH_VERBOSE_WINPR_ASSERT != 0) +#define WINPR_ASSERT(cond) \ + do \ + { \ + if (!(cond)) \ + { \ + wLog* _log_cached_ptr = WLog_Get("com.freerdp.winpr.assert"); \ + WLog_Print(_log_cached_ptr, WLOG_FATAL, "%s [%s:%s:%" PRIuz "]", #cond, __FILE__, \ + __FUNCTION__, __LINE__); \ + winpr_log_backtrace_ex(_log_cached_ptr, WLOG_FATAL, 20); \ + abort(); \ + } \ + } while (0) +#else +#define WINPR_ASSERT(cond) \ + do \ + { \ + } while (0) +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_ERROR_H */ diff --git a/winpr/include/winpr/clipboard.h b/winpr/include/winpr/clipboard.h index bbf2f5f9573b..95fb913e2aa8 100644 --- a/winpr/include/winpr/clipboard.h +++ b/winpr/include/winpr/clipboard.h @@ -64,6 +64,8 @@ struct _wClipboardDelegate (wClipboardDelegate*, const wClipboardFileRangeRequest*, const BYTE* data, UINT32 size); UINT(*ClipboardFileRangeFailure) (wClipboardDelegate*, const wClipboardFileRangeRequest*, UINT errorCode); + + BOOL (*IsFileNameComponentValid)(LPCWSTR lpFileName); }; #ifdef __cplusplus diff --git a/winpr/include/winpr/crt.h b/winpr/include/winpr/crt.h index 181ac14b6afe..40fde55a9ffd 100644 --- a/winpr/include/winpr/crt.h +++ b/winpr/include/winpr/crt.h @@ -32,41 +32,47 @@ #ifndef _WIN32 +#include + +#ifndef _write +#define _write write +#endif + #ifndef _strtoui64 #define _strtoui64 strtoull -#endif +#endif /* _strtoui64 */ #ifndef _strtoi64 #define _strtoi64 strtoll -#endif +#endif /* _strtoi64 */ #ifndef _rotl static INLINE UINT32 _rotl(UINT32 value, int shift) { return (value << shift) | (value >> (32 - shift)); } -#endif +#endif /* _rotl */ #ifndef _rotl64 static INLINE UINT64 _rotl64(UINT64 value, int shift) { return (value << shift) | (value >> (64 - shift)); } -#endif +#endif /* _rotl64 */ #ifndef _rotr static INLINE UINT32 _rotr(UINT32 value, int shift) { return (value >> shift) | (value << (32 - shift)); } -#endif +#endif /* _rotr */ #ifndef _rotr64 static INLINE UINT64 _rotr64(UINT64 value, int shift) { return (value >> shift) | (value << (64 - shift)); } -#endif +#endif /* _rotr64 */ #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) @@ -89,7 +95,7 @@ static INLINE UINT64 _byteswap_uint64(UINT64 _val) ((_val) >> 56)); } -#endif +#endif /* (__GNUC__ > 4) || ... */ #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)) @@ -102,7 +108,7 @@ static INLINE UINT16 _byteswap_ushort(UINT16 _val) return (((_val) >> 8) | ((_val) << 8)); } -#endif +#endif /* (__GNUC__ > 4) || ... */ #define CopyMemory(Destination, Source, Length) memcpy((Destination), (Source), (Length)) #define MoveMemory(Destination, Source, Length) memmove((Destination), (Source), (Length)) @@ -125,7 +131,7 @@ extern "C" #ifndef _ERRNO_T_DEFINED #define _ERRNO_T_DEFINED typedef int errno_t; -#endif +#endif /* _ERRNO_T_DEFINED */ #ifdef __cplusplus extern "C" @@ -160,6 +166,6 @@ extern "C" } #endif -#endif +#endif /* _WIN32 */ #endif /* WINPR_CRT_H */ diff --git a/winpr/include/winpr/crypto.h b/winpr/include/winpr/crypto.h index 67c2d99894b4..df38fedb708a 100644 --- a/winpr/include/winpr/crypto.h +++ b/winpr/include/winpr/crypto.h @@ -20,829 +20,7 @@ #ifndef WINPR_CRYPTO_H #define WINPR_CRYPTO_H -#include -#include - -#include - -#ifdef _WIN32 - -#include - -#endif - -#ifndef ALG_TYPE_RESERVED7 -#define ALG_TYPE_RESERVED7 (7 << 9) -#endif - -#if (NTDDI_VERSION <= 0x05010200) -#define ALG_SID_SHA_256 12 -#define ALG_SID_SHA_384 13 -#define ALG_SID_SHA_512 14 -#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) -#define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) -#define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) -#endif - -#ifndef _WIN32 - -/* ncrypt.h */ - -typedef ULONG_PTR NCRYPT_HANDLE; -typedef ULONG_PTR NCRYPT_PROV_HANDLE; -typedef ULONG_PTR NCRYPT_KEY_HANDLE; -typedef ULONG_PTR NCRYPT_HASH_HANDLE; -typedef ULONG_PTR NCRYPT_SECRET_HANDLE; - -/* wincrypt.h */ - -#define GET_ALG_CLASS(x) (x & (7 << 13)) -#define GET_ALG_TYPE(x) (x & (15 << 9)) -#define GET_ALG_SID(x) (x & (511)) - -#define ALG_CLASS_ANY (0) -#define ALG_CLASS_SIGNATURE (1 << 13) -#define ALG_CLASS_MSG_ENCRYPT (2 << 13) -#define ALG_CLASS_DATA_ENCRYPT (3 << 13) -#define ALG_CLASS_HASH (4 << 13) -#define ALG_CLASS_KEY_EXCHANGE (5 << 13) -#define ALG_CLASS_ALL (7 << 13) - -#define ALG_TYPE_ANY (0) -#define ALG_TYPE_DSS (1 << 9) -#define ALG_TYPE_RSA (2 << 9) -#define ALG_TYPE_BLOCK (3 << 9) -#define ALG_TYPE_STREAM (4 << 9) -#define ALG_TYPE_DH (5 << 9) -#define ALG_TYPE_SECURECHANNEL (6 << 9) - -#define ALG_SID_ANY (0) - -#define ALG_SID_RSA_ANY 0 -#define ALG_SID_RSA_PKCS 1 -#define ALG_SID_RSA_MSATWORK 2 -#define ALG_SID_RSA_ENTRUST 3 -#define ALG_SID_RSA_PGP 4 - -#define ALG_SID_DSS_ANY 0 -#define ALG_SID_DSS_PKCS 1 -#define ALG_SID_DSS_DMS 2 - -#define ALG_SID_DES 1 -#define ALG_SID_3DES 3 -#define ALG_SID_DESX 4 -#define ALG_SID_IDEA 5 -#define ALG_SID_CAST 6 -#define ALG_SID_SAFERSK64 7 -#define ALG_SID_SAFERSK128 8 -#define ALG_SID_3DES_112 9 -#define ALG_SID_CYLINK_MEK 12 -#define ALG_SID_RC5 13 - -#define ALG_SID_AES_128 14 -#define ALG_SID_AES_192 15 -#define ALG_SID_AES_256 16 -#define ALG_SID_AES 17 - -#define ALG_SID_SKIPJACK 10 -#define ALG_SID_TEK 11 - -#define CRYPT_MODE_CBCI 6 -#define CRYPT_MODE_CFBP 7 -#define CRYPT_MODE_OFBP 8 -#define CRYPT_MODE_CBCOFM 9 -#define CRYPT_MODE_CBCOFMI 10 - -#define ALG_SID_RC2 2 - -#define ALG_SID_RC4 1 -#define ALG_SID_SEAL 2 - -#define ALG_SID_DH_SANDF 1 -#define ALG_SID_DH_EPHEM 2 -#define ALG_SID_AGREED_KEY_ANY 3 -#define ALG_SID_KEA 4 - -#define ALG_SID_ECDH 5 - -#define ALG_SID_MD2 1 -#define ALG_SID_MD4 2 -#define ALG_SID_MD5 3 -#define ALG_SID_SHA 4 -#define ALG_SID_SHA1 4 -#define ALG_SID_MAC 5 -#define ALG_SID_RIPEMD 6 -#define ALG_SID_RIPEMD160 7 -#define ALG_SID_SSL3SHAMD5 8 -#define ALG_SID_HMAC 9 -#define ALG_SID_TLS1PRF 10 - -#define ALG_SID_HASH_REPLACE_OWF 11 - -#define ALG_SID_SHA_256 12 -#define ALG_SID_SHA_384 13 -#define ALG_SID_SHA_512 14 - -#define ALG_SID_SSL3_MASTER 1 -#define ALG_SID_SCHANNEL_MASTER_HASH 2 -#define ALG_SID_SCHANNEL_MAC_KEY 3 -#define ALG_SID_PCT1_MASTER 4 -#define ALG_SID_SSL2_MASTER 5 -#define ALG_SID_TLS1_MASTER 6 -#define ALG_SID_SCHANNEL_ENC_KEY 7 - -#define ALG_SID_ECMQV 1 - -#define CALG_MD2 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD2) -#define CALG_MD4 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4) -#define CALG_MD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5) -#define CALG_SHA (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA) -#define CALG_SHA1 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1) -#define CALG_MAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MAC) -#define CALG_RSA_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY) -#define CALG_DSS_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY) - -#define CALG_NO_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_ANY | ALG_SID_ANY) - -#define CALG_RSA_KEYX (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RSA | ALG_SID_RSA_ANY) -#define CALG_DES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DES) -#define CALG_3DES_112 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES_112) -#define CALG_3DES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES) -#define CALG_DESX (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DESX) -#define CALG_RC2 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC2) -#define CALG_RC4 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_RC4) -#define CALG_SEAL (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_SEAL) -#define CALG_DH_SF (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_DH_SANDF) -#define CALG_DH_EPHEM (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_DH_EPHEM) -#define CALG_AGREEDKEY_ANY (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_AGREED_KEY_ANY) -#define CALG_KEA_KEYX (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_KEA) -#define CALG_HUGHES_MD5 (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_MD5) -#define CALG_SKIPJACK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_SKIPJACK) -#define CALG_TEK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_TEK) -#define CALG_CYLINK_MEK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_CYLINK_MEK) -#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) -#define CALG_SSL3_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SSL3_MASTER) -#define CALG_SCHANNEL_MASTER_HASH \ - (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_MASTER_HASH) -#define CALG_SCHANNEL_MAC_KEY \ - (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_MAC_KEY) -#define CALG_SCHANNEL_ENC_KEY \ - (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_ENC_KEY) -#define CALG_PCT1_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_PCT1_MASTER) -#define CALG_SSL2_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SSL2_MASTER) -#define CALG_TLS1_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_TLS1_MASTER) -#define CALG_RC5 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC5) -#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) -#define CALG_TLS1PRF (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_TLS1PRF) - -#define CALG_HASH_REPLACE_OWF (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HASH_REPLACE_OWF) -#define CALG_AES_128 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_128) -#define CALG_AES_192 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_192) -#define CALG_AES_256 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256) -#define CALG_AES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES) - -#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) -#define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) -#define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) - -#define CALG_ECDH (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_ECDH) -#define CALG_ECMQV (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_ECMQV) - -typedef struct _CRYPTOAPI_BLOB -{ - DWORD cbData; - BYTE* pbData; -} CRYPT_INTEGER_BLOB, *PCRYPT_INTEGER_BLOB, CRYPT_UINT_BLOB, *PCRYPT_UINT_BLOB, CRYPT_OBJID_BLOB, - *PCRYPT_OBJID_BLOB, CERT_NAME_BLOB, *PCERT_NAME_BLOB, CERT_RDN_VALUE_BLOB, - *PCERT_RDN_VALUE_BLOB, CERT_BLOB, *PCERT_BLOB, CRL_BLOB, *PCRL_BLOB, DATA_BLOB, *PDATA_BLOB, - CRYPT_DATA_BLOB, *PCRYPT_DATA_BLOB, CRYPT_HASH_BLOB, *PCRYPT_HASH_BLOB, CRYPT_DIGEST_BLOB, - *PCRYPT_DIGEST_BLOB, CRYPT_DER_BLOB, *PCRYPT_DER_BLOB, CRYPT_ATTR_BLOB, *PCRYPT_ATTR_BLOB; - -typedef struct _CRYPT_ALGORITHM_IDENTIFIER -{ - LPSTR pszObjId; - CRYPT_OBJID_BLOB Parameters; -} CRYPT_ALGORITHM_IDENTIFIER, *PCRYPT_ALGORITHM_IDENTIFIER; - -typedef struct _CRYPT_BIT_BLOB -{ - DWORD cbData; - BYTE* pbData; - DWORD cUnusedBits; -} CRYPT_BIT_BLOB, *PCRYPT_BIT_BLOB; - -typedef struct _CERT_PUBLIC_KEY_INFO -{ - CRYPT_ALGORITHM_IDENTIFIER Algorithm; - CRYPT_BIT_BLOB PublicKey; -} CERT_PUBLIC_KEY_INFO, *PCERT_PUBLIC_KEY_INFO; - -typedef struct _CERT_EXTENSION -{ - LPSTR pszObjId; - BOOL fCritical; - CRYPT_OBJID_BLOB Value; -} CERT_EXTENSION, *PCERT_EXTENSION; -typedef const CERT_EXTENSION* PCCERT_EXTENSION; - -typedef struct _CERT_INFO -{ - DWORD dwVersion; - CRYPT_INTEGER_BLOB SerialNumber; - CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; - CERT_NAME_BLOB Issuer; - FILETIME NotBefore; - FILETIME NotAfter; - CERT_NAME_BLOB Subject; - CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo; - CRYPT_BIT_BLOB IssuerUniqueId; - CRYPT_BIT_BLOB SubjectUniqueId; - DWORD cExtension; - PCERT_EXTENSION rgExtension; -} CERT_INFO, *PCERT_INFO; - -typedef void* HCERTSTORE; -typedef ULONG_PTR HCRYPTPROV; -typedef ULONG_PTR HCRYPTPROV_LEGACY; - -typedef struct _CERT_CONTEXT -{ - DWORD dwCertEncodingType; - BYTE* pbCertEncoded; - DWORD cbCertEncoded; - PCERT_INFO pCertInfo; - HCERTSTORE hCertStore; -} CERT_CONTEXT, *PCERT_CONTEXT; -typedef const CERT_CONTEXT* PCCERT_CONTEXT; - -#define CERT_ENCODING_TYPE_MASK 0x0000FFFF -#define CMSG_ENCODING_TYPE_MASK 0xFFFF0000 -#define GET_CERT_ENCODING_TYPE(x) (x & CERT_ENCODING_TYPE_MASK) -#define GET_CMSG_ENCODING_TYPE(x) (x & CMSG_ENCODING_TYPE_MASK) - -#define CRYPT_ASN_ENCODING 0x00000001 -#define CRYPT_NDR_ENCODING 0x00000002 -#define X509_ASN_ENCODING 0x00000001 -#define X509_NDR_ENCODING 0x00000002 -#define PKCS_7_ASN_ENCODING 0x00010000 -#define PKCS_7_NDR_ENCODING 0x00020000 - -#define CERT_COMPARE_MASK 0xFFFF -#define CERT_COMPARE_SHIFT 16 -#define CERT_COMPARE_ANY 0 -#define CERT_COMPARE_SHA1_HASH 1 -#define CERT_COMPARE_NAME 2 -#define CERT_COMPARE_ATTR 3 -#define CERT_COMPARE_MD5_HASH 4 -#define CERT_COMPARE_PROPERTY 5 -#define CERT_COMPARE_PUBLIC_KEY 6 -#define CERT_COMPARE_HASH CERT_COMPARE_SHA1_HASH -#define CERT_COMPARE_NAME_STR_A 7 -#define CERT_COMPARE_NAME_STR_W 8 -#define CERT_COMPARE_KEY_SPEC 9 -#define CERT_COMPARE_ENHKEY_USAGE 10 -#define CERT_COMPARE_CTL_USAGE CERT_COMPARE_ENHKEY_USAGE -#define CERT_COMPARE_SUBJECT_CERT 11 -#define CERT_COMPARE_ISSUER_OF 12 -#define CERT_COMPARE_EXISTING 13 -#define CERT_COMPARE_SIGNATURE_HASH 14 -#define CERT_COMPARE_KEY_IDENTIFIER 15 -#define CERT_COMPARE_CERT_ID 16 -#define CERT_COMPARE_CROSS_CERT_DIST_POINTS 17 -#define CERT_COMPARE_PUBKEY_MD5_HASH 18 -#define CERT_COMPARE_SUBJECT_INFO_ACCESS 19 -#define CERT_COMPARE_HASH_STR 20 -#define CERT_COMPARE_HAS_PRIVATE_KEY 21 - -#define CERT_FIND_ANY (CERT_COMPARE_ANY << CERT_COMPARE_SHIFT) -#define CERT_FIND_SHA1_HASH (CERT_COMPARE_SHA1_HASH << CERT_COMPARE_SHIFT) -#define CERT_FIND_MD5_HASH (CERT_COMPARE_MD5_HASH << CERT_COMPARE_SHIFT) -#define CERT_FIND_SIGNATURE_HASH (CERT_COMPARE_SIGNATURE_HASH << CERT_COMPARE_SHIFT) -#define CERT_FIND_KEY_IDENTIFIER (CERT_COMPARE_KEY_IDENTIFIER << CERT_COMPARE_SHIFT) -#define CERT_FIND_HASH CERT_FIND_SHA1_HASH -#define CERT_FIND_PROPERTY (CERT_COMPARE_PROPERTY << CERT_COMPARE_SHIFT) -#define CERT_FIND_PUBLIC_KEY (CERT_COMPARE_PUBLIC_KEY << CERT_COMPARE_SHIFT) -#define CERT_FIND_SUBJECT_NAME (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) -#define CERT_FIND_SUBJECT_ATTR (CERT_COMPARE_ATTR << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) -#define CERT_FIND_ISSUER_NAME (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) -#define CERT_FIND_ISSUER_ATTR (CERT_COMPARE_ATTR << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) -#define CERT_FIND_SUBJECT_STR_A \ - (CERT_COMPARE_NAME_STR_A << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) -#define CERT_FIND_SUBJECT_STR_W \ - (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) -#define CERT_FIND_SUBJECT_STR CERT_FIND_SUBJECT_STR_W -#define CERT_FIND_ISSUER_STR_A \ - (CERT_COMPARE_NAME_STR_A << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) -#define CERT_FIND_ISSUER_STR_W \ - (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) -#define CERT_FIND_ISSUER_STR CERT_FIND_ISSUER_STR_W -#define CERT_FIND_KEY_SPEC (CERT_COMPARE_KEY_SPEC << CERT_COMPARE_SHIFT) -#define CERT_FIND_ENHKEY_USAGE (CERT_COMPARE_ENHKEY_USAGE << CERT_COMPARE_SHIFT) -#define CERT_FIND_CTL_USAGE CERT_FIND_ENHKEY_USAGE -#define CERT_FIND_SUBJECT_CERT (CERT_COMPARE_SUBJECT_CERT << CERT_COMPARE_SHIFT) -#define CERT_FIND_ISSUER_OF (CERT_COMPARE_ISSUER_OF << CERT_COMPARE_SHIFT) -#define CERT_FIND_EXISTING (CERT_COMPARE_EXISTING << CERT_COMPARE_SHIFT) -#define CERT_FIND_CERT_ID (CERT_COMPARE_CERT_ID << CERT_COMPARE_SHIFT) -#define CERT_FIND_CROSS_CERT_DIST_POINTS (CERT_COMPARE_CROSS_CERT_DIST_POINTS << CERT_COMPARE_SHIFT) -#define CERT_FIND_PUBKEY_MD5_HASH (CERT_COMPARE_PUBKEY_MD5_HASH << CERT_COMPARE_SHIFT) -#define CERT_FIND_SUBJECT_INFO_ACCESS (CERT_COMPARE_SUBJECT_INFO_ACCESS << CERT_COMPARE_SHIFT) -#define CERT_FIND_HASH_STR (CERT_COMPARE_HASH_STR << CERT_COMPARE_SHIFT) -#define CERT_FIND_HAS_PRIVATE_KEY (CERT_COMPARE_HAS_PRIVATE_KEY << CERT_COMPARE_SHIFT) - -#define CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG 0x1 -#define CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG 0x2 -#define CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG 0x4 -#define CERT_FIND_NO_ENHKEY_USAGE_FLAG 0x8 -#define CERT_FIND_OR_ENHKEY_USAGE_FLAG 0x10 -#define CERT_FIND_VALID_ENHKEY_USAGE_FLAG 0x20 -#define CERT_FIND_OPTIONAL_CTL_USAGE_FLAG CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG -#define CERT_FIND_EXT_ONLY_CTL_USAGE_FLAG CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG -#define CERT_FIND_PROP_ONLY_CTL_USAGE_FLAG CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG -#define CERT_FIND_NO_CTL_USAGE_FLAG CERT_FIND_NO_ENHKEY_USAGE_FLAG -#define CERT_FIND_OR_CTL_USAGE_FLAG CERT_FIND_OR_ENHKEY_USAGE_FLAG -#define CERT_FIND_VALID_CTL_USAGE_FLAG CERT_FIND_VALID_ENHKEY_USAGE_FLAG - -#define CERT_NAME_EMAIL_TYPE 1 -#define CERT_NAME_RDN_TYPE 2 -#define CERT_NAME_ATTR_TYPE 3 -#define CERT_NAME_SIMPLE_DISPLAY_TYPE 4 -#define CERT_NAME_FRIENDLY_DISPLAY_TYPE 5 -#define CERT_NAME_DNS_TYPE 6 -#define CERT_NAME_URL_TYPE 7 -#define CERT_NAME_UPN_TYPE 8 - -#define CERT_NAME_ISSUER_FLAG 0x1 -#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000 - -#define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 - -#define CERT_STORE_PROV_MSG ((LPCSTR)1) -#define CERT_STORE_PROV_MEMORY ((LPCSTR)2) -#define CERT_STORE_PROV_FILE ((LPCSTR)3) -#define CERT_STORE_PROV_REG ((LPCSTR)4) -#define CERT_STORE_PROV_PKCS7 ((LPCSTR)5) -#define CERT_STORE_PROV_SERIALIZED ((LPCSTR)6) -#define CERT_STORE_PROV_FILENAME_A ((LPCSTR)7) -#define CERT_STORE_PROV_FILENAME_W ((LPCSTR)8) -#define CERT_STORE_PROV_FILENAME CERT_STORE_PROV_FILENAME_W -#define CERT_STORE_PROV_SYSTEM_A ((LPCSTR)9) -#define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)10) -#define CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W -#define CERT_STORE_PROV_COLLECTION ((LPCSTR)11) -#define CERT_STORE_PROV_SYSTEM_REGISTRY_A ((LPCSTR)12) -#define CERT_STORE_PROV_SYSTEM_REGISTRY_W ((LPCSTR)13) -#define CERT_STORE_PROV_SYSTEM_REGISTRY CERT_STORE_PROV_SYSTEM_REGISTRY_W -#define CERT_STORE_PROV_PHYSICAL_W ((LPCSTR)14) -#define CERT_STORE_PROV_PHYSICAL CERT_STORE_PROV_PHYSICAL_W -#define CERT_STORE_PROV_SMART_CARD_W ((LPCSTR)15) -#define CERT_STORE_PROV_SMART_CARD CERT_STORE_PROV_SMART_CARD_W -#define CERT_STORE_PROV_LDAP_W ((LPCSTR)16) -#define CERT_STORE_PROV_LDAP CERT_STORE_PROV_LDAP_W -#define CERT_STORE_PROV_PKCS12 ((LPCSTR)17) -#define sz_CERT_STORE_PROV_MEMORY "Memory" -#define sz_CERT_STORE_PROV_FILENAME_W "File" -#define sz_CERT_STORE_PROV_FILENAME sz_CERT_STORE_PROV_FILENAME_W -#define sz_CERT_STORE_PROV_SYSTEM_W "System" -#define sz_CERT_STORE_PROV_SYSTEM sz_CERT_STORE_PROV_SYSTEM_W -#define sz_CERT_STORE_PROV_PKCS7 "PKCS7" -#define sz_CERT_STORE_PROV_PKCS12 "PKCS12" -#define sz_CERT_STORE_PROV_SERIALIZED "Serialized" -#define sz_CERT_STORE_PROV_COLLECTION "Collection" -#define sz_CERT_STORE_PROV_SYSTEM_REGISTRY_W "SystemRegistry" -#define sz_CERT_STORE_PROV_SYSTEM_REGISTRY sz_CERT_STORE_PROV_SYSTEM_REGISTRY_W -#define sz_CERT_STORE_PROV_PHYSICAL_W "Physical" -#define sz_CERT_STORE_PROV_PHYSICAL sz_CERT_STORE_PROV_PHYSICAL_W -#define sz_CERT_STORE_PROV_SMART_CARD_W "SmartCard" -#define sz_CERT_STORE_PROV_SMART_CARD sz_CERT_STORE_PROV_SMART_CARD_W -#define sz_CERT_STORE_PROV_LDAP_W "Ldap" -#define sz_CERT_STORE_PROV_LDAP sz_CERT_STORE_PROV_LDAP_W - -#define CERT_STORE_SIGNATURE_FLAG 0x00000001 -#define CERT_STORE_TIME_VALIDITY_FLAG 0x00000002 -#define CERT_STORE_REVOCATION_FLAG 0x00000004 -#define CERT_STORE_NO_CRL_FLAG 0x00010000 -#define CERT_STORE_NO_ISSUER_FLAG 0x00020000 -#define CERT_STORE_BASE_CRL_FLAG 0x00000100 -#define CERT_STORE_DELTA_CRL_FLAG 0x00000200 - -#define CERT_STORE_NO_CRYPT_RELEASE_FLAG 0x00000001 -#define CERT_STORE_SET_LOCALIZED_NAME_FLAG 0x00000002 -#define CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG 0x00000004 -#define CERT_STORE_DELETE_FLAG 0x00000010 -#define CERT_STORE_UNSAFE_PHYSICAL_FLAG 0x00000020 -#define CERT_STORE_SHARE_STORE_FLAG 0x00000040 -#define CERT_STORE_SHARE_CONTEXT_FLAG 0x00000080 -#define CERT_STORE_MANIFOLD_FLAG 0x00000100 -#define CERT_STORE_ENUM_ARCHIVED_FLAG 0x00000200 -#define CERT_STORE_UPDATE_KEYID_FLAG 0x00000400 -#define CERT_STORE_BACKUP_RESTORE_FLAG 0x00000800 -#define CERT_STORE_READONLY_FLAG 0x00008000 -#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 -#define CERT_STORE_CREATE_NEW_FLAG 0x00002000 -#define CERT_STORE_MAXIMUM_ALLOWED_FLAG 0x00001000 - -#define CERT_SYSTEM_STORE_MASK 0xFFFF0000 -#define CERT_SYSTEM_STORE_RELOCATE_FLAG 0x80000000 -#define CERT_SYSTEM_STORE_UNPROTECTED_FLAG 0x40000000 -#define CERT_SYSTEM_STORE_DEFER_READ_FLAG 0x20000000 -#define CERT_SYSTEM_STORE_LOCATION_MASK 0x00FF0000 -#define CERT_SYSTEM_STORE_LOCATION_SHIFT 16 -#define CERT_SYSTEM_STORE_CURRENT_USER_ID 1 -#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ID 2 -#define CERT_SYSTEM_STORE_CURRENT_SERVICE_ID 4 -#define CERT_SYSTEM_STORE_SERVICES_ID 5 -#define CERT_SYSTEM_STORE_USERS_ID 6 -#define CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY_ID 7 -#define CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY_ID 8 -#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE_ID 9 - -#define CERT_SYSTEM_STORE_CURRENT_USER \ - (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) -#define CERT_SYSTEM_STORE_LOCAL_MACHINE \ - (CERT_SYSTEM_STORE_LOCAL_MACHINE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) -#define CERT_SYSTEM_STORE_CURRENT_SERVICE \ - (CERT_SYSTEM_STORE_CURRENT_SERVICE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) -#define CERT_SYSTEM_STORE_SERVICES \ - (CERT_SYSTEM_STORE_SERVICES_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) -#define CERT_SYSTEM_STORE_USERS (CERT_SYSTEM_STORE_USERS_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) -#define CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY \ - (CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) -#define CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY \ - (CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) -#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE \ - (CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) - -HCERTSTORE CertOpenStore(LPCSTR lpszStoreProvider, DWORD dwMsgAndCertEncodingType, - HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const void* pvPara); - -WINPR_API HCERTSTORE CertOpenSystemStoreW(HCRYPTPROV_LEGACY hProv, LPCWSTR szSubsystemProtocol); -WINPR_API HCERTSTORE CertOpenSystemStoreA(HCRYPTPROV_LEGACY hProv, LPCSTR szSubsystemProtocol); - -WINPR_API BOOL CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags); - -#ifdef UNICODE -#define CertOpenSystemStore CertOpenSystemStoreW -#else -#define CertOpenSystemStore CertOpenSystemStoreA -#endif - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API PCCERT_CONTEXT CertFindCertificateInStore(HCERTSTORE hCertStore, - DWORD dwCertEncodingType, DWORD dwFindFlags, - DWORD dwFindType, const void* pvFindPara, - PCCERT_CONTEXT pPrevCertContext); - - WINPR_API PCCERT_CONTEXT CertEnumCertificatesInStore(HCERTSTORE hCertStore, - PCCERT_CONTEXT pPrevCertContext); - - WINPR_API DWORD CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, - void* pvTypePara, LPWSTR pszNameString, DWORD cchNameString); - WINPR_API DWORD CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, - void* pvTypePara, LPSTR pszNameString, DWORD cchNameString); - -#ifdef __cplusplus -} -#endif - -#ifdef UNICODE -#define CertGetNameString CertGetNameStringW -#else -#define CertGetNameString CertGetNameStringA -#endif - -/** - * Data Protection API (DPAPI) - */ - -#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 - -#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00000000 -#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x00000001 -#define CRYPTPROTECTMEMORY_SAME_LOGON 0x00000002 - -#define CRYPTPROTECT_PROMPT_ON_UNPROTECT 0x00000001 -#define CRYPTPROTECT_PROMPT_ON_PROTECT 0x00000002 -#define CRYPTPROTECT_PROMPT_RESERVED 0x00000004 -#define CRYPTPROTECT_PROMPT_STRONG 0x00000008 -#define CRYPTPROTECT_PROMPT_REQUIRE_STRONG 0x00000010 - -#define CRYPTPROTECT_UI_FORBIDDEN 0x1 -#define CRYPTPROTECT_LOCAL_MACHINE 0x4 -#define CRYPTPROTECT_CRED_SYNC 0x8 -#define CRYPTPROTECT_AUDIT 0x10 -#define CRYPTPROTECT_NO_RECOVERY 0x20 -#define CRYPTPROTECT_VERIFY_PROTECTION 0x40 -#define CRYPTPROTECT_CRED_REGENERATE 0x80 - -#define CRYPTPROTECT_FIRST_RESERVED_FLAGVAL 0x0FFFFFFF -#define CRYPTPROTECT_LAST_RESERVED_FLAGVAL 0xFFFFFFFF - -typedef struct _CRYPTPROTECT_PROMPTSTRUCT -{ - DWORD cbSize; - DWORD dwPromptFlags; - HWND hwndApp; - LPCWSTR szPrompt; -} CRYPTPROTECT_PROMPTSTRUCT, *PCRYPTPROTECT_PROMPTSTRUCT; - -#define CRYPTPROTECT_DEFAULT_PROVIDER \ - { \ - 0xdf9d8cd0, 0x1501, 0x11d1, \ - { \ - 0x8c, 0x7a, 0x00, 0xc0, 0x4f, 0xc2, 0x97, 0xeb \ - } \ - } - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); - WINPR_API BOOL CryptUnprotectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); - - WINPR_API BOOL CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, - DATA_BLOB* pOptionalEntropy, PVOID pvReserved, - CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, - DATA_BLOB* pDataOut); - WINPR_API BOOL CryptUnprotectData(DATA_BLOB* pDataIn, LPWSTR* ppszDataDescr, - DATA_BLOB* pOptionalEntropy, PVOID pvReserved, - CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, - DATA_BLOB* pDataOut); - -#ifdef __cplusplus -} -#endif - -#define CRYPT_STRING_BASE64HEADER 0x00000000 -#define CRYPT_STRING_BASE64 0x00000001 -#define CRYPT_STRING_BINARY 0x00000002 -#define CRYPT_STRING_BASE64REQUESTHEADER 0x00000003 -#define CRYPT_STRING_HEX 0x00000004 -#define CRYPT_STRING_HEXASCII 0x00000005 -#define CRYPT_STRING_BASE64_ANY 0x00000006 -#define CRYPT_STRING_ANY 0x00000007 -#define CRYPT_STRING_HEX_ANY 0x00000008 -#define CRYPT_STRING_BASE64X509CRLHEADER 0x00000009 -#define CRYPT_STRING_HEXADDR 0x0000000A -#define CRYPT_STRING_HEXASCIIADDR 0x0000000B -#define CRYPT_STRING_HEXRAW 0x0000000C - -#define CRYPT_STRING_HASHDATA 0x10000000 -#define CRYPT_STRING_STRICT 0x20000000 -#define CRYPT_STRING_NOCRLF 0x40000000 -#define CRYPT_STRING_NOCR 0x80000000 - -BOOL CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, - DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags); -BOOL CryptStringToBinaryA(LPCSTR pszString, DWORD cchString, DWORD dwFlags, BYTE* pbBinary, - DWORD* pcbBinary, DWORD* pdwSkip, DWORD* pdwFlags); - -BOOL CryptBinaryToStringW(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, - DWORD* pcchString); -BOOL CryptBinaryToStringA(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, LPSTR pszString, - DWORD* pcchString); - -#ifdef UNICODE -#define CryptStringToBinary CryptStringToBinaryW -#define CryptBinaryToString CryptBinaryToStringW -#else -#define CryptStringToBinary CryptStringToBinaryA -#define CryptBinaryToString CryptBinaryToStringA -#endif - -#endif - -#ifndef ALG_SID_ECSDA -#define ALG_SID_ECDSA 3 -#define CALG_ECDSA (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_ECDSA) -#endif - -/** - * Custom Crypto API Abstraction Layer - */ - -#define WINPR_MD4_DIGEST_LENGTH 16 -#define WINPR_MD5_DIGEST_LENGTH 16 -#define WINPR_SHA1_DIGEST_LENGTH 20 -#define WINPR_SHA224_DIGEST_LENGTH 28 -#define WINPR_SHA256_DIGEST_LENGTH 32 -#define WINPR_SHA384_DIGEST_LENGTH 48 -#define WINPR_SHA512_DIGEST_LENGTH 64 -#define WINPR_RIPEMD160_DIGEST_LENGTH 20 -#define WINPR_SHA3_224_DIGEST_LENGTH 28 -#define WINPR_SHA3_256_DIGEST_LENGTH 32 -#define WINPR_SHA3_384_DIGEST_LENGTH 48 -#define WINPR_SHA3_512_DIGEST_LENGTH 64 -#define WINPR_SHAKE128_DIGEST_LENGTH 16 -#define WINPR_SHAKE256_DIGEST_LENGTH 32 - -/** - * HMAC - */ -typedef enum -{ - WINPR_MD_NONE = 0, - WINPR_MD_MD2 = 1, - WINPR_MD_MD4 = 2, - WINPR_MD_MD5 = 3, - WINPR_MD_SHA1 = 4, - WINPR_MD_SHA224 = 5, - WINPR_MD_SHA256 = 6, - WINPR_MD_SHA384 = 7, - WINPR_MD_SHA512 = 8, - WINPR_MD_RIPEMD160 = 9, - WINPR_MD_SHA3_224 = 10, - WINPR_MD_SHA3_256 = 11, - WINPR_MD_SHA3_384 = 12, - WINPR_MD_SHA3_512 = 13, - WINPR_MD_SHAKE128 = 14, - WINPR_MD_SHAKE256 = 15 -} WINPR_MD_TYPE; - -typedef struct _winpr_hmac_ctx_private_st WINPR_HMAC_CTX; - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API WINPR_MD_TYPE winpr_md_type_from_string(const char* name); - WINPR_API const char* winpr_md_type_to_string(WINPR_MD_TYPE md); - - WINPR_API WINPR_HMAC_CTX* winpr_HMAC_New(void); - WINPR_API BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const BYTE* key, - size_t keylen); - WINPR_API BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const BYTE* input, size_t ilen); - WINPR_API BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, BYTE* output, size_t ilen); - WINPR_API void winpr_HMAC_Free(WINPR_HMAC_CTX* ctx); - WINPR_API BOOL winpr_HMAC(WINPR_MD_TYPE md, const BYTE* key, size_t keylen, const BYTE* input, - size_t ilen, BYTE* output, size_t olen); - -#ifdef __cplusplus -} -#endif - -/** - * Generic Digest API - */ - -typedef struct _winpr_digest_ctx_private_st WINPR_DIGEST_CTX; - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API WINPR_DIGEST_CTX* winpr_Digest_New(void); - WINPR_API BOOL winpr_Digest_Init_Allow_FIPS(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md); - WINPR_API BOOL winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md); - WINPR_API BOOL winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const BYTE* input, size_t ilen); - WINPR_API BOOL winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, BYTE* output, size_t ilen); - WINPR_API void winpr_Digest_Free(WINPR_DIGEST_CTX* ctx); - WINPR_API BOOL winpr_Digest_Allow_FIPS(WINPR_MD_TYPE md, const BYTE* input, size_t ilen, - BYTE* output, size_t olen); - WINPR_API BOOL winpr_Digest(WINPR_MD_TYPE md, const BYTE* input, size_t ilen, BYTE* output, - size_t olen); - -#ifdef __cplusplus -} -#endif - -/** - * Random Number Generation - */ - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API int winpr_RAND(BYTE* output, size_t len); - WINPR_API int winpr_RAND_pseudo(BYTE* output, size_t len); - -#ifdef __cplusplus -} -#endif - -/** - * RC4 - */ - -typedef struct _winpr_rc4_ctx_private_st WINPR_RC4_CTX; - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API WINPR_RC4_CTX* winpr_RC4_New_Allow_FIPS(const BYTE* key, size_t keylen); - WINPR_API WINPR_RC4_CTX* winpr_RC4_New(const BYTE* key, size_t keylen); - WINPR_API BOOL winpr_RC4_Update(WINPR_RC4_CTX* ctx, size_t length, const BYTE* input, - BYTE* output); - WINPR_API void winpr_RC4_Free(WINPR_RC4_CTX* ctx); - -#ifdef __cplusplus -} -#endif - -/** - * Generic Cipher API - */ - -#define WINPR_AES_BLOCK_SIZE 16 - -/* cipher operation types */ -#define WINPR_ENCRYPT 0 -#define WINPR_DECRYPT 1 - -/* cipher types */ -#define WINPR_CIPHER_NONE 0 -#define WINPR_CIPHER_NULL 1 -#define WINPR_CIPHER_AES_128_ECB 2 -#define WINPR_CIPHER_AES_192_ECB 3 -#define WINPR_CIPHER_AES_256_ECB 4 -#define WINPR_CIPHER_AES_128_CBC 5 -#define WINPR_CIPHER_AES_192_CBC 6 -#define WINPR_CIPHER_AES_256_CBC 7 -#define WINPR_CIPHER_AES_128_CFB128 8 -#define WINPR_CIPHER_AES_192_CFB128 9 -#define WINPR_CIPHER_AES_256_CFB128 10 -#define WINPR_CIPHER_AES_128_CTR 11 -#define WINPR_CIPHER_AES_192_CTR 12 -#define WINPR_CIPHER_AES_256_CTR 13 -#define WINPR_CIPHER_AES_128_GCM 14 -#define WINPR_CIPHER_AES_192_GCM 15 -#define WINPR_CIPHER_AES_256_GCM 16 -#define WINPR_CIPHER_CAMELLIA_128_ECB 17 -#define WINPR_CIPHER_CAMELLIA_192_ECB 18 -#define WINPR_CIPHER_CAMELLIA_256_ECB 19 -#define WINPR_CIPHER_CAMELLIA_128_CBC 20 -#define WINPR_CIPHER_CAMELLIA_192_CBC 21 -#define WINPR_CIPHER_CAMELLIA_256_CBC 22 -#define WINPR_CIPHER_CAMELLIA_128_CFB128 23 -#define WINPR_CIPHER_CAMELLIA_192_CFB128 24 -#define WINPR_CIPHER_CAMELLIA_256_CFB128 25 -#define WINPR_CIPHER_CAMELLIA_128_CTR 26 -#define WINPR_CIPHER_CAMELLIA_192_CTR 27 -#define WINPR_CIPHER_CAMELLIA_256_CTR 28 -#define WINPR_CIPHER_CAMELLIA_128_GCM 29 -#define WINPR_CIPHER_CAMELLIA_192_GCM 30 -#define WINPR_CIPHER_CAMELLIA_256_GCM 31 -#define WINPR_CIPHER_DES_ECB 32 -#define WINPR_CIPHER_DES_CBC 33 -#define WINPR_CIPHER_DES_EDE_ECB 34 -#define WINPR_CIPHER_DES_EDE_CBC 35 -#define WINPR_CIPHER_DES_EDE3_ECB 36 -#define WINPR_CIPHER_DES_EDE3_CBC 37 -#define WINPR_CIPHER_BLOWFISH_ECB 38 -#define WINPR_CIPHER_BLOWFISH_CBC 39 -#define WINPR_CIPHER_BLOWFISH_CFB64 40 -#define WINPR_CIPHER_BLOWFISH_CTR 41 -#define WINPR_CIPHER_ARC4_128 42 -#define WINPR_CIPHER_AES_128_CCM 43 -#define WINPR_CIPHER_AES_192_CCM 44 -#define WINPR_CIPHER_AES_256_CCM 45 -#define WINPR_CIPHER_CAMELLIA_128_CCM 46 -#define WINPR_CIPHER_CAMELLIA_192_CCM 47 -#define WINPR_CIPHER_CAMELLIA_256_CCM 48 - -typedef struct _winpr_cipher_ctx_private_st WINPR_CIPHER_CTX; - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API WINPR_CIPHER_CTX* winpr_Cipher_New(int cipher, int op, const BYTE* key, - const BYTE* iv); - WINPR_API BOOL winpr_Cipher_Update(WINPR_CIPHER_CTX* ctx, const BYTE* input, size_t ilen, - BYTE* output, size_t* olen); - WINPR_API BOOL winpr_Cipher_Final(WINPR_CIPHER_CTX* ctx, BYTE* output, size_t* olen); - WINPR_API void winpr_Cipher_Free(WINPR_CIPHER_CTX* ctx); - -#ifdef __cplusplus -} -#endif - -/** - * Key Generation - */ - -#ifdef __cplusplus -extern "C" -{ -#endif - - WINPR_API int winpr_Cipher_BytesToKey(int cipher, int md, const BYTE* salt, const BYTE* data, - int datal, int count, BYTE* key, BYTE* iv); - -#ifdef __cplusplus -} -#endif +#include +#include #endif /* WINPR_CRYPTO_H */ diff --git a/winpr/include/winpr/custom-crypto.h b/winpr/include/winpr/custom-crypto.h new file mode 100644 index 000000000000..1b195ea2b39a --- /dev/null +++ b/winpr/include/winpr/custom-crypto.h @@ -0,0 +1,251 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_CUSTOM_CRYPTO_H +#define WINPR_CUSTOM_CRYPTO_H + +#include +#include + +#include + +/** + * Custom Crypto API Abstraction Layer + */ + +#define WINPR_MD4_DIGEST_LENGTH 16 +#define WINPR_MD5_DIGEST_LENGTH 16 +#define WINPR_SHA1_DIGEST_LENGTH 20 +#define WINPR_SHA224_DIGEST_LENGTH 28 +#define WINPR_SHA256_DIGEST_LENGTH 32 +#define WINPR_SHA384_DIGEST_LENGTH 48 +#define WINPR_SHA512_DIGEST_LENGTH 64 +#define WINPR_RIPEMD160_DIGEST_LENGTH 20 +#define WINPR_SHA3_224_DIGEST_LENGTH 28 +#define WINPR_SHA3_256_DIGEST_LENGTH 32 +#define WINPR_SHA3_384_DIGEST_LENGTH 48 +#define WINPR_SHA3_512_DIGEST_LENGTH 64 +#define WINPR_SHAKE128_DIGEST_LENGTH 16 +#define WINPR_SHAKE256_DIGEST_LENGTH 32 + +/** + * HMAC + */ +typedef enum +{ + WINPR_MD_NONE = 0, + WINPR_MD_MD2 = 1, + WINPR_MD_MD4 = 2, + WINPR_MD_MD5 = 3, + WINPR_MD_SHA1 = 4, + WINPR_MD_SHA224 = 5, + WINPR_MD_SHA256 = 6, + WINPR_MD_SHA384 = 7, + WINPR_MD_SHA512 = 8, + WINPR_MD_RIPEMD160 = 9, + WINPR_MD_SHA3_224 = 10, + WINPR_MD_SHA3_256 = 11, + WINPR_MD_SHA3_384 = 12, + WINPR_MD_SHA3_512 = 13, + WINPR_MD_SHAKE128 = 14, + WINPR_MD_SHAKE256 = 15 +} WINPR_MD_TYPE; + +typedef struct _winpr_hmac_ctx_private_st WINPR_HMAC_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API WINPR_MD_TYPE winpr_md_type_from_string(const char* name); + WINPR_API const char* winpr_md_type_to_string(WINPR_MD_TYPE md); + + WINPR_API WINPR_HMAC_CTX* winpr_HMAC_New(void); + WINPR_API BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const BYTE* key, + size_t keylen); + WINPR_API BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const BYTE* input, size_t ilen); + WINPR_API BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, BYTE* output, size_t ilen); + WINPR_API void winpr_HMAC_Free(WINPR_HMAC_CTX* ctx); + WINPR_API BOOL winpr_HMAC(WINPR_MD_TYPE md, const BYTE* key, size_t keylen, const BYTE* input, + size_t ilen, BYTE* output, size_t olen); + +#ifdef __cplusplus +} +#endif + +/** + * Generic Digest API + */ + +typedef struct _winpr_digest_ctx_private_st WINPR_DIGEST_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API WINPR_DIGEST_CTX* winpr_Digest_New(void); + WINPR_API BOOL winpr_Digest_Init_Allow_FIPS(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md); + WINPR_API BOOL winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md); + WINPR_API BOOL winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const BYTE* input, size_t ilen); + WINPR_API BOOL winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, BYTE* output, size_t ilen); + WINPR_API void winpr_Digest_Free(WINPR_DIGEST_CTX* ctx); + WINPR_API BOOL winpr_Digest_Allow_FIPS(WINPR_MD_TYPE md, const BYTE* input, size_t ilen, + BYTE* output, size_t olen); + WINPR_API BOOL winpr_Digest(WINPR_MD_TYPE md, const BYTE* input, size_t ilen, BYTE* output, + size_t olen); + +#ifdef __cplusplus +} +#endif + +/** + * Random Number Generation + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API int winpr_RAND(BYTE* output, size_t len); + WINPR_API int winpr_RAND_pseudo(BYTE* output, size_t len); + +#ifdef __cplusplus +} +#endif + +/** + * RC4 + */ + +typedef struct _winpr_rc4_ctx_private_st WINPR_RC4_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API WINPR_RC4_CTX* winpr_RC4_New_Allow_FIPS(const BYTE* key, size_t keylen); + WINPR_API WINPR_RC4_CTX* winpr_RC4_New(const BYTE* key, size_t keylen); + WINPR_API BOOL winpr_RC4_Update(WINPR_RC4_CTX* ctx, size_t length, const BYTE* input, + BYTE* output); + WINPR_API void winpr_RC4_Free(WINPR_RC4_CTX* ctx); + +#ifdef __cplusplus +} +#endif + +/** + * Generic Cipher API + */ + +#define WINPR_AES_BLOCK_SIZE 16 + +/* cipher operation types */ +#define WINPR_ENCRYPT 0 +#define WINPR_DECRYPT 1 + +/* cipher types */ +#define WINPR_CIPHER_NONE 0 +#define WINPR_CIPHER_NULL 1 +#define WINPR_CIPHER_AES_128_ECB 2 +#define WINPR_CIPHER_AES_192_ECB 3 +#define WINPR_CIPHER_AES_256_ECB 4 +#define WINPR_CIPHER_AES_128_CBC 5 +#define WINPR_CIPHER_AES_192_CBC 6 +#define WINPR_CIPHER_AES_256_CBC 7 +#define WINPR_CIPHER_AES_128_CFB128 8 +#define WINPR_CIPHER_AES_192_CFB128 9 +#define WINPR_CIPHER_AES_256_CFB128 10 +#define WINPR_CIPHER_AES_128_CTR 11 +#define WINPR_CIPHER_AES_192_CTR 12 +#define WINPR_CIPHER_AES_256_CTR 13 +#define WINPR_CIPHER_AES_128_GCM 14 +#define WINPR_CIPHER_AES_192_GCM 15 +#define WINPR_CIPHER_AES_256_GCM 16 +#define WINPR_CIPHER_CAMELLIA_128_ECB 17 +#define WINPR_CIPHER_CAMELLIA_192_ECB 18 +#define WINPR_CIPHER_CAMELLIA_256_ECB 19 +#define WINPR_CIPHER_CAMELLIA_128_CBC 20 +#define WINPR_CIPHER_CAMELLIA_192_CBC 21 +#define WINPR_CIPHER_CAMELLIA_256_CBC 22 +#define WINPR_CIPHER_CAMELLIA_128_CFB128 23 +#define WINPR_CIPHER_CAMELLIA_192_CFB128 24 +#define WINPR_CIPHER_CAMELLIA_256_CFB128 25 +#define WINPR_CIPHER_CAMELLIA_128_CTR 26 +#define WINPR_CIPHER_CAMELLIA_192_CTR 27 +#define WINPR_CIPHER_CAMELLIA_256_CTR 28 +#define WINPR_CIPHER_CAMELLIA_128_GCM 29 +#define WINPR_CIPHER_CAMELLIA_192_GCM 30 +#define WINPR_CIPHER_CAMELLIA_256_GCM 31 +#define WINPR_CIPHER_DES_ECB 32 +#define WINPR_CIPHER_DES_CBC 33 +#define WINPR_CIPHER_DES_EDE_ECB 34 +#define WINPR_CIPHER_DES_EDE_CBC 35 +#define WINPR_CIPHER_DES_EDE3_ECB 36 +#define WINPR_CIPHER_DES_EDE3_CBC 37 +#define WINPR_CIPHER_BLOWFISH_ECB 38 +#define WINPR_CIPHER_BLOWFISH_CBC 39 +#define WINPR_CIPHER_BLOWFISH_CFB64 40 +#define WINPR_CIPHER_BLOWFISH_CTR 41 +#define WINPR_CIPHER_ARC4_128 42 +#define WINPR_CIPHER_AES_128_CCM 43 +#define WINPR_CIPHER_AES_192_CCM 44 +#define WINPR_CIPHER_AES_256_CCM 45 +#define WINPR_CIPHER_CAMELLIA_128_CCM 46 +#define WINPR_CIPHER_CAMELLIA_192_CCM 47 +#define WINPR_CIPHER_CAMELLIA_256_CCM 48 + +typedef struct _winpr_cipher_ctx_private_st WINPR_CIPHER_CTX; + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API WINPR_CIPHER_CTX* winpr_Cipher_New(int cipher, int op, const BYTE* key, + const BYTE* iv); + WINPR_API BOOL winpr_Cipher_Update(WINPR_CIPHER_CTX* ctx, const BYTE* input, size_t ilen, + BYTE* output, size_t* olen); + WINPR_API BOOL winpr_Cipher_Final(WINPR_CIPHER_CTX* ctx, BYTE* output, size_t* olen); + WINPR_API void winpr_Cipher_Free(WINPR_CIPHER_CTX* ctx); + +#ifdef __cplusplus +} +#endif + +/** + * Key Generation + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API int winpr_Cipher_BytesToKey(int cipher, int md, const BYTE* salt, const BYTE* data, + int datal, int count, BYTE* key, BYTE* iv); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_CUSTOM_CRYPTO_H */ diff --git a/winpr/include/winpr/locale.h b/winpr/include/winpr/locale.h index 45ef81bb9a79..af776dd0d4a9 100644 --- a/winpr/include/winpr/locale.h +++ b/winpr/include/winpr/locale.h @@ -484,13 +484,13 @@ extern "C" { #endif - DWORD WINAPI FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, - DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, - va_list* Arguments); + WINPR_API DWORD WINAPI FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, + DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, + va_list* Arguments); - DWORD WINAPI FormatMessageW(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, - DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, - va_list* Arguments); + WINPR_API DWORD WINAPI FormatMessageW(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, + DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, + va_list* Arguments); #ifdef UNICODE #define FormatMessage FormatMessageW diff --git a/winpr/include/winpr/smartcard.h b/winpr/include/winpr/smartcard.h index e19a6ee02889..b040f0ad7063 100644 --- a/winpr/include/winpr/smartcard.h +++ b/winpr/include/winpr/smartcard.h @@ -590,9 +590,9 @@ extern "C" { #endif - extern const SCARD_IO_REQUEST g_rgSCardT0Pci; - extern const SCARD_IO_REQUEST g_rgSCardT1Pci; - extern const SCARD_IO_REQUEST g_rgSCardRawPci; + WINPR_API extern const SCARD_IO_REQUEST g_rgSCardT0Pci; + WINPR_API extern const SCARD_IO_REQUEST g_rgSCardT1Pci; + WINPR_API extern const SCARD_IO_REQUEST g_rgSCardRawPci; #define SCARD_PCI_T0 (&g_rgSCardT0Pci) #define SCARD_PCI_T1 (&g_rgSCardT1Pci) diff --git a/winpr/include/winpr/ssl.h b/winpr/include/winpr/ssl.h index 942a062ed8bc..ff50097a00ec 100644 --- a/winpr/include/winpr/ssl.h +++ b/winpr/include/winpr/ssl.h @@ -22,6 +22,7 @@ #define WINPR_SSL_H #include +#include #define WINPR_SSL_INIT_DEFAULT 0x00 #define WINPR_SSL_INIT_ALREADY_INITIALIZED 0x01 diff --git a/winpr/include/winpr/stream.h b/winpr/include/winpr/stream.h index f351eaa15663..ed637f034465 100644 --- a/winpr/include/winpr/stream.h +++ b/winpr/include/winpr/stream.h @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" @@ -56,6 +57,19 @@ extern "C" WINPR_API void Stream_StaticInit(wStream* s, BYTE* buffer, size_t size); WINPR_API void Stream_Free(wStream* s, BOOL bFreeBuffer); +#define Stream_CheckAndLogRequiredLength(tag, s, len) \ + Stream_CheckAndLogRequiredLengthEx(tag, WLOG_WARN, s, len, "%s(%s:%d)", __FUNCTION__, \ + __FILE__, __LINE__) + WINPR_API BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, + UINT64 len, const char* fmt, ...); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, + UINT64 len, const char* fmt, va_list args); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, + UINT64 len, const char* fmt, ...); + WINPR_API BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, + UINT64 len, const char* fmt, + va_list args); + static INLINE void Stream_Seek(wStream* s, size_t _offset) { s->pointer += (_offset); diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h index 8ce83bc1d97a..bb98dffdc9ea 100644 --- a/winpr/include/winpr/string.h +++ b/winpr/include/winpr/string.h @@ -31,6 +31,9 @@ extern "C" { #endif + WINPR_API BOOL winpr_str_append(const char* what, char* buffer, size_t size, + const char* separator); + #ifndef _WIN32 #define CSTR_LESS_THAN 1 @@ -57,10 +60,13 @@ extern "C" WINPR_API int _strnicmp(const char* string1, const char* string2, size_t count); WINPR_API int _wcscmp(const WCHAR* string1, const WCHAR* string2); + WINPR_API int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count); WINPR_API size_t _wcslen(const WCHAR* str); WINPR_API size_t _wcsnlen(const WCHAR* str, size_t maxNumberOfElements); + WINPR_API WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch); + WINPR_API WCHAR* _wcschr(const WCHAR* str, WCHAR c); WINPR_API WCHAR* _wcsrchr(const WCHAR* str, WCHAR c); @@ -70,12 +76,14 @@ extern "C" #else #define _wcscmp wcscmp +#define _wcsncmp wcsncmp #define _wcslen wcslen #define _wcsnlen wcsnlen +#define _wcsstr wcsstr #define _wcschr wcschr #define _wcsrchr wcsrchr -#endif +#endif /* _WIN32 */ #if !defined(_WIN32) || defined(_UWP) diff --git a/winpr/include/winpr/thread.h b/winpr/include/winpr/thread.h index 4e603b3dcacf..1771526491d0 100644 --- a/winpr/include/winpr/thread.h +++ b/winpr/include/winpr/thread.h @@ -207,7 +207,7 @@ extern "C" LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId); - WINPR_API DECLSPEC_NORETURN VOID ExitThread(DWORD dwExitCode); + WINPR_API VOID ExitThread(DWORD dwExitCode); WINPR_API BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode); WINPR_API HANDLE _GetCurrentThread(void); @@ -248,10 +248,7 @@ extern "C" /* CommandLineToArgvA is not present in the original Windows API, WinPR always exports it */ WINPR_API LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs); - -#if defined(WITH_DEBUG_THREADS) WINPR_API VOID DumpThreadHandles(void); -#endif #ifdef __cplusplus } diff --git a/winpr/include/winpr/wincrypt.h b/winpr/include/winpr/wincrypt.h new file mode 100644 index 000000000000..10a1274ba218 --- /dev/null +++ b/winpr/include/winpr/wincrypt.h @@ -0,0 +1,637 @@ +/** + * WinPR: Windows Portable Runtime + * Cryptography API (CryptoAPI) + * + * Copyright 2012 Marc-Andre Moreau + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_WINCRYPT_H +#define WINPR_WINCRYPT_H + +#include +#include + +#include + +#ifdef _WIN32 + +#include + +#endif + +#ifndef ALG_TYPE_RESERVED7 +#define ALG_TYPE_RESERVED7 (7 << 9) +#endif + +#if !defined(NTDDI_VERSION) || (NTDDI_VERSION <= 0x05010200) +#define ALG_SID_SHA_256 12 +#define ALG_SID_SHA_384 13 +#define ALG_SID_SHA_512 14 +#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) +#define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) +#define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) +#endif + +#ifndef _WIN32 + +/* ncrypt.h */ + +typedef ULONG_PTR NCRYPT_HANDLE; +typedef ULONG_PTR NCRYPT_PROV_HANDLE; +typedef ULONG_PTR NCRYPT_KEY_HANDLE; +typedef ULONG_PTR NCRYPT_HASH_HANDLE; +typedef ULONG_PTR NCRYPT_SECRET_HANDLE; + +/* wincrypt.h */ + +#define GET_ALG_CLASS(x) (x & (7 << 13)) +#define GET_ALG_TYPE(x) (x & (15 << 9)) +#define GET_ALG_SID(x) (x & (511)) + +#define ALG_CLASS_ANY (0) +#define ALG_CLASS_SIGNATURE (1 << 13) +#define ALG_CLASS_MSG_ENCRYPT (2 << 13) +#define ALG_CLASS_DATA_ENCRYPT (3 << 13) +#define ALG_CLASS_HASH (4 << 13) +#define ALG_CLASS_KEY_EXCHANGE (5 << 13) +#define ALG_CLASS_ALL (7 << 13) + +#define ALG_TYPE_ANY (0) +#define ALG_TYPE_DSS (1 << 9) +#define ALG_TYPE_RSA (2 << 9) +#define ALG_TYPE_BLOCK (3 << 9) +#define ALG_TYPE_STREAM (4 << 9) +#define ALG_TYPE_DH (5 << 9) +#define ALG_TYPE_SECURECHANNEL (6 << 9) + +#define ALG_SID_ANY (0) + +#define ALG_SID_RSA_ANY 0 +#define ALG_SID_RSA_PKCS 1 +#define ALG_SID_RSA_MSATWORK 2 +#define ALG_SID_RSA_ENTRUST 3 +#define ALG_SID_RSA_PGP 4 + +#define ALG_SID_DSS_ANY 0 +#define ALG_SID_DSS_PKCS 1 +#define ALG_SID_DSS_DMS 2 + +#define ALG_SID_DES 1 +#define ALG_SID_3DES 3 +#define ALG_SID_DESX 4 +#define ALG_SID_IDEA 5 +#define ALG_SID_CAST 6 +#define ALG_SID_SAFERSK64 7 +#define ALG_SID_SAFERSK128 8 +#define ALG_SID_3DES_112 9 +#define ALG_SID_CYLINK_MEK 12 +#define ALG_SID_RC5 13 + +#define ALG_SID_AES_128 14 +#define ALG_SID_AES_192 15 +#define ALG_SID_AES_256 16 +#define ALG_SID_AES 17 + +#define ALG_SID_SKIPJACK 10 +#define ALG_SID_TEK 11 + +#define CRYPT_MODE_CBCI 6 +#define CRYPT_MODE_CFBP 7 +#define CRYPT_MODE_OFBP 8 +#define CRYPT_MODE_CBCOFM 9 +#define CRYPT_MODE_CBCOFMI 10 + +#define ALG_SID_RC2 2 + +#define ALG_SID_RC4 1 +#define ALG_SID_SEAL 2 + +#define ALG_SID_DH_SANDF 1 +#define ALG_SID_DH_EPHEM 2 +#define ALG_SID_AGREED_KEY_ANY 3 +#define ALG_SID_KEA 4 + +#define ALG_SID_ECDH 5 + +#define ALG_SID_MD2 1 +#define ALG_SID_MD4 2 +#define ALG_SID_MD5 3 +#define ALG_SID_SHA 4 +#define ALG_SID_SHA1 4 +#define ALG_SID_MAC 5 +#define ALG_SID_RIPEMD 6 +#define ALG_SID_RIPEMD160 7 +#define ALG_SID_SSL3SHAMD5 8 +#define ALG_SID_HMAC 9 +#define ALG_SID_TLS1PRF 10 + +#define ALG_SID_HASH_REPLACE_OWF 11 + +#define ALG_SID_SHA_256 12 +#define ALG_SID_SHA_384 13 +#define ALG_SID_SHA_512 14 + +#define ALG_SID_SSL3_MASTER 1 +#define ALG_SID_SCHANNEL_MASTER_HASH 2 +#define ALG_SID_SCHANNEL_MAC_KEY 3 +#define ALG_SID_PCT1_MASTER 4 +#define ALG_SID_SSL2_MASTER 5 +#define ALG_SID_TLS1_MASTER 6 +#define ALG_SID_SCHANNEL_ENC_KEY 7 + +#define ALG_SID_ECMQV 1 + +#define CALG_MD2 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD2) +#define CALG_MD4 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4) +#define CALG_MD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5) +#define CALG_SHA (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA) +#define CALG_SHA1 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1) +#define CALG_MAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MAC) +#define CALG_RSA_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY) +#define CALG_DSS_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY) + +#define CALG_NO_SIGN (ALG_CLASS_SIGNATURE | ALG_TYPE_ANY | ALG_SID_ANY) + +#define CALG_RSA_KEYX (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RSA | ALG_SID_RSA_ANY) +#define CALG_DES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DES) +#define CALG_3DES_112 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES_112) +#define CALG_3DES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES) +#define CALG_DESX (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_DESX) +#define CALG_RC2 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC2) +#define CALG_RC4 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_RC4) +#define CALG_SEAL (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_STREAM | ALG_SID_SEAL) +#define CALG_DH_SF (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_DH_SANDF) +#define CALG_DH_EPHEM (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_DH_EPHEM) +#define CALG_AGREEDKEY_ANY (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_AGREED_KEY_ANY) +#define CALG_KEA_KEYX (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_KEA) +#define CALG_HUGHES_MD5 (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_MD5) +#define CALG_SKIPJACK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_SKIPJACK) +#define CALG_TEK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_TEK) +#define CALG_CYLINK_MEK (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_CYLINK_MEK) +#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) +#define CALG_SSL3_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SSL3_MASTER) +#define CALG_SCHANNEL_MASTER_HASH \ + (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_MASTER_HASH) +#define CALG_SCHANNEL_MAC_KEY \ + (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_MAC_KEY) +#define CALG_SCHANNEL_ENC_KEY \ + (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SCHANNEL_ENC_KEY) +#define CALG_PCT1_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_PCT1_MASTER) +#define CALG_SSL2_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_SSL2_MASTER) +#define CALG_TLS1_MASTER (ALG_CLASS_MSG_ENCRYPT | ALG_TYPE_SECURECHANNEL | ALG_SID_TLS1_MASTER) +#define CALG_RC5 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_RC5) +#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) +#define CALG_TLS1PRF (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_TLS1PRF) + +#define CALG_HASH_REPLACE_OWF (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HASH_REPLACE_OWF) +#define CALG_AES_128 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_128) +#define CALG_AES_192 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_192) +#define CALG_AES_256 (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256) +#define CALG_AES (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES) + +#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256) +#define CALG_SHA_384 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384) +#define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) + +#define CALG_ECDH (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_ECDH) +#define CALG_ECMQV (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_ECMQV) + +typedef struct +{ + DWORD cbData; + BYTE* pbData; +} CRYPT_INTEGER_BLOB, *PCRYPT_INTEGER_BLOB, CRYPT_UINT_BLOB, *PCRYPT_UINT_BLOB, CRYPT_OBJID_BLOB, + *PCRYPT_OBJID_BLOB, CERT_NAME_BLOB, *PCERT_NAME_BLOB, CERT_RDN_VALUE_BLOB, + *PCERT_RDN_VALUE_BLOB, CERT_BLOB, *PCERT_BLOB, CRL_BLOB, *PCRL_BLOB, DATA_BLOB, *PDATA_BLOB, + CRYPT_DATA_BLOB, *PCRYPT_DATA_BLOB, CRYPT_HASH_BLOB, *PCRYPT_HASH_BLOB, CRYPT_DIGEST_BLOB, + *PCRYPT_DIGEST_BLOB, CRYPT_DER_BLOB, *PCRYPT_DER_BLOB, CRYPT_ATTR_BLOB, *PCRYPT_ATTR_BLOB; + +typedef struct +{ + LPSTR pszObjId; + CRYPT_OBJID_BLOB Parameters; +} CRYPT_ALGORITHM_IDENTIFIER, *PCRYPT_ALGORITHM_IDENTIFIER; + +typedef struct +{ + DWORD cbData; + BYTE* pbData; + DWORD cUnusedBits; +} CRYPT_BIT_BLOB, *PCRYPT_BIT_BLOB; + +typedef struct +{ + CRYPT_ALGORITHM_IDENTIFIER Algorithm; + CRYPT_BIT_BLOB PublicKey; +} CERT_PUBLIC_KEY_INFO, *PCERT_PUBLIC_KEY_INFO; + +typedef struct +{ + LPSTR pszObjId; + BOOL fCritical; + CRYPT_OBJID_BLOB Value; +} CERT_EXTENSION, *PCERT_EXTENSION; +typedef const CERT_EXTENSION* PCCERT_EXTENSION; + +typedef struct +{ + DWORD dwVersion; + CRYPT_INTEGER_BLOB SerialNumber; + CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; + CERT_NAME_BLOB Issuer; + FILETIME NotBefore; + FILETIME NotAfter; + CERT_NAME_BLOB Subject; + CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo; + CRYPT_BIT_BLOB IssuerUniqueId; + CRYPT_BIT_BLOB SubjectUniqueId; + DWORD cExtension; + PCERT_EXTENSION rgExtension; +} CERT_INFO, *PCERT_INFO; + +typedef void* HCERTSTORE; +typedef ULONG_PTR HCRYPTPROV; +typedef ULONG_PTR HCRYPTPROV_LEGACY; + +typedef struct +{ + DWORD dwCertEncodingType; + BYTE* pbCertEncoded; + DWORD cbCertEncoded; + PCERT_INFO pCertInfo; + HCERTSTORE hCertStore; +} CERT_CONTEXT, *PCERT_CONTEXT; +typedef const CERT_CONTEXT* PCCERT_CONTEXT; + +#if !defined(AT_KEYEXCHANGE) +#define AT_KEYEXCHANGE (1) +#endif +#if !defined(AT_SIGNATURE) +#define AT_SIGNATURE (2) +#endif +#if !defined(AT_AUTHENTICATE) +#define AT_AUTHENTICATE (3) +#endif + +#define CERT_ENCODING_TYPE_MASK 0x0000FFFF +#define CMSG_ENCODING_TYPE_MASK 0xFFFF0000 +#define GET_CERT_ENCODING_TYPE(x) (x & CERT_ENCODING_TYPE_MASK) +#define GET_CMSG_ENCODING_TYPE(x) (x & CMSG_ENCODING_TYPE_MASK) + +#define CRYPT_ASN_ENCODING 0x00000001 +#define CRYPT_NDR_ENCODING 0x00000002 +#define X509_ASN_ENCODING 0x00000001 +#define X509_NDR_ENCODING 0x00000002 +#define PKCS_7_ASN_ENCODING 0x00010000 +#define PKCS_7_NDR_ENCODING 0x00020000 + +#define CERT_COMPARE_MASK 0xFFFF +#define CERT_COMPARE_SHIFT 16 +#define CERT_COMPARE_ANY 0 +#define CERT_COMPARE_SHA1_HASH 1 +#define CERT_COMPARE_NAME 2 +#define CERT_COMPARE_ATTR 3 +#define CERT_COMPARE_MD5_HASH 4 +#define CERT_COMPARE_PROPERTY 5 +#define CERT_COMPARE_PUBLIC_KEY 6 +#define CERT_COMPARE_HASH CERT_COMPARE_SHA1_HASH +#define CERT_COMPARE_NAME_STR_A 7 +#define CERT_COMPARE_NAME_STR_W 8 +#define CERT_COMPARE_KEY_SPEC 9 +#define CERT_COMPARE_ENHKEY_USAGE 10 +#define CERT_COMPARE_CTL_USAGE CERT_COMPARE_ENHKEY_USAGE +#define CERT_COMPARE_SUBJECT_CERT 11 +#define CERT_COMPARE_ISSUER_OF 12 +#define CERT_COMPARE_EXISTING 13 +#define CERT_COMPARE_SIGNATURE_HASH 14 +#define CERT_COMPARE_KEY_IDENTIFIER 15 +#define CERT_COMPARE_CERT_ID 16 +#define CERT_COMPARE_CROSS_CERT_DIST_POINTS 17 +#define CERT_COMPARE_PUBKEY_MD5_HASH 18 +#define CERT_COMPARE_SUBJECT_INFO_ACCESS 19 +#define CERT_COMPARE_HASH_STR 20 +#define CERT_COMPARE_HAS_PRIVATE_KEY 21 + +#define CERT_FIND_ANY (CERT_COMPARE_ANY << CERT_COMPARE_SHIFT) +#define CERT_FIND_SHA1_HASH (CERT_COMPARE_SHA1_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_MD5_HASH (CERT_COMPARE_MD5_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_SIGNATURE_HASH (CERT_COMPARE_SIGNATURE_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_KEY_IDENTIFIER (CERT_COMPARE_KEY_IDENTIFIER << CERT_COMPARE_SHIFT) +#define CERT_FIND_HASH CERT_FIND_SHA1_HASH +#define CERT_FIND_PROPERTY (CERT_COMPARE_PROPERTY << CERT_COMPARE_SHIFT) +#define CERT_FIND_PUBLIC_KEY (CERT_COMPARE_PUBLIC_KEY << CERT_COMPARE_SHIFT) +#define CERT_FIND_SUBJECT_NAME (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_SUBJECT_ATTR (CERT_COMPARE_ATTR << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_ISSUER_NAME (CERT_COMPARE_NAME << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_ISSUER_ATTR (CERT_COMPARE_ATTR << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_SUBJECT_STR_A \ + (CERT_COMPARE_NAME_STR_A << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_SUBJECT_STR_W \ + (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_SUBJECT_FLAG) +#define CERT_FIND_SUBJECT_STR CERT_FIND_SUBJECT_STR_W +#define CERT_FIND_ISSUER_STR_A \ + (CERT_COMPARE_NAME_STR_A << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_ISSUER_STR_W \ + (CERT_COMPARE_NAME_STR_W << CERT_COMPARE_SHIFT | CERT_INFO_ISSUER_FLAG) +#define CERT_FIND_ISSUER_STR CERT_FIND_ISSUER_STR_W +#define CERT_FIND_KEY_SPEC (CERT_COMPARE_KEY_SPEC << CERT_COMPARE_SHIFT) +#define CERT_FIND_ENHKEY_USAGE (CERT_COMPARE_ENHKEY_USAGE << CERT_COMPARE_SHIFT) +#define CERT_FIND_CTL_USAGE CERT_FIND_ENHKEY_USAGE +#define CERT_FIND_SUBJECT_CERT (CERT_COMPARE_SUBJECT_CERT << CERT_COMPARE_SHIFT) +#define CERT_FIND_ISSUER_OF (CERT_COMPARE_ISSUER_OF << CERT_COMPARE_SHIFT) +#define CERT_FIND_EXISTING (CERT_COMPARE_EXISTING << CERT_COMPARE_SHIFT) +#define CERT_FIND_CERT_ID (CERT_COMPARE_CERT_ID << CERT_COMPARE_SHIFT) +#define CERT_FIND_CROSS_CERT_DIST_POINTS (CERT_COMPARE_CROSS_CERT_DIST_POINTS << CERT_COMPARE_SHIFT) +#define CERT_FIND_PUBKEY_MD5_HASH (CERT_COMPARE_PUBKEY_MD5_HASH << CERT_COMPARE_SHIFT) +#define CERT_FIND_SUBJECT_INFO_ACCESS (CERT_COMPARE_SUBJECT_INFO_ACCESS << CERT_COMPARE_SHIFT) +#define CERT_FIND_HASH_STR (CERT_COMPARE_HASH_STR << CERT_COMPARE_SHIFT) +#define CERT_FIND_HAS_PRIVATE_KEY (CERT_COMPARE_HAS_PRIVATE_KEY << CERT_COMPARE_SHIFT) + +#define CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG 0x1 +#define CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG 0x2 +#define CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG 0x4 +#define CERT_FIND_NO_ENHKEY_USAGE_FLAG 0x8 +#define CERT_FIND_OR_ENHKEY_USAGE_FLAG 0x10 +#define CERT_FIND_VALID_ENHKEY_USAGE_FLAG 0x20 +#define CERT_FIND_OPTIONAL_CTL_USAGE_FLAG CERT_FIND_OPTIONAL_ENHKEY_USAGE_FLAG +#define CERT_FIND_EXT_ONLY_CTL_USAGE_FLAG CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG +#define CERT_FIND_PROP_ONLY_CTL_USAGE_FLAG CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG +#define CERT_FIND_NO_CTL_USAGE_FLAG CERT_FIND_NO_ENHKEY_USAGE_FLAG +#define CERT_FIND_OR_CTL_USAGE_FLAG CERT_FIND_OR_ENHKEY_USAGE_FLAG +#define CERT_FIND_VALID_CTL_USAGE_FLAG CERT_FIND_VALID_ENHKEY_USAGE_FLAG + +#define CERT_NAME_EMAIL_TYPE 1 +#define CERT_NAME_RDN_TYPE 2 +#define CERT_NAME_ATTR_TYPE 3 +#define CERT_NAME_SIMPLE_DISPLAY_TYPE 4 +#define CERT_NAME_FRIENDLY_DISPLAY_TYPE 5 +#define CERT_NAME_DNS_TYPE 6 +#define CERT_NAME_URL_TYPE 7 +#define CERT_NAME_UPN_TYPE 8 + +#define CERT_NAME_ISSUER_FLAG 0x1 +#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000 + +#define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 + +#define CERT_STORE_PROV_MSG ((LPCSTR)1) +#define CERT_STORE_PROV_MEMORY ((LPCSTR)2) +#define CERT_STORE_PROV_FILE ((LPCSTR)3) +#define CERT_STORE_PROV_REG ((LPCSTR)4) +#define CERT_STORE_PROV_PKCS7 ((LPCSTR)5) +#define CERT_STORE_PROV_SERIALIZED ((LPCSTR)6) +#define CERT_STORE_PROV_FILENAME_A ((LPCSTR)7) +#define CERT_STORE_PROV_FILENAME_W ((LPCSTR)8) +#define CERT_STORE_PROV_FILENAME CERT_STORE_PROV_FILENAME_W +#define CERT_STORE_PROV_SYSTEM_A ((LPCSTR)9) +#define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)10) +#define CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W +#define CERT_STORE_PROV_COLLECTION ((LPCSTR)11) +#define CERT_STORE_PROV_SYSTEM_REGISTRY_A ((LPCSTR)12) +#define CERT_STORE_PROV_SYSTEM_REGISTRY_W ((LPCSTR)13) +#define CERT_STORE_PROV_SYSTEM_REGISTRY CERT_STORE_PROV_SYSTEM_REGISTRY_W +#define CERT_STORE_PROV_PHYSICAL_W ((LPCSTR)14) +#define CERT_STORE_PROV_PHYSICAL CERT_STORE_PROV_PHYSICAL_W +#define CERT_STORE_PROV_SMART_CARD_W ((LPCSTR)15) +#define CERT_STORE_PROV_SMART_CARD CERT_STORE_PROV_SMART_CARD_W +#define CERT_STORE_PROV_LDAP_W ((LPCSTR)16) +#define CERT_STORE_PROV_LDAP CERT_STORE_PROV_LDAP_W +#define CERT_STORE_PROV_PKCS12 ((LPCSTR)17) +#define sz_CERT_STORE_PROV_MEMORY "Memory" +#define sz_CERT_STORE_PROV_FILENAME_W "File" +#define sz_CERT_STORE_PROV_FILENAME sz_CERT_STORE_PROV_FILENAME_W +#define sz_CERT_STORE_PROV_SYSTEM_W "System" +#define sz_CERT_STORE_PROV_SYSTEM sz_CERT_STORE_PROV_SYSTEM_W +#define sz_CERT_STORE_PROV_PKCS7 "PKCS7" +#define sz_CERT_STORE_PROV_PKCS12 "PKCS12" +#define sz_CERT_STORE_PROV_SERIALIZED "Serialized" +#define sz_CERT_STORE_PROV_COLLECTION "Collection" +#define sz_CERT_STORE_PROV_SYSTEM_REGISTRY_W "SystemRegistry" +#define sz_CERT_STORE_PROV_SYSTEM_REGISTRY sz_CERT_STORE_PROV_SYSTEM_REGISTRY_W +#define sz_CERT_STORE_PROV_PHYSICAL_W "Physical" +#define sz_CERT_STORE_PROV_PHYSICAL sz_CERT_STORE_PROV_PHYSICAL_W +#define sz_CERT_STORE_PROV_SMART_CARD_W "SmartCard" +#define sz_CERT_STORE_PROV_SMART_CARD sz_CERT_STORE_PROV_SMART_CARD_W +#define sz_CERT_STORE_PROV_LDAP_W "Ldap" +#define sz_CERT_STORE_PROV_LDAP sz_CERT_STORE_PROV_LDAP_W + +#define CERT_STORE_SIGNATURE_FLAG 0x00000001 +#define CERT_STORE_TIME_VALIDITY_FLAG 0x00000002 +#define CERT_STORE_REVOCATION_FLAG 0x00000004 +#define CERT_STORE_NO_CRL_FLAG 0x00010000 +#define CERT_STORE_NO_ISSUER_FLAG 0x00020000 +#define CERT_STORE_BASE_CRL_FLAG 0x00000100 +#define CERT_STORE_DELTA_CRL_FLAG 0x00000200 + +#define CERT_STORE_NO_CRYPT_RELEASE_FLAG 0x00000001 +#define CERT_STORE_SET_LOCALIZED_NAME_FLAG 0x00000002 +#define CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG 0x00000004 +#define CERT_STORE_DELETE_FLAG 0x00000010 +#define CERT_STORE_UNSAFE_PHYSICAL_FLAG 0x00000020 +#define CERT_STORE_SHARE_STORE_FLAG 0x00000040 +#define CERT_STORE_SHARE_CONTEXT_FLAG 0x00000080 +#define CERT_STORE_MANIFOLD_FLAG 0x00000100 +#define CERT_STORE_ENUM_ARCHIVED_FLAG 0x00000200 +#define CERT_STORE_UPDATE_KEYID_FLAG 0x00000400 +#define CERT_STORE_BACKUP_RESTORE_FLAG 0x00000800 +#define CERT_STORE_READONLY_FLAG 0x00008000 +#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 +#define CERT_STORE_CREATE_NEW_FLAG 0x00002000 +#define CERT_STORE_MAXIMUM_ALLOWED_FLAG 0x00001000 + +#define CERT_SYSTEM_STORE_MASK 0xFFFF0000 +#define CERT_SYSTEM_STORE_RELOCATE_FLAG 0x80000000 +#define CERT_SYSTEM_STORE_UNPROTECTED_FLAG 0x40000000 +#define CERT_SYSTEM_STORE_DEFER_READ_FLAG 0x20000000 +#define CERT_SYSTEM_STORE_LOCATION_MASK 0x00FF0000 +#define CERT_SYSTEM_STORE_LOCATION_SHIFT 16 +#define CERT_SYSTEM_STORE_CURRENT_USER_ID 1 +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ID 2 +#define CERT_SYSTEM_STORE_CURRENT_SERVICE_ID 4 +#define CERT_SYSTEM_STORE_SERVICES_ID 5 +#define CERT_SYSTEM_STORE_USERS_ID 6 +#define CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY_ID 7 +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY_ID 8 +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE_ID 9 + +#define CERT_SYSTEM_STORE_CURRENT_USER \ + (CERT_SYSTEM_STORE_CURRENT_USER_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_LOCAL_MACHINE \ + (CERT_SYSTEM_STORE_LOCAL_MACHINE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_CURRENT_SERVICE \ + (CERT_SYSTEM_STORE_CURRENT_SERVICE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_SERVICES \ + (CERT_SYSTEM_STORE_SERVICES_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_USERS (CERT_SYSTEM_STORE_USERS_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY \ + (CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY \ + (CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) +#define CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE \ + (CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE_ID << CERT_SYSTEM_STORE_LOCATION_SHIFT) + +WINPR_API HCERTSTORE CertOpenStore(LPCSTR lpszStoreProvider, DWORD dwMsgAndCertEncodingType, + HCRYPTPROV_LEGACY hCryptProv, DWORD dwFlags, const void* pvPara); + +WINPR_API HCERTSTORE CertOpenSystemStoreW(HCRYPTPROV_LEGACY hProv, LPCWSTR szSubsystemProtocol); +WINPR_API HCERTSTORE CertOpenSystemStoreA(HCRYPTPROV_LEGACY hProv, LPCSTR szSubsystemProtocol); + +WINPR_API BOOL CertCloseStore(HCERTSTORE hCertStore, DWORD dwFlags); + +#ifdef UNICODE +#define CertOpenSystemStore CertOpenSystemStoreW +#else +#define CertOpenSystemStore CertOpenSystemStoreA +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API PCCERT_CONTEXT CertFindCertificateInStore(HCERTSTORE hCertStore, + DWORD dwCertEncodingType, DWORD dwFindFlags, + DWORD dwFindType, const void* pvFindPara, + PCCERT_CONTEXT pPrevCertContext); + + WINPR_API PCCERT_CONTEXT CertEnumCertificatesInStore(HCERTSTORE hCertStore, + PCCERT_CONTEXT pPrevCertContext); + + WINPR_API DWORD CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, + void* pvTypePara, LPWSTR pszNameString, DWORD cchNameString); + WINPR_API DWORD CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, DWORD dwFlags, + void* pvTypePara, LPSTR pszNameString, DWORD cchNameString); + +#ifdef __cplusplus +} +#endif + +#ifdef UNICODE +#define CertGetNameString CertGetNameStringW +#else +#define CertGetNameString CertGetNameStringA +#endif + +/** + * Data Protection API (DPAPI) + */ + +#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 + +#define CRYPTPROTECTMEMORY_SAME_PROCESS 0x00000000 +#define CRYPTPROTECTMEMORY_CROSS_PROCESS 0x00000001 +#define CRYPTPROTECTMEMORY_SAME_LOGON 0x00000002 + +#define CRYPTPROTECT_PROMPT_ON_UNPROTECT 0x00000001 +#define CRYPTPROTECT_PROMPT_ON_PROTECT 0x00000002 +#define CRYPTPROTECT_PROMPT_RESERVED 0x00000004 +#define CRYPTPROTECT_PROMPT_STRONG 0x00000008 +#define CRYPTPROTECT_PROMPT_REQUIRE_STRONG 0x00000010 + +#define CRYPTPROTECT_UI_FORBIDDEN 0x1 +#define CRYPTPROTECT_LOCAL_MACHINE 0x4 +#define CRYPTPROTECT_CRED_SYNC 0x8 +#define CRYPTPROTECT_AUDIT 0x10 +#define CRYPTPROTECT_NO_RECOVERY 0x20 +#define CRYPTPROTECT_VERIFY_PROTECTION 0x40 +#define CRYPTPROTECT_CRED_REGENERATE 0x80 + +#define CRYPTPROTECT_FIRST_RESERVED_FLAGVAL 0x0FFFFFFF +#define CRYPTPROTECT_LAST_RESERVED_FLAGVAL 0xFFFFFFFF + +typedef struct +{ + DWORD cbSize; + DWORD dwPromptFlags; + HWND hwndApp; + LPCWSTR szPrompt; +} CRYPTPROTECT_PROMPTSTRUCT, *PCRYPTPROTECT_PROMPTSTRUCT; + +#define CRYPTPROTECT_DEFAULT_PROVIDER \ + { \ + 0xdf9d8cd0, 0x1501, 0x11d1, \ + { \ + 0x8c, 0x7a, 0x00, 0xc0, 0x4f, 0xc2, 0x97, 0xeb \ + } \ + } + +#ifdef __cplusplus +extern "C" +{ +#endif + + WINPR_API BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); + WINPR_API BOOL CryptUnprotectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags); + + WINPR_API BOOL CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, + DATA_BLOB* pOptionalEntropy, PVOID pvReserved, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, + DATA_BLOB* pDataOut); + WINPR_API BOOL CryptUnprotectData(DATA_BLOB* pDataIn, LPWSTR* ppszDataDescr, + DATA_BLOB* pOptionalEntropy, PVOID pvReserved, + CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, + DATA_BLOB* pDataOut); + +#ifdef __cplusplus +} +#endif + +#define CRYPT_STRING_BASE64HEADER 0x00000000 +#define CRYPT_STRING_BASE64 0x00000001 +#define CRYPT_STRING_BINARY 0x00000002 +#define CRYPT_STRING_BASE64REQUESTHEADER 0x00000003 +#define CRYPT_STRING_HEX 0x00000004 +#define CRYPT_STRING_HEXASCII 0x00000005 +#define CRYPT_STRING_BASE64_ANY 0x00000006 +#define CRYPT_STRING_ANY 0x00000007 +#define CRYPT_STRING_HEX_ANY 0x00000008 +#define CRYPT_STRING_BASE64X509CRLHEADER 0x00000009 +#define CRYPT_STRING_HEXADDR 0x0000000A +#define CRYPT_STRING_HEXASCIIADDR 0x0000000B +#define CRYPT_STRING_HEXRAW 0x0000000C + +#define CRYPT_STRING_HASHDATA 0x10000000 +#define CRYPT_STRING_STRICT 0x20000000 +#define CRYPT_STRING_NOCRLF 0x40000000 +#define CRYPT_STRING_NOCR 0x80000000 + +WINPR_API BOOL CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, + BYTE* pbBinary, DWORD* pcbBinary, DWORD* pdwSkip, + DWORD* pdwFlags); +WINPR_API BOOL CryptStringToBinaryA(LPCSTR pszString, DWORD cchString, DWORD dwFlags, + BYTE* pbBinary, DWORD* pcbBinary, DWORD* pdwSkip, + DWORD* pdwFlags); + +WINPR_API BOOL CryptBinaryToStringW(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, + LPWSTR pszString, DWORD* pcchString); +WINPR_API BOOL CryptBinaryToStringA(CONST BYTE* pbBinary, DWORD cbBinary, DWORD dwFlags, + LPSTR pszString, DWORD* pcchString); + +#ifdef UNICODE +#define CryptStringToBinary CryptStringToBinaryW +#define CryptBinaryToString CryptBinaryToStringW +#else +#define CryptStringToBinary CryptStringToBinaryA +#define CryptBinaryToString CryptBinaryToStringA +#endif + +#endif + +#ifndef ALG_SID_ECSDA +#define ALG_SID_ECDSA 3 +#define CALG_ECDSA (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_ECDSA) +#endif + +#endif /* WINPR_WINCRYPT_H */ diff --git a/winpr/include/winpr/windows.h b/winpr/include/winpr/windows.h index b5313f0670c4..5bc6722c2d31 100644 --- a/winpr/include/winpr/windows.h +++ b/winpr/include/winpr/windows.h @@ -37,10 +37,34 @@ /* Client System Parameters Update PDU * defined in winuser.h */ -#define SPI_SETCARETWIDTH 0x00002007 -#define SPI_SETSTICKYKEYS 0x0000003B -#define SPI_SETTOGGLEKEYS 0x00000035 -#define SPI_SETFILTERKEYS 0x00000033 +typedef enum +{ + SPI_SETDRAGFULLWINDOWS = 0x00000025, + SPI_SETKEYBOARDCUES = 0x0000100B, + SPI_SETKEYBOARDPREF = 0x00000045, + SPI_SETWORKAREA = 0x0000002f, + RAIL_SPI_DISPLAYCHANGE = 0x0000F001, + SPI_SETMOUSEBUTTONSWAP = 0x00000021, + RAIL_SPI_TASKBARPOS = 0x0000F000, + SPI_SETHIGHCONTRAST = 0x00000043, + SPI_SETCARETWIDTH = 0x00002007, + SPI_SETSTICKYKEYS = 0x0000003B, + SPI_SETTOGGLEKEYS = 0x00000035, + SPI_SETFILTERKEYS = 0x00000033, + RAIL_SPI_DISPLAY_ANIMATIONS_ENABLED = 0x0000F002, + RAIL_SPI_DISPLAY_ADVANCED_EFFECTS_ENABLED = 0x0000F003, + RAIL_SPI_DISPLAY_AUTO_HIDE_SCROLLBARS = 0x0000F004, + RAIL_SPI_DISPLAY_MESSAGE_DURATION = 0x0000F005, + RAIL_SPI_CLOSED_CAPTION_FONT_COLOR = 0x0000F006, + RAIL_SPI_CLOSED_CAPTION_FONT_OPACITY = 0x0000F007, + RAIL_SPI_CLOSED_CAPTION_FONT_SIZE = 0x0000F008, + RAIL_SPI_CLOSED_CAPTION_FONT_STYLE = 0x0000F009, + RAIL_SPI_CLOSED_CAPTION_FONT_EDGE_EFFECT = 0x0000F00A, + RAIL_SPI_CLOSED_CAPTION_BACKGROUND_COLOR = 0x0000F00B, + RAIL_SPI_CLOSED_CAPTION_BACKGROUND_OPACITY = 0x0000F00C, + RAIL_SPI_CLOSED_CAPTION_REGION_COLOR = 0x0000F00D, + RAIL_SPI_CLOSED_CAPTION_REGION_OPACITY = 0x0000F00E +} SystemParam; /* Server System Parameters Update PDU */ #define SPI_SETSCREENSAVEACTIVE 0x00000011 diff --git a/winpr/include/winpr/wlog.h b/winpr/include/winpr/wlog.h index 292eac3d64e6..1eedffa8252a 100644 --- a/winpr/include/winpr/wlog.h +++ b/winpr/include/winpr/wlog.h @@ -30,6 +30,7 @@ extern "C" #include #include +#include #include #include diff --git a/winpr/include/winpr/wtypes.h.in b/winpr/include/winpr/wtypes.h.in index d2648908b670..c868095b53b6 100644 --- a/winpr/include/winpr/wtypes.h.in +++ b/winpr/include/winpr/wtypes.h.in @@ -161,8 +161,9 @@ typedef void* PVOID, *LPVOID, *PVOID64, *LPVOID64; #ifndef __APPLE__ typedef __int32 BOOL; #else /* __APPLE__ */ +#include /* ensure compatibility with objc libraries */ -#if (TARGET_OS_IPHONE && __LP64__) || TARGET_OS_WATCH +#if (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0) && defined(__LP64__)) || (defined(TARGET_OS_WATCH) && (TARGET_OS_WATCH != 0)) typedef bool BOOL; #else typedef signed char BOOL; diff --git a/winpr/libwinpr/CMakeLists.txt b/winpr/libwinpr/CMakeLists.txt index d9673f2d4a2c..2df8d899231a 100644 --- a/winpr/libwinpr/CMakeLists.txt +++ b/winpr/libwinpr/CMakeLists.txt @@ -78,13 +78,15 @@ macro (winpr_definition_add) endmacro() set(CMAKE_REQUIRED_LIBRARIES rt) -check_function_exists(timer_create TIMER_CREATE) -check_function_exists(timer_delete TIMER_DELETE) -check_function_exists(timer_settime TIMER_SETTIME) -check_function_exists(timer_gettime TIMER_GETTIME) -if (TIMER_CREATE AND TIMER_DELETE AND TIMER_SETTIME AND TIMER_GETTIME) - add_definitions(-DWITH_POSIX_TIMER) - winpr_library_add_private(rt) +if (NOT IOS) + check_function_exists(timer_create TIMER_CREATE) + check_function_exists(timer_delete TIMER_DELETE) + check_function_exists(timer_settime TIMER_SETTIME) + check_function_exists(timer_gettime TIMER_GETTIME) + if (TIMER_CREATE AND TIMER_DELETE AND TIMER_SETTIME AND TIMER_GETTIME) + add_definitions(-DWITH_POSIX_TIMER) + winpr_library_add_private(rt) + endif() endif() if (ANDROID) diff --git a/winpr/libwinpr/clipboard/clipboard.c b/winpr/libwinpr/clipboard/clipboard.c index 2e88d75bba7f..3c5acd4cca5c 100644 --- a/winpr/libwinpr/clipboard/clipboard.c +++ b/winpr/libwinpr/clipboard/clipboard.c @@ -528,7 +528,7 @@ static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard) #ifdef WITH_WCLIPBOARD_POSIX if (ClipboardInitPosixFileSubsystem(clipboard)) { - WLog_INFO(TAG, "initialized POSIX local file subsystem"); + WLog_DBG(TAG, "initialized POSIX local file subsystem"); return; } else diff --git a/winpr/libwinpr/clipboard/posix.c b/winpr/libwinpr/clipboard/posix.c index 3fe1d70f48de..867fddf974eb 100644 --- a/winpr/libwinpr/clipboard/posix.c +++ b/winpr/libwinpr/clipboard/posix.c @@ -182,10 +182,13 @@ static char* decode_percent_encoded_string(const char* str, size_t len) * Note that the function converts a single file name component, * it does not take care of component separators. */ -static WCHAR* convert_local_name_component_to_remote(const char* local_name) +static WCHAR* convert_local_name_component_to_remote(wClipboard* clipboard, const char* local_name) { + wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard); WCHAR* remote_name = NULL; + WINPR_ASSERT(delegate); + /* * Note that local file names are not actually guaranteed to be * encoded in UTF-8. Filesystems and users can use whatever they @@ -203,8 +206,12 @@ static WCHAR* convert_local_name_component_to_remote(const char* local_name) * Some file names are not valid on Windows. Check for these now * so that we won't get ourselves into a trouble later as such names * are known to crash some Windows shells when pasted via clipboard. + * + * The IsFileNameComponentValid callback can be overridden by the API + * user, if it is known, that the connected peer is not on the + * Windows platform. */ - if (!ValidFileNameComponent(remote_name)) + if (!delegate->IsFileNameComponentValid(remote_name)) { WLog_ERR(TAG, "invalid file name component: %s", local_name); goto error; @@ -252,10 +259,12 @@ static WCHAR* concat_remote_name(const WCHAR* dir, const WCHAR* file) return buffer; } -static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files); +static BOOL add_file_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, wArrayList* files); -static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* remote_dir_name, - const struct dirent* entry, wArrayList* files) +static BOOL add_directory_entry_to_list(wClipboard* clipboard, const char* local_dir_name, + const WCHAR* remote_dir_name, const struct dirent* entry, + wArrayList* files) { BOOL result = FALSE; char* local_name = NULL; @@ -266,7 +275,7 @@ static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) return TRUE; - remote_base_name = convert_local_name_component_to_remote(entry->d_name); + remote_base_name = convert_local_name_component_to_remote(clipboard, entry->d_name); if (!remote_base_name) return FALSE; @@ -275,7 +284,7 @@ static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* remote_name = concat_remote_name(remote_dir_name, remote_base_name); if (local_name && remote_name) - result = add_file_to_list(local_name, remote_name, files); + result = add_file_to_list(clipboard, local_name, remote_name, files); free(remote_base_name); free(remote_name); @@ -283,8 +292,9 @@ static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* return result; } -static BOOL do_add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name, - DIR* dirp, wArrayList* files) +static BOOL do_add_directory_contents_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, DIR* dirp, + wArrayList* files) { /* * For some reason POSIX does not require readdir() to be thread-safe. @@ -316,15 +326,15 @@ static BOOL do_add_directory_contents_to_list(const char* local_name, const WCHA return FALSE; } - if (!add_directory_entry_to_list(local_name, remote_name, entry, files)) + if (!add_directory_entry_to_list(clipboard, local_name, remote_name, entry, files)) return FALSE; } return TRUE; } -static BOOL add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name, - wArrayList* files) +static BOOL add_directory_contents_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, wArrayList* files) { BOOL result = FALSE; DIR* dirp = NULL; @@ -338,7 +348,7 @@ static BOOL add_directory_contents_to_list(const char* local_name, const WCHAR* goto out; } - result = do_add_directory_contents_to_list(local_name, remote_name, dirp, files); + result = do_add_directory_contents_to_list(clipboard, local_name, remote_name, dirp, files); if (closedir(dirp)) { @@ -350,7 +360,8 @@ static BOOL add_directory_contents_to_list(const char* local_name, const WCHAR* return result; } -static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files) +static BOOL add_file_to_list(wClipboard* clipboard, const char* local_name, + const WCHAR* remote_name, wArrayList* files) { struct posix_file* file = NULL; WLog_VRB(TAG, "adding file: %s", local_name); @@ -371,7 +382,7 @@ static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, w * This is effectively a recursive call, but we do not track * recursion depth, thus filesystem loops can cause a crash. */ - if (!add_directory_contents_to_list(local_name, remote_name, files)) + if (!add_directory_contents_to_list(clipboard, local_name, remote_name, files)) return FALSE; } @@ -392,7 +403,7 @@ static const char* get_basename(const char* name) return last_name; } -static BOOL process_file_name(const char* local_name, wArrayList* files) +static BOOL process_file_name(wClipboard* clipboard, const char* local_name, wArrayList* files) { BOOL result = FALSE; const char* base_name = NULL; @@ -403,17 +414,17 @@ static BOOL process_file_name(const char* local_name, wArrayList* files) * to have names relative to that selection. */ base_name = get_basename(local_name); - remote_name = convert_local_name_component_to_remote(base_name); + remote_name = convert_local_name_component_to_remote(clipboard, base_name); if (!remote_name) return FALSE; - result = add_file_to_list(local_name, remote_name, files); + result = add_file_to_list(clipboard, local_name, remote_name, files); free(remote_name); return result; } -static BOOL process_uri(const char* uri, size_t uri_len, wArrayList* files) +static BOOL process_uri(wClipboard* clipboard, const char* uri, size_t uri_len, wArrayList* files) { const char prefix[] = "file://"; BOOL result = FALSE; @@ -432,13 +443,14 @@ static BOOL process_uri(const char* uri, size_t uri_len, wArrayList* files) if (!name) goto out; - result = process_file_name(name, files); + result = process_file_name(clipboard, name, files); out: free(name); return result; } -static BOOL process_uri_list(const char* data, size_t length, wArrayList* files) +static BOOL process_uri_list(wClipboard* clipboard, const char* data, size_t length, + wArrayList* files) { const char* cur = data; const char* lim = data + length; @@ -485,7 +497,7 @@ static BOOL process_uri_list(const char* data, size_t length, wArrayList* files) if (comment) continue; - if (!process_uri(start, stop - start, files)) + if (!process_uri(clipboard, start, stop - start, files)) return FALSE; } @@ -561,7 +573,7 @@ static void* convert_uri_list_to_filedescriptors(wClipboard* clipboard, UINT32 f if (formatId != ClipboardGetFormatId(clipboard, "text/uri-list")) return NULL; - if (!process_uri_list((const char*)data, *pSize, clipboard->localFiles)) + if (!process_uri_list(clipboard, (const char*)data, *pSize, clipboard->localFiles)) return NULL; descriptors = convert_local_file_list_to_filedescriptors(clipboard->localFiles); @@ -946,6 +958,7 @@ static void setup_delegate(wClipboardDelegate* delegate) delegate->ClientRequestFileRange = posix_file_request_range; delegate->ClipboardFileRangeSuccess = dummy_file_range_success; delegate->ClipboardFileRangeFailure = dummy_file_range_failure; + delegate->IsFileNameComponentValid = ValidFileNameComponent; } BOOL ClipboardInitPosixFileSubsystem(wClipboard* clipboard) diff --git a/winpr/libwinpr/clipboard/synthetic.c b/winpr/libwinpr/clipboard/synthetic.c index 63b5153489e4..fcd66dcb6197 100644 --- a/winpr/libwinpr/clipboard/synthetic.c +++ b/winpr/libwinpr/clipboard/synthetic.c @@ -321,91 +321,107 @@ static void* clipboard_synthesize_image_bmp(wClipboard* clipboard, UINT32 format */ static void* clipboard_synthesize_html_format(wClipboard* clipboard, UINT32 formatId, - const void* data, UINT32* pSize) + const void* pData, UINT32* pSize) { - char* pSrcData = NULL; + union + { + const void* cpv; + const char* cpc; + const BYTE* cpb; + WCHAR* pv; + } pSrcData; char* pDstData = NULL; - INT64 SrcSize = (INT64)*pSize; + pSrcData.cpv = NULL; + + WINPR_ASSERT(clipboard); + WINPR_ASSERT(pSize); if (formatId == ClipboardGetFormatId(clipboard, "text/html")) { + const INT64 SrcSize = (INT64)*pSize; + const size_t DstSize = SrcSize + 200; char* body; - BYTE bom[2]; - char num[20]; - WCHAR* wstr; + char num[20] = { 0 }; + + /* Create a copy, we modify the input data */ + pSrcData.pv = calloc(1, SrcSize + 1); + if (!pSrcData.pv) + goto fail; + memcpy(pSrcData.pv, pData, SrcSize); if (SrcSize > 2) { - CopyMemory(bom, data, 2); + if (SrcSize > INT_MAX) + return NULL; - if ((bom[0] == 0xFE) && (bom[1] == 0xFF)) - { - ByteSwapUnicode((WCHAR*)data, SrcSize / 2); - } + /* Check the BOM (Byte Order Mark) */ + if ((pSrcData.cpb[0] == 0xFE) && (pSrcData.cpb[1] == 0xFF)) + ByteSwapUnicode(pSrcData.pv, (int)(SrcSize / 2)); - if ((bom[0] == 0xFF) && (bom[1] == 0xFE)) + /* Check if we have WCHAR, convert to UTF-8 */ + if ((pSrcData.cpb[0] == 0xFF) && (pSrcData.cpb[1] == 0xFE)) { - wstr = (WCHAR*)&((BYTE*)data)[2]; - ConvertFromUnicode(CP_UTF8, 0, wstr, (SrcSize - 2) / 2, &pSrcData, 0, NULL, NULL); + char* utfString = NULL; + ConvertFromUnicode(CP_UTF8, 0, &pSrcData.pv[1], (int)(SrcSize - 2) / 2, &utfString, + 0, NULL, NULL); + free(pSrcData.pv); + pSrcData.cpc = utfString; } } - if (!pSrcData) - { - pSrcData = (char*)calloc(1, SrcSize + 1); - - if (!pSrcData) - return NULL; - - CopyMemory(pSrcData, data, SrcSize); - } - - pDstData = (char*)calloc(1, SrcSize + 200); + pDstData = (char*)calloc(1, DstSize); if (!pDstData) - { - free(pSrcData); - return NULL; - } + goto fail; - sprintf_s(pDstData, SrcSize + 200, + sprintf_s(pDstData, DstSize, "Version:0.9\r\n" "StartHTML:0000000000\r\n" "EndHTML:0000000000\r\n" "StartFragment:0000000000\r\n" "EndFragment:0000000000\r\n"); - body = strstr(pSrcData, ""); + { + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; + } - strcat(pDstData, ""); + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; /* StartFragment */ sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); CopyMemory(&pDstData[69], num, 10); - strcat(pDstData, pSrcData); + if (!winpr_str_append(pSrcData.cpc, pDstData, DstSize, NULL)) + goto fail; /* EndFragment */ sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); CopyMemory(&pDstData[93], num, 10); - strcat(pDstData, ""); + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; if (!body) - strcat(pDstData, ""); + { + if (!winpr_str_append("", pDstData, DstSize, NULL)) + goto fail; + } /* EndHTML */ - sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200)); + sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, DstSize)); CopyMemory(&pDstData[43], num, 10); - *pSize = (UINT32)strlen(pDstData) + 1; - free(pSrcData); + *pSize = (UINT32)strnlen(pDstData, DstSize) + 1; } +fail: + free(pSrcData.pv); return pDstData; } diff --git a/winpr/libwinpr/credui/CMakeLists.txt b/winpr/libwinpr/credui/CMakeLists.txt index b01be1d97ef7..886fe12ebece 100644 --- a/winpr/libwinpr/credui/CMakeLists.txt +++ b/winpr/libwinpr/credui/CMakeLists.txt @@ -19,8 +19,8 @@ winpr_module_add(credui.c) if(WIN32) winpr_library_add_public(credui) -endif() - -if(BUILD_TESTING) - add_subdirectory(test) +else() + if(BUILD_TESTING) + add_subdirectory(test) + endif() endif() diff --git a/winpr/libwinpr/crt/CMakeLists.txt b/winpr/libwinpr/crt/CMakeLists.txt index a2d0dda3e35d..5ee4bbd1e3c0 100644 --- a/winpr/libwinpr/crt/CMakeLists.txt +++ b/winpr/libwinpr/crt/CMakeLists.txt @@ -29,8 +29,8 @@ if (NOT WITH_ICU) endif(NOT WITH_ICU) if (WITH_ICU) - find_package(ICU REQUIRED i18n uc io) - include_directories(${ICU_INCLUDE_DIRS}) + find_package(ICU REQUIRED i18n uc io data) + winpr_include_directory_add(${ICU_INCLUDE_DIRS}) winpr_library_add_private(${ICU_LIBRARIES}) endif (WITH_ICU) diff --git a/winpr/libwinpr/crt/alignment.c b/winpr/libwinpr/crt/alignment.c index af76023d7689..88c3f7d4043c 100644 --- a/winpr/libwinpr/crt/alignment.c +++ b/winpr/libwinpr/crt/alignment.c @@ -242,4 +242,4 @@ void _aligned_free(void* memblock) free(pMem->base_addr); } -#endif +#endif /* _WIN32 */ diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c index 37fcb4b25b3c..6596b42f525c 100644 --- a/winpr/libwinpr/crt/string.c +++ b/winpr/libwinpr/crt/string.c @@ -26,6 +26,7 @@ #include #include +#include #include /* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */ @@ -33,6 +34,23 @@ #include "../log.h" #define TAG WINPR_TAG("crt") +BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator) +{ + const size_t used = strnlen(buffer, size); + const size_t add = strnlen(what, size); + const size_t sep_len = separator ? strnlen(separator, size) : 0; + const size_t sep = (used > 0) ? sep_len : 0; + + if (used + add + sep >= size) + return FALSE; + + if ((used > 0) && (sep_len > 0)) + strncat(buffer, separator, sep_len); + + strncat(buffer, what, add); + return TRUE; +} + #ifndef _WIN32 char* _strdup(const char* strSource) @@ -80,17 +98,39 @@ int _strnicmp(const char* string1, const char* string2, size_t count) int _wcscmp(const WCHAR* string1, const WCHAR* string2) { - WCHAR value1, value2; + WINPR_ASSERT(string1); + WINPR_ASSERT(string2); - while (*string1 && (*string1 == *string2)) + while (TRUE) { - string1++; - string2++; + const WCHAR w1 = *string1++; + const WCHAR w2 = *string2++; + + if (w1 != w2) + return (int)w1 - w2; + else if ((w1 == '\0') || (w2 == '\0')) + return (int)w1 - w2; } - Data_Read_UINT16(string1, value1); - Data_Read_UINT16(string2, value2); - return value1 - value2; + return 0; +} + +int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count) +{ + WINPR_ASSERT(string1); + WINPR_ASSERT(string2); + + for (size_t x = 0; x < count; x++) + { + const WCHAR a = string1[x]; + const WCHAR b = string2[x]; + + if (a != b) + return (int)a - b; + else if ((a == '\0') || (b == '\0')) + return (int)a - b; + } + return 0; } /* _wcslen -> wcslen */ @@ -99,8 +139,7 @@ size_t _wcslen(const WCHAR* str) { const WCHAR* p = (const WCHAR*)str; - if (!p) - return 0; + WINPR_ASSERT(p); while (*p) p++; @@ -114,8 +153,7 @@ size_t _wcsnlen(const WCHAR* str, size_t max) { size_t x; - if (!str) - return 0; + WINPR_ASSERT(str); for (x = 0; x < max; x++) { @@ -126,6 +164,26 @@ size_t _wcsnlen(const WCHAR* str, size_t max) return x; } +/* _wcsstr -> wcsstr */ + +WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch) +{ + WINPR_ASSERT(str); + WINPR_ASSERT(strSearch); + + if (strSearch[0] == '\0') + return str; + + const size_t searchLen = _wcslen(strSearch); + while (*str) + { + if (_wcsncmp(str, strSearch, searchLen) == 0) + return str; + str++; + } + return NULL; +} + /* _wcschr -> wcschr */ WCHAR* _wcschr(const WCHAR* str, WCHAR c) diff --git a/winpr/libwinpr/crt/unicode.c b/winpr/libwinpr/crt/unicode.c index a491979710e1..acbec014e67c 100644 --- a/winpr/libwinpr/crt/unicode.c +++ b/winpr/libwinpr/crt/unicode.c @@ -40,6 +40,8 @@ #include "../log.h" #define TAG WINPR_TAG("unicode") +#define UCNV_CONVERT 1 + /** * Notes on cross-platform Unicode portability: * @@ -201,6 +203,26 @@ int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int targetCapacity = cchWideChar; error = U_ZERO_ERROR; +#if defined(UCNV_CONVERT) + if (cchWideChar == 0) + { + targetLength = + ucnv_convert("UTF-16LE", "UTF-8", NULL, 0, lpMultiByteStr, cbMultiByte, &error); + if (targetLength > 0) + targetLength /= sizeof(WCHAR); + cchWideChar = targetLength; + } + else + { + targetLength = + ucnv_convert("UTF-16LE", "UTF-8", (char*)targetStart, + targetCapacity * sizeof(WCHAR), lpMultiByteStr, cbMultiByte, &error); + if (targetLength > 0) + targetLength /= sizeof(WCHAR); + cchWideChar = U_SUCCESS(error) ? targetLength : 0; + } + +#else if (cchWideChar == 0) { u_strFromUTF8(NULL, 0, &targetLength, lpMultiByteStr, cbMultiByte, &error); @@ -212,6 +234,7 @@ int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr, int &error); cchWideChar = U_SUCCESS(error) ? targetLength : 0; } +#endif } #else @@ -327,6 +350,21 @@ int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int targetCapacity = cbMultiByte; error = U_ZERO_ERROR; +#if defined(UCNV_CONVERT) + if (cbMultiByte == 0) + { + targetLength = ucnv_convert("UTF-8", "UTF-16LE", NULL, 0, (char*)lpWideCharStr, + cchWideChar * sizeof(WCHAR), &error); + cbMultiByte = targetLength; + } + else + { + targetLength = ucnv_convert("UTF-8", "UTF-16LE", targetStart, targetCapacity, + (char*)lpWideCharStr, cchWideChar * sizeof(WCHAR), &error); + cbMultiByte = U_SUCCESS(error) ? targetLength : 0; + } + +#else if (cbMultiByte == 0) { u_strToUTF8(NULL, 0, &targetLength, lpWideCharStr, cchWideChar, &error); @@ -338,6 +376,7 @@ int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr, int &error); cbMultiByte = U_SUCCESS(error) ? targetLength : 0; } +#endif } #else diff --git a/winpr/libwinpr/crt/utf.c b/winpr/libwinpr/crt/utf.c index 8c8d2a841487..2cbc5987b6b4 100644 --- a/winpr/libwinpr/crt/utf.c +++ b/winpr/libwinpr/crt/utf.c @@ -582,7 +582,7 @@ ConversionResult ConvertUTF8toUTF16(const BYTE** sourceStart, const BYTE* source ch -= offsetsFromUTF8[extraBytesToRead]; - if ((target >= end) && (!computeLength)) + if ((target * sizeof(WCHAR) >= end) && (!computeLength)) { source -= (extraBytesToRead + 1); /* Back up source pointer! */ result = targetExhausted; @@ -633,7 +633,7 @@ ConversionResult ConvertUTF8toUTF16(const BYTE** sourceStart, const BYTE* source else { /* target is a character in range 0xFFFF - 0x10FFFF. */ - if ((target + 1 >= end) && (!computeLength)) + if (((target + 1) * sizeof(WCHAR) >= end) && (!computeLength)) { source -= (extraBytesToRead + 1); /* Back up source pointer! */ result = targetExhausted; diff --git a/winpr/libwinpr/crypto/CMakeLists.txt b/winpr/libwinpr/crypto/CMakeLists.txt index d215744c7196..92077ec872d7 100644 --- a/winpr/libwinpr/crypto/CMakeLists.txt +++ b/winpr/libwinpr/crypto/CMakeLists.txt @@ -15,13 +15,26 @@ # See the License for the specific language governing permissions and # limitations under the License. -winpr_module_add( +set(SRCS hash.c rand.c cipher.c cert.c crypto.c - crypto.h) + crypto.h +) +if (WITH_INTERNAL_MD4) + list(APPEND SRCS md4.c md4.h) +endif() + +if (WITH_INTERNAL_MD5) + list(APPEND SRCS md5.c md5.h) + list(APPEND SRCS hmac_md5.c hmac_md5.h) +endif() + +winpr_module_add( + ${SRCS} +) if(OPENSSL_FOUND) winpr_include_directory_add(${OPENSSL_INCLUDE_DIR}) diff --git a/winpr/libwinpr/crypto/cert.c b/winpr/libwinpr/crypto/cert.c index c769f563d743..cecd51692560 100644 --- a/winpr/libwinpr/crypto/cert.c +++ b/winpr/libwinpr/crypto/cert.c @@ -151,6 +151,7 @@ */ #include +#include #ifndef _WIN32 diff --git a/winpr/libwinpr/crypto/cipher.c b/winpr/libwinpr/crypto/cipher.c index e2477f39e058..0fffc6de51d8 100644 --- a/winpr/libwinpr/crypto/cipher.c +++ b/winpr/libwinpr/crypto/cipher.c @@ -29,9 +29,6 @@ #include #include #include -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) -#include -#endif #endif #ifdef WITH_MBEDTLS @@ -61,11 +58,6 @@ static WINPR_RC4_CTX* winpr_RC4_New_Internal(const BYTE* key, size_t keylen, BOO if (keylen > INT_MAX) return NULL; -#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) - if (OSSL_PROVIDER_load(NULL, "legacy") == NULL) - return NULL; -#endif - if (!(ctx = (WINPR_RC4_CTX*)EVP_CIPHER_CTX_new())) return NULL; @@ -74,7 +66,11 @@ static WINPR_RC4_CTX* winpr_RC4_New_Internal(const BYTE* key, size_t keylen, BOO if (!evp) return NULL; +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + EVP_CIPHER_CTX_reset((EVP_CIPHER_CTX*)ctx); +#else EVP_CIPHER_CTX_init((EVP_CIPHER_CTX*)ctx); +#endif if (EVP_EncryptInit_ex((EVP_CIPHER_CTX*)ctx, evp, NULL, NULL, NULL) != 1) { EVP_CIPHER_CTX_free ((EVP_CIPHER_CTX*)ctx); diff --git a/winpr/libwinpr/crypto/hash.c b/winpr/libwinpr/crypto/hash.c index 8265b2e904b9..9e564a16df01 100644 --- a/winpr/libwinpr/crypto/hash.c +++ b/winpr/libwinpr/crypto/hash.c @@ -21,7 +21,7 @@ #endif #include - +#include #include #ifdef WITH_OPENSSL @@ -39,6 +39,15 @@ #include #endif +#if defined(WITH_INTERNAL_MD4) +#include "md4.h" +#endif + +#if defined(WITH_INTERNAL_MD5) +#include "md5.h" +#include "hmac_md5.h" +#endif + /** * HMAC */ @@ -146,46 +155,75 @@ const char* winpr_md_type_to_string(WINPR_MD_TYPE md) return NULL; } -WINPR_HMAC_CTX* winpr_HMAC_New(void) +struct _winpr_hmac_ctx_private_st { - WINPR_HMAC_CTX* ctx = NULL; + WINPR_MD_TYPE md; + +#if defined(WITH_INTERNAL_MD5) + WINPR_HMAC_MD5_CTX hmac_md5; +#endif #if defined(WITH_OPENSSL) - HMAC_CTX* hmac = NULL; -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) + HMAC_CTX* hmac; +#endif +#if defined(WITH_MBEDTLS) + mbedtls_md_context_t hmac; +#endif +}; - if (!(hmac = (HMAC_CTX*)calloc(1, sizeof(HMAC_CTX)))) +WINPR_HMAC_CTX* winpr_HMAC_New(void) +{ + WINPR_HMAC_CTX* ctx = calloc(1, sizeof(WINPR_HMAC_CTX)); + if (!ctx) return NULL; +#if defined(WITH_OPENSSL) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + + if (!(ctx->hmac = (HMAC_CTX*)calloc(1, sizeof(HMAC_CTX)))) + goto fail; - HMAC_CTX_init(hmac); + HMAC_CTX_init(ctx->hmac); #else - if (!(hmac = HMAC_CTX_new())) - return NULL; + if (!(ctx->hmac = HMAC_CTX_new())) + goto fail; #endif - ctx = (WINPR_HMAC_CTX*)hmac; #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* hmac; - - if (!(hmac = (mbedtls_md_context_t*)calloc(1, sizeof(mbedtls_md_context_t)))) - return NULL; - - mbedtls_md_init(hmac); - ctx = (WINPR_HMAC_CTX*)hmac; + mbedtls_md_init(&ctx->hmac); #endif return ctx; + +fail: + winpr_HMAC_Free(ctx); + return NULL; } BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const BYTE* key, size_t keylen) { + WINPR_ASSERT(ctx); + + ctx->md = md; + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + hmac_md5_init(&ctx->hmac_md5, key, keylen); + return TRUE; +#endif + default: + break; + } + #if defined(WITH_OPENSSL) - HMAC_CTX* hmac = (HMAC_CTX*)ctx; + HMAC_CTX* hmac = ctx->hmac; const EVP_MD* evp = winpr_openssl_get_evp_md(md); if (!evp || !hmac) return FALSE; -#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_Init_ex(hmac, key, keylen, evp, NULL); /* no return value on OpenSSL 0.9.x */ return TRUE; #else @@ -195,7 +233,7 @@ BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const BYTE* key, siz #endif #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* hmac = (mbedtls_md_context_t*)ctx; + mbedtls_md_context_t* hmac = &ctx->hmac; mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md); const mbedtls_md_info_t* md_info = mbedtls_md_info_from_type(md_type); @@ -219,9 +257,23 @@ BOOL winpr_HMAC_Init(WINPR_HMAC_CTX* ctx, WINPR_MD_TYPE md, const BYTE* key, siz BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const BYTE* input, size_t ilen) { + WINPR_ASSERT(ctx); + + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + hmac_md5_update(&ctx->hmac_md5, input, ilen); + return TRUE; +#endif + default: + break; + } + #if defined(WITH_OPENSSL) - HMAC_CTX* hmac = (HMAC_CTX*)ctx; -#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) + HMAC_CTX* hmac = ctx->hmac; +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_Update(hmac, input, ilen); /* no return value on OpenSSL 0.9.x */ return TRUE; #else @@ -231,7 +283,7 @@ BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const BYTE* input, size_t ilen) #endif #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* mdctx = (mbedtls_md_context_t*)ctx; + mbedtls_md_context_t* mdctx = &ctx->hmac; if (mbedtls_md_hmac_update(mdctx, input, ilen) == 0) return TRUE; @@ -242,18 +294,25 @@ BOOL winpr_HMAC_Update(WINPR_HMAC_CTX* ctx, const BYTE* input, size_t ilen) BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, BYTE* output, size_t olen) { -#if defined(WITH_OPENSSL) - HMAC_CTX* hmac; -#elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* mdctx; -#endif + WINPR_ASSERT(ctx); - if (!ctx) - return FALSE; + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + if (olen < WINPR_MD5_DIGEST_LENGTH) + return FALSE; + hmac_md5_finalize(&ctx->hmac_md5, output); + return TRUE; +#endif + default: + break; + } #if defined(WITH_OPENSSL) - hmac = (HMAC_CTX*)ctx; -#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER) + HMAC_CTX* hmac = ctx->hmac; +#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_Final(hmac, output, NULL); /* no return value on OpenSSL 0.9.x */ return TRUE; #else @@ -263,7 +322,7 @@ BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, BYTE* output, size_t olen) #endif #elif defined(WITH_MBEDTLS) - mdctx = (mbedtls_md_context_t*)ctx; + mbedtls_md_context_t* mdctx = &ctx->hmac; if (mbedtls_md_hmac_finish(mdctx, output) == 0) return TRUE; @@ -274,12 +333,16 @@ BOOL winpr_HMAC_Final(WINPR_HMAC_CTX* ctx, BYTE* output, size_t olen) void winpr_HMAC_Free(WINPR_HMAC_CTX* ctx) { + if (!ctx) + return; + #if defined(WITH_OPENSSL) - HMAC_CTX* hmac = (HMAC_CTX*)ctx; + HMAC_CTX* hmac = ctx->hmac; if (hmac) { -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) HMAC_CTX_cleanup(hmac); free(hmac); #else @@ -288,15 +351,14 @@ void winpr_HMAC_Free(WINPR_HMAC_CTX* ctx) } #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* hmac = (mbedtls_md_context_t*)ctx; + mbedtls_md_context_t* hmac = &ctx->hmac; if (hmac) - { mbedtls_md_free(hmac); - free(hmac); - } #endif + + free(ctx); } BOOL winpr_HMAC(WINPR_MD_TYPE md, const BYTE* key, size_t keylen, const BYTE* input, size_t ilen, @@ -327,33 +389,60 @@ BOOL winpr_HMAC(WINPR_MD_TYPE md, const BYTE* key, size_t keylen, const BYTE* in * Generic Digest API */ -WINPR_DIGEST_CTX* winpr_Digest_New(void) +struct _winpr_digest_ctx_private_st { - WINPR_DIGEST_CTX* ctx = NULL; + WINPR_MD_TYPE md; + +#if defined(WITH_INTERNAL_MD4) + WINPR_MD4_CTX md4; +#endif +#if defined(WITH_INTERNAL_MD5) + WINPR_MD5_CTX md5; +#endif #if defined(WITH_OPENSSL) EVP_MD_CTX* mdctx; -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) - mdctx = EVP_MD_CTX_create(); +#endif +#if defined(WITH_MBEDTLS) + mbedtls_md_context_t* mdctx; +#endif +}; + +WINPR_DIGEST_CTX* winpr_Digest_New(void) +{ + WINPR_DIGEST_CTX* ctx = calloc(1, sizeof(WINPR_DIGEST_CTX)); + if (!ctx) + return NULL; + +#if defined(WITH_OPENSSL) +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + ctx->mdctx = EVP_MD_CTX_create(); #else - mdctx = EVP_MD_CTX_new(); + ctx->mdctx = EVP_MD_CTX_new(); #endif - ctx = (WINPR_DIGEST_CTX*)mdctx; + if (!ctx->mdctx) + goto fail; + #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* mdctx; - mdctx = (mbedtls_md_context_t*)calloc(1, sizeof(mbedtls_md_context_t)); + ctx->mdctx = (mbedtls_md_context_t*)calloc(1, sizeof(mbedtls_md_context_t)); - if (mdctx) - mbedtls_md_init(mdctx); + if (!ctx->mdctx) + goto fail; - ctx = (WINPR_DIGEST_CTX*)mdctx; + mbedtls_md_init(ctx->mdctx); #endif return ctx; + +fail: + winpr_Digest_Free(ctx); + return NULL; } #if defined(WITH_OPENSSL) static BOOL winpr_Digest_Init_Internal(WINPR_DIGEST_CTX* ctx, const EVP_MD* evp) { - EVP_MD_CTX* mdctx = (EVP_MD_CTX*)ctx; + WINPR_ASSERT(ctx); + EVP_MD_CTX* mdctx = ctx->mdctx; if (!mdctx || !evp) return FALSE; @@ -367,7 +456,8 @@ static BOOL winpr_Digest_Init_Internal(WINPR_DIGEST_CTX* ctx, const EVP_MD* evp) #elif defined(WITH_MBEDTLS) static BOOL winpr_Digest_Init_Internal(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) { - mbedtls_md_context_t* mdctx = (mbedtls_md_context_t*)ctx; + WINPR_ASSERT(ctx); + mbedtls_md_context_t* mdctx = ctx->mdctx; mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md); const mbedtls_md_info_t* md_info = mbedtls_md_info_from_type(md_type); @@ -391,15 +481,16 @@ static BOOL winpr_Digest_Init_Internal(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) BOOL winpr_Digest_Init_Allow_FIPS(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) { + WINPR_ASSERT(ctx); + #if defined(WITH_OPENSSL) - EVP_MD_CTX* mdctx = (EVP_MD_CTX*)ctx; const EVP_MD* evp = winpr_openssl_get_evp_md(md); /* Only MD5 is supported for FIPS allow override */ if (md != WINPR_MD_MD5) return FALSE; - EVP_MD_CTX_set_flags(mdctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); + EVP_MD_CTX_set_flags(ctx->mdctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); return winpr_Digest_Init_Internal(ctx, evp); #elif defined(WITH_MBEDTLS) @@ -413,6 +504,25 @@ BOOL winpr_Digest_Init_Allow_FIPS(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) BOOL winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) { + WINPR_ASSERT(ctx); + + ctx->md = md; + switch (md) + { +#if defined(WITH_INTERNAL_MD4) + case WINPR_MD_MD4: + winpr_MD4_Init(&ctx->md4); + return TRUE; +#endif +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + winpr_MD5_Init(&ctx->md5); + return TRUE; +#endif + default: + break; + } + #if defined(WITH_OPENSSL) const EVP_MD* evp = winpr_openssl_get_evp_md(md); return winpr_Digest_Init_Internal(ctx, evp); @@ -423,14 +533,32 @@ BOOL winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, WINPR_MD_TYPE md) BOOL winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const BYTE* input, size_t ilen) { + WINPR_ASSERT(ctx); + + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD4) + case WINPR_MD_MD4: + winpr_MD4_Update(&ctx->md4, input, ilen); + return TRUE; +#endif +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + winpr_MD5_Update(&ctx->md5, input, ilen); + return TRUE; +#endif + default: + break; + } + #if defined(WITH_OPENSSL) - EVP_MD_CTX* mdctx = (EVP_MD_CTX*)ctx; + EVP_MD_CTX* mdctx = ctx->mdctx; if (EVP_DigestUpdate(mdctx, input, ilen) != 1) return FALSE; #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* mdctx = (mbedtls_md_context_t*)ctx; + mbedtls_md_context_t* mdctx = ctx->mdctx; if (mbedtls_md_update(mdctx, input, ilen) != 0) return FALSE; @@ -441,14 +569,37 @@ BOOL winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const BYTE* input, size_t ilen) BOOL winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, BYTE* output, size_t olen) { + WINPR_ASSERT(ctx); + + switch (ctx->md) + { +#if defined(WITH_INTERNAL_MD4) + case WINPR_MD_MD4: + if (olen < WINPR_MD4_DIGEST_LENGTH) + return FALSE; + winpr_MD4_Final(output, &ctx->md4); + return TRUE; +#endif +#if defined(WITH_INTERNAL_MD5) + case WINPR_MD_MD5: + if (olen < WINPR_MD5_DIGEST_LENGTH) + return FALSE; + winpr_MD5_Final(output, &ctx->md5); + return TRUE; +#endif + + default: + break; + } + #if defined(WITH_OPENSSL) - EVP_MD_CTX* mdctx = (EVP_MD_CTX*)ctx; + EVP_MD_CTX* mdctx = ctx->mdctx; if (EVP_DigestFinal_ex(mdctx, output, NULL) == 1) return TRUE; #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* mdctx = (mbedtls_md_context_t*)ctx; + mbedtls_md_context_t* mdctx = ctx->mdctx; if (mbedtls_md_finish(mdctx, output) == 0) return TRUE; @@ -459,28 +610,28 @@ BOOL winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, BYTE* output, size_t olen) void winpr_Digest_Free(WINPR_DIGEST_CTX* ctx) { + if (!ctx) + return; #if defined(WITH_OPENSSL) - EVP_MD_CTX* mdctx = (EVP_MD_CTX*)ctx; - - if (mdctx) + if (ctx->mdctx) { -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) - EVP_MD_CTX_destroy(mdctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL) + EVP_MD_CTX_destroy(ctx->mdctx); #else - EVP_MD_CTX_free(mdctx); + EVP_MD_CTX_free(ctx->mdctx); #endif } #elif defined(WITH_MBEDTLS) - mbedtls_md_context_t* mdctx = (mbedtls_md_context_t*)ctx; - - if (mdctx) + if (ctx->mdctx) { - mbedtls_md_free(mdctx); - free(mdctx); + mbedtls_md_free(ctx->mdctx); + free(ctx->mdctx); } #endif + free(ctx); } BOOL winpr_Digest_Allow_FIPS(WINPR_MD_TYPE md, const BYTE* input, size_t ilen, BYTE* output, diff --git a/winpr/libwinpr/crypto/hmac_md5.c b/winpr/libwinpr/crypto/hmac_md5.c new file mode 100644 index 000000000000..4bc9b9305c41 --- /dev/null +++ b/winpr/libwinpr/crypto/hmac_md5.c @@ -0,0 +1,54 @@ +#include +#include "hmac_md5.h" +#include "md5.h" + +void hmac_md5_init(WINPR_HMAC_MD5_CTX* ctx, unsigned char* key, size_t key_len) +{ + const WINPR_HMAC_MD5_CTX empty = { 0 }; + unsigned char k_ipad[KEY_IOPAD_SIZE] = { 0 }; + unsigned char k_opad[KEY_IOPAD_SIZE] = { 0 }; + + assert(ctx); + *ctx = empty; + + if (key_len <= KEY_IOPAD_SIZE) + { + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + } + else + { + WINPR_MD5_CTX lctx = { 0 }; + + winpr_MD5_Init(&lctx); + winpr_MD5_Update(&lctx, key, key_len); + winpr_MD5_Final(k_ipad, &lctx); + memcpy(k_opad, k_ipad, KEY_IOPAD_SIZE); + } + for (size_t i = 0; i < KEY_IOPAD_SIZE; i++) + { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + winpr_MD5_Init(&ctx->icontext); + winpr_MD5_Update(&ctx->icontext, k_ipad, KEY_IOPAD_SIZE); + + winpr_MD5_Init(&ctx->ocontext); + winpr_MD5_Update(&ctx->ocontext, k_opad, KEY_IOPAD_SIZE); +} + +void hmac_md5_update(WINPR_HMAC_MD5_CTX* ctx, unsigned char* text, size_t text_len) +{ + assert(ctx); + winpr_MD5_Update(&ctx->icontext, text, text_len); +} + +void hmac_md5_finalize(WINPR_HMAC_MD5_CTX* ctx, unsigned char* hmac) +{ + assert(ctx); + winpr_MD5_Final(hmac, &ctx->icontext); + + winpr_MD5_Update(&ctx->ocontext, hmac, 16); + winpr_MD5_Final(hmac, &ctx->ocontext); +} diff --git a/winpr/libwinpr/crypto/hmac_md5.h b/winpr/libwinpr/crypto/hmac_md5.h new file mode 100644 index 000000000000..a1ea6a045ce1 --- /dev/null +++ b/winpr/libwinpr/crypto/hmac_md5.h @@ -0,0 +1,19 @@ +#ifndef WINPR_HMAC_MD5 +#define WINPR_HMAC_MD5 + +#include + +#include "md5.h" + +#define KEY_IOPAD_SIZE 64 +typedef struct +{ + WINPR_MD5_CTX icontext; + WINPR_MD5_CTX ocontext; +} WINPR_HMAC_MD5_CTX; + +void hmac_md5_init(WINPR_HMAC_MD5_CTX* ctx, unsigned char* key, size_t key_len); +void hmac_md5_update(WINPR_HMAC_MD5_CTX* ctx, unsigned char* text, size_t text_len); +void hmac_md5_finalize(WINPR_HMAC_MD5_CTX* ctx, unsigned char* hmac); + +#endif diff --git a/winpr/libwinpr/crypto/md4.c b/winpr/libwinpr/crypto/md4.c new file mode 100644 index 000000000000..c33aea334b38 --- /dev/null +++ b/winpr/libwinpr/crypto/md4.c @@ -0,0 +1,266 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD4 Message-Digest Algorithm (RFC 1320). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#include + +#include "md4.h" + +/* + * The basic MD4 functions. + * + * F and G are optimized compared to their RFC 1320 definitions, with the + * optimization for F borrowed from Colin Plumb's MD5 implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) + +/* + * The MD4 transformation for all three rounds. + */ +#define STEP(f, a, b, c, d, x, s) \ + (a) += f((b), (c), (d)) + (x); \ + (a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * winpr_MD4_u32plus. In practice, this problem may occur if these MD4 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD4 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) (*(winpr_MD4_u32plus*)&ptr[(n)*4]) +#define GET(n) SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = (winpr_MD4_u32plus)ptr[(n)*4] | ((winpr_MD4_u32plus)ptr[(n)*4 + 1] << 8) | \ + ((winpr_MD4_u32plus)ptr[(n)*4 + 2] << 16) | \ + ((winpr_MD4_u32plus)ptr[(n)*4 + 3] << 24)) +#define GET(n) (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void* body(WINPR_MD4_CTX* ctx, const void* data, unsigned long size) +{ + const unsigned char* ptr; + winpr_MD4_u32plus a, b, c, d; + winpr_MD4_u32plus saved_a, saved_b, saved_c, saved_d; + const winpr_MD4_u32plus ac1 = 0x5a827999, ac2 = 0x6ed9eba1; + + ptr = (const unsigned char*)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do + { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + /* Round 1 */ + STEP(F, a, b, c, d, SET(0), 3) + STEP(F, d, a, b, c, SET(1), 7) + STEP(F, c, d, a, b, SET(2), 11) + STEP(F, b, c, d, a, SET(3), 19) + STEP(F, a, b, c, d, SET(4), 3) + STEP(F, d, a, b, c, SET(5), 7) + STEP(F, c, d, a, b, SET(6), 11) + STEP(F, b, c, d, a, SET(7), 19) + STEP(F, a, b, c, d, SET(8), 3) + STEP(F, d, a, b, c, SET(9), 7) + STEP(F, c, d, a, b, SET(10), 11) + STEP(F, b, c, d, a, SET(11), 19) + STEP(F, a, b, c, d, SET(12), 3) + STEP(F, d, a, b, c, SET(13), 7) + STEP(F, c, d, a, b, SET(14), 11) + STEP(F, b, c, d, a, SET(15), 19) + + /* Round 2 */ + STEP(G, a, b, c, d, GET(0) + ac1, 3) + STEP(G, d, a, b, c, GET(4) + ac1, 5) + STEP(G, c, d, a, b, GET(8) + ac1, 9) + STEP(G, b, c, d, a, GET(12) + ac1, 13) + STEP(G, a, b, c, d, GET(1) + ac1, 3) + STEP(G, d, a, b, c, GET(5) + ac1, 5) + STEP(G, c, d, a, b, GET(9) + ac1, 9) + STEP(G, b, c, d, a, GET(13) + ac1, 13) + STEP(G, a, b, c, d, GET(2) + ac1, 3) + STEP(G, d, a, b, c, GET(6) + ac1, 5) + STEP(G, c, d, a, b, GET(10) + ac1, 9) + STEP(G, b, c, d, a, GET(14) + ac1, 13) + STEP(G, a, b, c, d, GET(3) + ac1, 3) + STEP(G, d, a, b, c, GET(7) + ac1, 5) + STEP(G, c, d, a, b, GET(11) + ac1, 9) + STEP(G, b, c, d, a, GET(15) + ac1, 13) + + /* Round 3 */ + STEP(H, a, b, c, d, GET(0) + ac2, 3) + STEP(H, d, a, b, c, GET(8) + ac2, 9) + STEP(H, c, d, a, b, GET(4) + ac2, 11) + STEP(H, b, c, d, a, GET(12) + ac2, 15) + STEP(H, a, b, c, d, GET(2) + ac2, 3) + STEP(H, d, a, b, c, GET(10) + ac2, 9) + STEP(H, c, d, a, b, GET(6) + ac2, 11) + STEP(H, b, c, d, a, GET(14) + ac2, 15) + STEP(H, a, b, c, d, GET(1) + ac2, 3) + STEP(H, d, a, b, c, GET(9) + ac2, 9) + STEP(H, c, d, a, b, GET(5) + ac2, 11) + STEP(H, b, c, d, a, GET(13) + ac2, 15) + STEP(H, a, b, c, d, GET(3) + ac2, 3) + STEP(H, d, a, b, c, GET(11) + ac2, 9) + STEP(H, c, d, a, b, GET(7) + ac2, 11) + STEP(H, b, c, d, a, GET(15) + ac2, 15) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void winpr_MD4_Init(WINPR_MD4_CTX* ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void winpr_MD4_Update(WINPR_MD4_CTX* ctx, const void* data, unsigned long size) +{ + winpr_MD4_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) + { + available = 64 - used; + + if (size < available) + { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char*)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) + { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void winpr_MD4_Final(unsigned char* result, WINPR_MD4_CTX* ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) + { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + memset(ctx, 0, sizeof(*ctx)); +} diff --git a/winpr/libwinpr/crypto/md4.h b/winpr/libwinpr/crypto/md4.h new file mode 100644 index 000000000000..1ea008eccbef --- /dev/null +++ b/winpr/libwinpr/crypto/md4.h @@ -0,0 +1,46 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD4 Message-Digest Algorithm (RFC 1320). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md4.c for more information. + */ + +#if !defined(WINPR_MD4_H) +#define WINPR_MD4_H + +#include + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef UINT32 winpr_MD4_u32plus; + +typedef struct +{ + winpr_MD4_u32plus lo, hi; + winpr_MD4_u32plus a, b, c, d; + unsigned char buffer[64]; + winpr_MD4_u32plus block[16]; +} WINPR_MD4_CTX; + +extern void winpr_MD4_Init(WINPR_MD4_CTX* ctx); +extern void winpr_MD4_Update(WINPR_MD4_CTX* ctx, const void* data, unsigned long size); +extern void winpr_MD4_Final(unsigned char* result, WINPR_MD4_CTX* ctx); + +#endif diff --git a/winpr/libwinpr/crypto/md5.c b/winpr/libwinpr/crypto/md5.c new file mode 100644 index 000000000000..96e8282a32c0 --- /dev/null +++ b/winpr/libwinpr/crypto/md5.c @@ -0,0 +1,287 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them in a + * properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned memory + * accesses is just an optimization. Nothing will break if it fails to detect + * a suitable architecture. + * + * Unfortunately, this optimization may be a C strict aliasing rules violation + * if the caller's data buffer has effective type that cannot be aliased by + * MD5_u32plus. In practice, this problem may occur if these MD5 routines are + * inlined into a calling function, or with future and dangerously advanced + * link-time optimizations. For the time being, keeping these MD5 routines in + * their own translation unit avoids the problem. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) (*(winpr_MD5_u32plus*)&ptr[(n)*4]) +#define GET(n) SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = (winpr_MD5_u32plus)ptr[(n)*4] | ((winpr_MD5_u32plus)ptr[(n)*4 + 1] << 8) | \ + ((winpr_MD5_u32plus)ptr[(n)*4 + 2] << 16) | \ + ((winpr_MD5_u32plus)ptr[(n)*4 + 3] << 24)) +#define GET(n) (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update the bit + * counters. There are no alignment requirements. + */ +static const void* body(WINPR_MD5_CTX* ctx, const void* data, unsigned long size) +{ + const unsigned char* ptr; + winpr_MD5_u32plus a, b, c, d; + winpr_MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char*)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do + { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + /* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + + /* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + + /* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + + /* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void winpr_MD5_Init(WINPR_MD5_CTX* ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void winpr_MD5_Update(WINPR_MD5_CTX* ctx, const void* data, unsigned long size) +{ + winpr_MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) + { + available = 64 - used; + + if (size < available) + { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char*)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) + { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +#define OUT(dst, src) \ + (dst)[0] = (unsigned char)(src); \ + (dst)[1] = (unsigned char)((src) >> 8); \ + (dst)[2] = (unsigned char)((src) >> 16); \ + (dst)[3] = (unsigned char)((src) >> 24); + +void winpr_MD5_Final(unsigned char* result, WINPR_MD5_CTX* ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) + { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + OUT(&ctx->buffer[56], ctx->lo) + OUT(&ctx->buffer[60], ctx->hi) + + body(ctx, ctx->buffer, 64); + + OUT(&result[0], ctx->a) + OUT(&result[4], ctx->b) + OUT(&result[8], ctx->c) + OUT(&result[12], ctx->d) + + memset(ctx, 0, sizeof(*ctx)); +} diff --git a/winpr/libwinpr/crypto/md5.h b/winpr/libwinpr/crypto/md5.h new file mode 100644 index 000000000000..2056b9a806fc --- /dev/null +++ b/winpr/libwinpr/crypto/md5.h @@ -0,0 +1,48 @@ + + +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#if !defined(WINPR_MD5_H) +#define WINPR_MD5_H + +#include + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef UINT32 winpr_MD5_u32plus; + +typedef struct +{ + winpr_MD5_u32plus lo, hi; + winpr_MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + winpr_MD5_u32plus block[16]; +} WINPR_MD5_CTX; + +extern void winpr_MD5_Init(WINPR_MD5_CTX* ctx); +extern void winpr_MD5_Update(WINPR_MD5_CTX* ctx, const void* data, unsigned long size); +extern void winpr_MD5_Final(unsigned char* result, WINPR_MD5_CTX* ctx); + +#endif diff --git a/winpr/libwinpr/crypto/test/TestCryptoHash.c b/winpr/libwinpr/crypto/test/TestCryptoHash.c index 8b911866bb7a..761d72d77522 100644 --- a/winpr/libwinpr/crypto/test/TestCryptoHash.c +++ b/winpr/libwinpr/crypto/test/TestCryptoHash.c @@ -11,7 +11,7 @@ static const BYTE TEST_MD5_HASH[] = static BOOL test_crypto_hash_md5(void) { BOOL result = FALSE; - BYTE hash[WINPR_MD5_DIGEST_LENGTH]; + BYTE hash[WINPR_MD5_DIGEST_LENGTH] = { 0 }; WINPR_DIGEST_CTX* ctx; if (!(ctx = winpr_Digest_New())) @@ -64,7 +64,7 @@ static const BYTE TEST_MD4_HASH[] = static BOOL test_crypto_hash_md4(void) { BOOL result = FALSE; - BYTE hash[WINPR_MD4_DIGEST_LENGTH]; + BYTE hash[WINPR_MD4_DIGEST_LENGTH] = { 0 }; WINPR_DIGEST_CTX* ctx; if (!(ctx = winpr_Digest_New())) @@ -117,7 +117,7 @@ static const BYTE TEST_SHA1_HASH[] = static BOOL test_crypto_hash_sha1(void) { BOOL result = FALSE; - BYTE hash[WINPR_SHA1_DIGEST_LENGTH]; + BYTE hash[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; WINPR_DIGEST_CTX* ctx; if (!(ctx = winpr_Digest_New())) @@ -168,11 +168,11 @@ static const char TEST_HMAC_MD5_DATA[] = "Hi There"; static const BYTE TEST_HMAC_MD5_KEY[] = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"; static const BYTE TEST_HMAC_MD5_HASH[] = - "\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d"; + "\xb5\x79\x91\xa2\x20\x3d\x49\x2d\x73\xfb\x71\x43\xdf\xc5\x08\x28"; static BOOL test_crypto_hash_hmac_md5(void) { - BYTE hash[WINPR_MD5_DIGEST_LENGTH]; + BYTE hash[WINPR_MD5_DIGEST_LENGTH] = { 0 }; WINPR_HMAC_CTX* ctx; BOOL result = FALSE; @@ -193,6 +193,12 @@ static BOOL test_crypto_hash_hmac_md5(void) fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __FUNCTION__); goto out; } + if (!winpr_HMAC_Update(ctx, (const BYTE*)TEST_HMAC_MD5_DATA, + strnlen(TEST_HMAC_MD5_DATA, sizeof(TEST_HMAC_MD5_DATA)))) + { + fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __FUNCTION__); + goto out; + } if (!winpr_HMAC_Final(ctx, hash, sizeof(hash))) { fprintf(stderr, "%s: winpr_HMAC_Final failed\n", __FUNCTION__); @@ -225,11 +231,11 @@ static const char TEST_HMAC_SHA1_DATA[] = "Hi There"; static const BYTE TEST_HMAC_SHA1_KEY[] = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"; static const BYTE TEST_HMAC_SHA1_HASH[] = - "\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00"; + "\xab\x23\x08\x2d\xca\x0c\x75\xea\xca\x60\x09\xc0\xb8\x8c\x2d\xf4\xf4\xbf\x88\xee"; static BOOL test_crypto_hash_hmac_sha1(void) { - BYTE hash[WINPR_SHA1_DIGEST_LENGTH]; + BYTE hash[WINPR_SHA1_DIGEST_LENGTH] = { 0 }; WINPR_HMAC_CTX* ctx; BOOL result = FALSE; @@ -250,6 +256,12 @@ static BOOL test_crypto_hash_hmac_sha1(void) fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __FUNCTION__); goto out; } + if (!winpr_HMAC_Update(ctx, (const BYTE*)TEST_HMAC_SHA1_DATA, + strnlen(TEST_HMAC_SHA1_DATA, sizeof(TEST_HMAC_SHA1_DATA)))) + { + fprintf(stderr, "%s: winpr_HMAC_Update failed\n", __FUNCTION__); + goto out; + } if (!winpr_HMAC_Final(ctx, hash, sizeof(hash))) { fprintf(stderr, "%s: winpr_HMAC_Final failed\n", __FUNCTION__); diff --git a/winpr/libwinpr/file/generic.c b/winpr/libwinpr/file/generic.c index 1f8bf9935f50..9668bb55d1e2 100644 --- a/winpr/libwinpr/file/generic.c +++ b/winpr/libwinpr/file/generic.c @@ -24,6 +24,7 @@ #endif #include +#include #include #include @@ -539,15 +540,72 @@ DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName) return ret; } +static char* append(char* buffer, size_t size, const char* append) +{ + winpr_str_append(append, buffer, size, "|"); + return buffer; +} + +static const char* flagsToStr(char* buffer, size_t size, DWORD flags) +{ + char strflags[32] = { 0 }; + if (flags & FILE_ATTRIBUTE_READONLY) + append(buffer, size, "FILE_ATTRIBUTE_READONLY"); + if (flags & FILE_ATTRIBUTE_HIDDEN) + append(buffer, size, "FILE_ATTRIBUTE_HIDDEN"); + if (flags & FILE_ATTRIBUTE_SYSTEM) + append(buffer, size, "FILE_ATTRIBUTE_SYSTEM"); + if (flags & FILE_ATTRIBUTE_DIRECTORY) + append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY"); + if (flags & FILE_ATTRIBUTE_ARCHIVE) + append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE"); + if (flags & FILE_ATTRIBUTE_DEVICE) + append(buffer, size, "FILE_ATTRIBUTE_DEVICE"); + if (flags & FILE_ATTRIBUTE_NORMAL) + append(buffer, size, "FILE_ATTRIBUTE_NORMAL"); + if (flags & FILE_ATTRIBUTE_TEMPORARY) + append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY"); + if (flags & FILE_ATTRIBUTE_SPARSE_FILE) + append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE"); + if (flags & FILE_ATTRIBUTE_REPARSE_POINT) + append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT"); + if (flags & FILE_ATTRIBUTE_COMPRESSED) + append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED"); + if (flags & FILE_ATTRIBUTE_OFFLINE) + append(buffer, size, "FILE_ATTRIBUTE_OFFLINE"); + if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) + append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED"); + if (flags & FILE_ATTRIBUTE_ENCRYPTED) + append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED"); + if (flags & FILE_ATTRIBUTE_VIRTUAL) + append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL"); + + _snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags); + winpr_str_append(strflags, buffer, size, NULL); + return buffer; +} + BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) { struct stat st; + int fd; + BOOL rc = FALSE; - if (stat(lpFileName, &st) != 0) + if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) { - return FALSE; + char buffer[8192] = { 0 }; + const char* flags = + flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); + WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags); } + fd = open(lpFileName, O_RDONLY); + if (fd < 0) + return FALSE; + + if (fstat(fd, &st) != 0) + goto fail; + if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) { st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); @@ -557,12 +615,13 @@ BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) st.st_mode |= S_IWUSR; } - if (chmod(lpFileName, st.st_mode) != 0) - { - return FALSE; - } + if (fchmod(fd, st.st_mode) != 0) + goto fail; - return TRUE; + rc = TRUE; +fail: + close(fd); + return rc; } BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) @@ -570,6 +629,14 @@ BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) BOOL ret; LPSTR lpCFileName; + if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) + { + char buffer[8192] = { 0 }; + const char* flags = + flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); + WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags); + } + if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); diff --git a/winpr/libwinpr/file/test/CMakeLists.txt b/winpr/libwinpr/file/test/CMakeLists.txt index c7015c67087d..3999271105c8 100644 --- a/winpr/libwinpr/file/test/CMakeLists.txt +++ b/winpr/libwinpr/file/test/CMakeLists.txt @@ -1,54 +1,58 @@ -set(MODULE_NAME "TestFile") -set(MODULE_PREFIX "TEST_FILE") +if (NOT WIN32) + set(MODULE_NAME "TestFile") + set(MODULE_PREFIX "TEST_FILE") + + set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) + + set(${MODULE_PREFIX}_TESTS + TestFileCreateFile.c + TestFileDeleteFile.c + TestFileReadFile.c + TestSetFileAttributes.c + TestFileWriteFile.c + TestFilePatternMatch.c + TestFileFindFirstFile.c + TestFileFindFirstFileEx.c + TestFileFindNextFile.c + TestFileGetStdHandle.c + ) + + create_test_sourcelist(${MODULE_PREFIX}_SRCS + ${${MODULE_PREFIX}_DRIVER} + ${${MODULE_PREFIX}_TESTS}) + + add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + + target_link_libraries(${MODULE_NAME} winpr) + + set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") + + if(NOT MSVC) + set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME}Area") + else() + set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${MODULE_NAME}Area") + endif() + + file(MAKE_DIRECTORY "${TEST_AREA}") + file(WRITE "${TEST_AREA}/TestFile1" "TestFile1") + file(WRITE "${TEST_AREA}/TestFile2" "TestFile2") + file(WRITE "${TEST_AREA}/TestFile3" "TestFile3") + file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory1") + file(WRITE "${TEST_AREA}/TestDirectory1/TestDirectory1File1" "TestDirectory1File1") + file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory2") + file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File1" "TestDirectory2File1") + file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File2" "TestDirectory2File2") + file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory3") + file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File1" "TestDirectory3File1") + file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File2" "TestDirectory3File2") + file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File3" "TestDirectory3File3") + + foreach(test ${${MODULE_PREFIX}_TESTS}) + get_filename_component(TestName ${test} NAME_WE) + add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName} ${TEST_AREA}) + endforeach() + + set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") -set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c) - -set(${MODULE_PREFIX}_TESTS - TestFileCreateFile.c - TestFileDeleteFile.c - TestFileReadFile.c - TestFileWriteFile.c - TestFilePatternMatch.c - TestFileFindFirstFile.c - TestFileFindFirstFileEx.c - TestFileFindNextFile.c - TestFileGetStdHandle.c -) - -create_test_sourcelist(${MODULE_PREFIX}_SRCS - ${${MODULE_PREFIX}_DRIVER} - ${${MODULE_PREFIX}_TESTS}) - -add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) - -target_link_libraries(${MODULE_NAME} winpr) - -set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}") - -if(NOT MSVC) - set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME}Area") -else() - set(TEST_AREA "${TESTING_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}/${MODULE_NAME}Area") endif() - -file(MAKE_DIRECTORY "${TEST_AREA}") -file(WRITE "${TEST_AREA}/TestFile1" "TestFile1") -file(WRITE "${TEST_AREA}/TestFile2" "TestFile2") -file(WRITE "${TEST_AREA}/TestFile3" "TestFile3") -file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory1") -file(WRITE "${TEST_AREA}/TestDirectory1/TestDirectory1File1" "TestDirectory1File1") -file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory2") -file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File1" "TestDirectory2File1") -file(WRITE "${TEST_AREA}/TestDirectory2/TestDirectory2File2" "TestDirectory2File2") -file(MAKE_DIRECTORY "${TEST_AREA}/TestDirectory3") -file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File1" "TestDirectory3File1") -file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File2" "TestDirectory3File2") -file(WRITE "${TEST_AREA}/TestDirectory3/TestDirectory3File3" "TestDirectory3File3") - -foreach(test ${${MODULE_PREFIX}_TESTS}) - get_filename_component(TestName ${test} NAME_WE) - add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName} ${TEST_AREA}) -endforeach() - -set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test") diff --git a/winpr/libwinpr/file/test/TestSetFileAttributes.c b/winpr/libwinpr/file/test/TestSetFileAttributes.c new file mode 100644 index 000000000000..e81c89c82e15 --- /dev/null +++ b/winpr/libwinpr/file/test/TestSetFileAttributes.c @@ -0,0 +1,149 @@ + +#include +#include +#include +#include +#include +#include +#include + +static const DWORD allflags[] = { + 0, + FILE_ATTRIBUTE_READONLY, + FILE_ATTRIBUTE_HIDDEN, + FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_DIRECTORY, + FILE_ATTRIBUTE_ARCHIVE, + FILE_ATTRIBUTE_DEVICE, + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_TEMPORARY, + FILE_ATTRIBUTE_SPARSE_FILE, + FILE_ATTRIBUTE_REPARSE_POINT, + FILE_ATTRIBUTE_COMPRESSED, + FILE_ATTRIBUTE_OFFLINE, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, + FILE_ATTRIBUTE_ENCRYPTED, + FILE_ATTRIBUTE_VIRTUAL, + FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_DEVICE | + FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_SPARSE_FILE | FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_COMPRESSED | FILE_ATTRIBUTE_OFFLINE, + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_VIRTUAL +}; + +static BOOL test_SetFileAttributesA(void) +{ + BOOL rc = FALSE; + HANDLE handle; + DWORD x; + const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY }; + char* name = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd"); + if (!name) + goto fail; + + for (x = 0; x < ARRAYSIZE(allflags); x++) + { + const DWORD flag = allflags[x]; + rc = SetFileAttributesA(NULL, flag); + if (rc) + goto fail; + + rc = SetFileAttributesA(name, flag); + if (rc) + goto fail; + } + + handle = CreateFileA(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + goto fail; + CloseHandle(handle); + + for (x = 0; x < ARRAYSIZE(flags); x++) + { + DWORD attr; + const DWORD flag = flags[x]; + rc = SetFileAttributesA(name, flag); + if (!rc) + goto fail; + + attr = GetFileAttributesA(name); + if (flag != 0) + { + if ((attr & flag) == 0) + goto fail; + } + } + +fail: + DeleteFileA(name); + free(name); + return rc; +} + +static BOOL test_SetFileAttributesW(void) +{ + BOOL rc = FALSE; + WCHAR* name = NULL; + HANDLE handle; + DWORD x; + const DWORD flags[] = { 0, FILE_ATTRIBUTE_READONLY }; + char* base = GetKnownSubPath(KNOWN_PATH_TEMP, "afsklhjwe4oq5iu432oijrlkejadlkhjaklhfdkahfd"); + if (!base) + goto fail; + + ConvertToUnicode(CP_UTF8, 0, base, -1, &name, 0); + + for (x = 0; x < ARRAYSIZE(allflags); x++) + { + const DWORD flag = allflags[x]; + rc = SetFileAttributesW(NULL, flag); + if (rc) + goto fail; + + rc = SetFileAttributesW(name, flag); + if (rc) + goto fail; + } + + handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, NULL); + if (handle == INVALID_HANDLE_VALUE) + goto fail; + CloseHandle(handle); + + for (x = 0; x < ARRAYSIZE(flags); x++) + { + DWORD attr; + const DWORD flag = flags[x]; + rc = SetFileAttributesW(name, flag); + if (!rc) + goto fail; + + attr = GetFileAttributesW(name); + if (flag != 0) + { + if ((attr & flag) == 0) + goto fail; + } + } + +fail: + DeleteFileW(name); + free(name); + free(base); + return rc; +} + +int TestSetFileAttributes(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_SetFileAttributesA()) + return -1; + if (!test_SetFileAttributesW()) + return -1; + return 0; +} diff --git a/winpr/libwinpr/path/shell_ios.m b/winpr/libwinpr/path/shell_ios.m index 7a6f78bf1b84..b2b9e31d5f3c 100644 --- a/winpr/libwinpr/path/shell_ios.m +++ b/winpr/libwinpr/path/shell_ios.m @@ -26,7 +26,7 @@ #include "shell_ios.h" -NSString *ios_get_directory_for_search_path(NSString *searchPath) +NSString *ios_get_directory_for_search_path(NSSearchPathDirectory searchPath) { return [NSSearchPathForDirectoriesInDomains(searchPath, NSUserDomainMask, YES) lastObject]; } diff --git a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c index 6c8d3be2e29c..ba2fce09a074 100644 --- a/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c +++ b/winpr/libwinpr/pipe/test/TestPipeCreateNamedPipeOverlapped.c @@ -19,7 +19,7 @@ static BYTE CLIENT_MESSAGE[PIPE_BUFFER_SIZE]; static BOOL bClientSuccess = FALSE; static BOOL bServerSuccess = FALSE; -static HANDLE serverReadyEvent; +static HANDLE serverReadyEvent = NULL; static LPTSTR lpszPipeName = _T("\\\\.\\pipe\\winpr_test_pipe_overlapped"); @@ -30,11 +30,13 @@ static DWORD WINAPI named_pipe_client_thread(LPVOID arg) HANDLE hNamedPipe = NULL; BYTE* lpReadBuffer = NULL; BOOL fSuccess = FALSE; - OVERLAPPED overlapped; + OVERLAPPED overlapped = { 0 }; DWORD nNumberOfBytesToRead; DWORD nNumberOfBytesToWrite; DWORD NumberOfBytesTransferred; + WINPR_UNUSED(arg); + status = WaitForSingleObject(serverReadyEvent, PIPE_TIMEOUT_MS); if (status != WAIT_OBJECT_0) { @@ -43,8 +45,6 @@ static DWORD WINAPI named_pipe_client_thread(LPVOID arg) } /* 1: initialize overlapped structure */ - - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { printf("client: CreateEvent failure: %" PRIu32 "\n", GetLastError()); @@ -159,16 +159,16 @@ static DWORD WINAPI named_pipe_server_thread(LPVOID arg) HANDLE hEvent = NULL; HANDLE hNamedPipe = NULL; BYTE* lpReadBuffer = NULL; - OVERLAPPED overlapped; + OVERLAPPED overlapped = { 0 }; BOOL fSuccess = FALSE; BOOL fConnected = FALSE; DWORD nNumberOfBytesToRead; DWORD nNumberOfBytesToWrite; DWORD NumberOfBytesTransferred; - /* 1: initialize overlapped structure */ + WINPR_UNUSED(arg); - ZeroMemory(&overlapped, sizeof(OVERLAPPED)); + /* 1: initialize overlapped structure */ if (!(hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { printf("server: CreateEvent failure: %" PRIu32 "\n", GetLastError()); @@ -332,8 +332,8 @@ static DWORD WINAPI named_pipe_server_thread(LPVOID arg) int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) { - HANDLE ClientThread; - HANDLE ServerThread; + HANDLE ClientThread = NULL; + HANDLE ServerThread = NULL; int result = -1; FillMemory(SERVER_MESSAGE, PIPE_BUFFER_SIZE, 0xAA); @@ -349,8 +349,7 @@ int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) printf("CreateThread (client) failed: %" PRIu32 "\n", GetLastError()); goto out; } - if (!(ServerThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)named_pipe_server_thread, - NULL, 0, NULL))) + if (!(ServerThread = CreateThread(NULL, 0, named_pipe_server_thread, NULL, 0, NULL))) { printf("CreateThread (server) failed: %" PRIu32 "\n", GetLastError()); goto out; @@ -372,6 +371,13 @@ int TestPipeCreateNamedPipeOverlapped(int argc, char* argv[]) out: + if (ClientThread) + CloseHandle(ClientThread); + if (ServerThread) + CloseHandle(ServerThread); + if (serverReadyEvent) + CloseHandle(serverReadyEvent); + #ifndef _WIN32 if (result == 0) { diff --git a/winpr/libwinpr/registry/registry.c b/winpr/libwinpr/registry/registry.c index 726233e3def2..e0c148b5a61b 100644 --- a/winpr/libwinpr/registry/registry.c +++ b/winpr/libwinpr/registry/registry.c @@ -34,18 +34,18 @@ #include #include #include + #include +#include #include "registry_reg.h" static Reg* instance = NULL; -static Reg* RegGetInstance() +static Reg* RegGetInstance(void) { if (!instance) - { instance = reg_open(1); - } return instance; } @@ -213,18 +213,23 @@ LONG RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesir LONG RegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) { - Reg* reg; RegKey* pKey; - reg = RegGetInstance(); + Reg* reg = RegGetInstance(); if (!reg) return -1; + if (hKey != HKEY_LOCAL_MACHINE) + return ERROR_FILE_NOT_FOUND; + + assert(reg->root_key); pKey = reg->root_key->subkeys; while (pKey != NULL) { - if (_stricmp(pKey->subname, lpSubKey) == 0) + assert(lpSubKey); + + if (pKey->subname && (_stricmp(pKey->subname, lpSubKey) == 0)) { *phkResult = (HKEY)pKey; return ERROR_SUCCESS; @@ -271,24 +276,48 @@ LONG RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD RegKey* key; RegVal* pValue; + WINPR_UNUSED(lpReserved); + key = (RegKey*)hKey; + assert(key); + pValue = key->values; while (pValue != NULL) { if (strcmp(pValue->name, lpValueName) == 0) { + if (lpType) + *lpType = pValue->type; + if (pValue->type == REG_DWORD) { DWORD* pData = (DWORD*)lpData; - if (pData != NULL) + if (lpcbData) { - *pData = pValue->data.dword; + DWORD size = *lpcbData; + *lpcbData = sizeof(DWORD); + if (pData) + { + if (size < *lpcbData) + return ERROR_MORE_DATA; + } } - *lpcbData = sizeof(DWORD); + if (pData != NULL) + { + DWORD size; + assert(lpcbData); + size = *lpcbData; + *lpcbData = sizeof(DWORD); + if (size < sizeof(DWORD)) + return ERROR_MORE_DATA; + *pData = pValue->data.dword; + } + else if (lpcbData != NULL) + *lpcbData = sizeof(DWORD); return ERROR_SUCCESS; } else if (pValue->type == REG_SZ) @@ -300,11 +329,18 @@ LONG RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD if (pData != NULL) { + DWORD size; + assert(lpcbData); + + size = *lpcbData; + *lpcbData = length; + if (size < length) + return ERROR_MORE_DATA; memcpy(pData, pValue->data.string, length); pData[length] = '\0'; } - - *lpcbData = (UINT32)length; + else if (lpcbData) + *lpcbData = (UINT32)length; return ERROR_SUCCESS; } diff --git a/winpr/libwinpr/registry/registry_reg.c b/winpr/libwinpr/registry/registry_reg.c index 353e490c113f..3867b066fecc 100644 --- a/winpr/libwinpr/registry/registry_reg.c +++ b/winpr/libwinpr/registry/registry_reg.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -40,7 +41,7 @@ struct reg_data_type { char* tag; - int length; + size_t length; DWORD type; }; @@ -52,50 +53,71 @@ static struct reg_data_type REG_DATA_TYPE_TABLE[] = { { "\"", 1, REG_SZ }, { "hex:", 4, REG_BINARY }, { "hex(2):\"", 8, REG_EXPAND_SZ }, { "hex(7):\"", 8, REG_MULTI_SZ }, - { "hex(b):\"", 8, REG_QWORD }, - { NULL, 0, 0 } }; - -static char* REG_DATA_TYPE_STRINGS[] = { "REG_NONE", - "REG_SZ", - "REG_EXPAND_SZ", - "REG_BINARY", - "REG_DWORD", - "REG_DWORD_BIG_ENDIAN", - "REG_LINK", - "REG_MULTI_SZ", - "REG_RESOURCE_LIST", - "REG_FULL_RESOURCE_DESCRIPTOR", - "REG_RESOURCE_REQUIREMENTS_LIST", - "REG_QWORD" }; - -static void reg_load_start(Reg* reg) + { "hex(b):\"", 8, REG_QWORD } }; + +static char* reg_data_type_string(DWORD type) { + switch (type) + { + case REG_NONE: + return "REG_NONE"; + case REG_SZ: + return "REG_SZ"; + case REG_EXPAND_SZ: + return "REG_EXPAND_SZ"; + case REG_BINARY: + return "REG_BINARY"; + case REG_DWORD: + return "REG_DWORD"; + case REG_DWORD_BIG_ENDIAN: + return "REG_DWORD_BIG_ENDIAN"; + case REG_LINK: + return "REG_LINK"; + case REG_MULTI_SZ: + return "REG_MULTI_SZ"; + case REG_RESOURCE_LIST: + return "REG_RESOURCE_LIST"; + case REG_FULL_RESOURCE_DESCRIPTOR: + return "REG_FULL_RESOURCE_DESCRIPTOR"; + case REG_RESOURCE_REQUIREMENTS_LIST: + return "REG_RESOURCE_REQUIREMENTS_LIST"; + case REG_QWORD: + return "REG_QWORD"; + default: + return "REG_UNKNOWN"; + } +} + +static BOOL reg_load_start(Reg* reg) +{ + char* buffer; INT64 file_size; + + WINPR_ASSERT(reg); + WINPR_ASSERT(reg->fp); + _fseeki64(reg->fp, 0, SEEK_END); file_size = _ftelli64(reg->fp); _fseeki64(reg->fp, 0, SEEK_SET); reg->line = NULL; reg->next_line = NULL; - reg->buffer = NULL; if (file_size < 1) - return; + return FALSE; - reg->buffer = (char*)malloc(file_size + 2); + buffer = (char*)realloc(reg->buffer, (size_t)file_size + 2); - if (!reg->buffer) - return; + if (!buffer) + return FALSE; + reg->buffer = buffer; - if (fread(reg->buffer, file_size, 1, reg->fp) != 1) - { - free(reg->buffer); - reg->buffer = NULL; - return; - } + if (fread(reg->buffer, (size_t)file_size, 1, reg->fp) != 1) + return FALSE; reg->buffer[file_size] = '\n'; reg->buffer[file_size + 1] = '\0'; reg->next_line = strtok(reg->buffer, "\n"); + return TRUE; } static void reg_load_finish(Reg* reg) @@ -112,15 +134,23 @@ static void reg_load_finish(Reg* reg) static RegVal* reg_load_value(Reg* reg, RegKey* key) { - int index; - char* p[5]; - int length; - char* name; + size_t index; + char* p[5] = { 0 }; + size_t length; + char* name = NULL; char* type; char* data; - RegVal* value; + RegVal* value = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + WINPR_ASSERT(reg->line); + p[0] = reg->line + 1; p[1] = strstr(p[0], "\"="); + if (!p[1]) + return NULL; + p[2] = p[1] + 2; type = p[2]; @@ -129,67 +159,85 @@ static RegVal* reg_load_value(Reg* reg, RegKey* key) else p[3] = strchr(p[2], ':'); + if (!p[3]) + return NULL; + data = p[3] + 1; - length = p[1] - p[0]; - name = (char*)malloc(length + 1); + length = (size_t)(p[1] - p[0]); + if (length < 1) + goto fail; + + name = (char*)calloc(length + 1, sizeof(char)); if (!name) - return NULL; + goto fail; memcpy(name, p[0], length); - name[length] = '\0'; - value = (RegVal*)malloc(sizeof(RegVal)); + value = (RegVal*)calloc(1, sizeof(RegVal)); if (!value) - { - free(name); - return NULL; - } + goto fail; value->name = name; value->type = REG_NONE; - value->next = value->prev = NULL; - for (index = 0; REG_DATA_TYPE_TABLE[index].length > 0; index++) + for (index = 0; index < ARRAYSIZE(REG_DATA_TYPE_TABLE); index++) { - if (strncmp(type, REG_DATA_TYPE_TABLE[index].tag, REG_DATA_TYPE_TABLE[index].length) == 0) + const struct reg_data_type* current = ®_DATA_TYPE_TABLE[index]; + WINPR_ASSERT(current->tag); + WINPR_ASSERT(current->length > 0); + WINPR_ASSERT(current->type != REG_NONE); + + if (strncmp(type, current->tag, current->length) == 0) { - value->type = REG_DATA_TYPE_TABLE[index].type; + value->type = current->type; break; } } - if (value->type == REG_DWORD) + switch (value->type) { - unsigned long val; - errno = 0; - val = strtoul(data, NULL, 16); - - if ((errno != 0) || (val > UINT32_MAX)) + case REG_DWORD: { - free(value); - free(name); - return NULL; - } + unsigned long val; + errno = 0; + val = strtoul(data, NULL, 16); - value->data.dword = val; - } - else if (value->type == REG_SZ) - { - p[4] = strchr(data, '"'); - p[4][0] = '\0'; - value->data.string = _strdup(data); + if ((errno != 0) || (val > UINT32_MAX)) + goto fail; - if (!value->data.string) + value->data.dword = (DWORD)val; + } + break; + case REG_SZ: { - free(value); - free(name); - return NULL; + size_t len, cmp; + char* end; + char* start = strchr(data, '"'); + if (!start) + goto fail; + + /* Check for terminating quote, check it is the last symbol */ + len = strlen(start); + end = strchr(start + 1, '"'); + if (!end) + goto fail; + cmp = end - start + 1; + if (len != cmp) + goto fail; + if (start[0] == '"') + start++; + if (end[0] == '"') + end[0] = '\0'; + value->data.string = _strdup(start); + + if (!value->data.string) + goto fail; } - } - else - { - WLog_ERR(TAG, "unimplemented format: %s", REG_DATA_TYPE_STRINGS[value->type]); + break; + default: + WLog_ERR(TAG, "unimplemented format: %s", reg_data_type_string(value->type)); + break; } if (!key->values) @@ -210,6 +258,11 @@ static RegVal* reg_load_value(Reg* reg, RegKey* key) } return value; + +fail: + free(value); + free(name); + return NULL; } static BOOL reg_load_has_next_line(Reg* reg) @@ -233,15 +286,21 @@ static char* reg_load_get_next_line(Reg* reg) static char* reg_load_peek_next_line(Reg* reg) { + WINPR_ASSERT(reg); return reg->next_line; } static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey) { - char* name; - char* path; - char* save; - int length; + char* name = NULL; + char* path = NULL; + char* save = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + WINPR_ASSERT(subkey); + WINPR_ASSERT(subkey->name); + path = _strdup(subkey->name); if (!path) @@ -253,9 +312,8 @@ static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey) { if (strcmp(key->name, name) == 0) { - length = strlen(name); - name += length + 1; - subkey->subname = _strdup(name); + if (save) + subkey->subname = _strdup(save); /* TODO: free allocated memory in error case */ if (!subkey->subname) @@ -274,19 +332,24 @@ static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey) static RegKey* reg_load_key(Reg* reg, RegKey* key) { char* p[2]; - int length; - char* line; + size_t length; RegKey* subkey; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + + WINPR_ASSERT(reg->line); p[0] = reg->line + 1; p[1] = strrchr(p[0], ']'); - subkey = (RegKey*)malloc(sizeof(RegKey)); + if (!p[1]) + return NULL; + + subkey = (RegKey*)calloc(1, sizeof(RegKey)); if (!subkey) return NULL; - subkey->values = NULL; - subkey->prev = subkey->next = NULL; - length = p[1] - p[0]; + length = (size_t)(p[1] - p[0]); subkey->name = (char*)malloc(length + 1); if (!subkey->name) @@ -300,7 +363,7 @@ static RegKey* reg_load_key(Reg* reg, RegKey* key) while (reg_load_has_next_line(reg)) { - line = reg_load_peek_next_line(reg); + char* line = reg_load_peek_next_line(reg); if (line[0] == '[') break; @@ -354,16 +417,16 @@ static void reg_load(Reg* reg) static void reg_unload_value(Reg* reg, RegVal* value) { - if (value->type == REG_DWORD) - { - } - else if (value->type == REG_SZ) - { - free(value->data.string); - } - else + WINPR_ASSERT(reg); + WINPR_ASSERT(value); + + switch (value->type) { - WLog_ERR(TAG, "unimplemented format: %s", REG_DATA_TYPE_STRINGS[value->type]); + case REG_SZ: + free(value->data.string); + break; + default: + break; } free(value); @@ -372,12 +435,15 @@ static void reg_unload_value(Reg* reg, RegVal* value) static void reg_unload_key(Reg* reg, RegKey* key) { RegVal* pValue; - RegVal* pValueNext; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + pValue = key->values; while (pValue != NULL) { - pValueNext = pValue->next; + RegVal* pValueNext = pValue->next; reg_unload_value(reg, pValue); pValue = pValueNext; } @@ -389,23 +455,26 @@ static void reg_unload_key(Reg* reg, RegKey* key) static void reg_unload(Reg* reg) { RegKey* pKey; - RegKey* pKeyNext; - pKey = reg->root_key->subkeys; - while (pKey != NULL) + WINPR_ASSERT(reg); + if (reg->root_key) { - pKeyNext = pKey->next; - reg_unload_key(reg, pKey); - pKey = pKeyNext; - } + pKey = reg->root_key->subkeys; + + while (pKey != NULL) + { + RegKey* pKeyNext = pKey->next; + reg_unload_key(reg, pKey); + pKey = pKeyNext; + } - free(reg->root_key); + free(reg->root_key); + } } Reg* reg_open(BOOL read_only) { - Reg* reg; - reg = (Reg*)malloc(sizeof(Reg)); + Reg* reg = (Reg*)calloc(1, sizeof(Reg)); if (!reg) return NULL; @@ -414,9 +483,7 @@ Reg* reg_open(BOOL read_only) reg->filename = WINPR_HKLM_HIVE; if (reg->read_only) - { reg->fp = winpr_fopen(reg->filename, "r"); - } else { reg->fp = winpr_fopen(reg->filename, "r+"); @@ -426,25 +493,22 @@ Reg* reg_open(BOOL read_only) } if (!reg->fp) - { - free(reg); - return NULL; - } + goto fail; - reg->root_key = (RegKey*)malloc(sizeof(RegKey)); + reg->root_key = (RegKey*)calloc(1, sizeof(RegKey)); if (!reg->root_key) - { - fclose(reg->fp); - free(reg); - return NULL; - } + goto fail; reg->root_key->values = NULL; reg->root_key->subkeys = NULL; reg->root_key->name = "HKEY_LOCAL_MACHINE"; reg_load(reg); return reg; + +fail: + reg_close(reg); + return NULL; } void reg_close(Reg* reg) @@ -452,7 +516,8 @@ void reg_close(Reg* reg) if (reg) { reg_unload(reg); - fclose(reg->fp); + if (reg->fp) + fclose(reg->fp); free(reg); } } diff --git a/winpr/libwinpr/registry/registry_reg.h b/winpr/libwinpr/registry/registry_reg.h index 90db136b42cc..ab3b34f3b411 100644 --- a/winpr/libwinpr/registry/registry_reg.h +++ b/winpr/libwinpr/registry/registry_reg.h @@ -30,7 +30,7 @@ struct _reg FILE* fp; char* line; char* next_line; - int line_length; + size_t line_length; char* buffer; char* filename; BOOL read_only; @@ -44,7 +44,8 @@ struct _reg_val RegVal* prev; RegVal* next; - union reg_data { + union reg_data + { DWORD dword; char* string; } data; diff --git a/winpr/libwinpr/smartcard/smartcard.c b/winpr/libwinpr/smartcard/smartcard.c index 22d6b03ed477..30e472ca2c73 100644 --- a/winpr/libwinpr/smartcard/smartcard.c +++ b/winpr/libwinpr/smartcard/smartcard.c @@ -1047,103 +1047,47 @@ WINSCARDAPI const char* WINAPI SCardGetCardStateString(DWORD dwCardState) WINSCARDAPI char* WINAPI SCardGetReaderStateString(DWORD dwReaderState) { - char* szReaderState = malloc(512); + const size_t size = 512; + char* buffer = calloc(size, sizeof(char)); - if (!szReaderState) + if (!buffer) return NULL; - szReaderState[0] = '\0'; - if (dwReaderState & SCARD_STATE_IGNORE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_IGNORE"); - } + winpr_str_append("SCARD_STATE_IGNORE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_CHANGED) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_CHANGED"); - } + winpr_str_append("SCARD_STATE_CHANGED", buffer, size, "|"); if (dwReaderState & SCARD_STATE_UNKNOWN) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_UNKNOWN"); - } + winpr_str_append("SCARD_STATE_UNKNOWN", buffer, size, "|"); if (dwReaderState & SCARD_STATE_UNAVAILABLE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_UNAVAILABLE"); - } + winpr_str_append("SCARD_STATE_UNAVAILABLE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_EMPTY) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_EMPTY"); - } + winpr_str_append("SCARD_STATE_EMPTY", buffer, size, "|"); if (dwReaderState & SCARD_STATE_PRESENT) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_PRESENT"); - } + winpr_str_append("SCARD_STATE_PRESENT", buffer, size, "|"); if (dwReaderState & SCARD_STATE_ATRMATCH) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_ATRMATCH"); - } + winpr_str_append("SCARD_STATE_ATRMATCH", buffer, size, "|"); if (dwReaderState & SCARD_STATE_EXCLUSIVE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_EXCLUSIVE"); - } + winpr_str_append("SCARD_STATE_EXCLUSIVE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_INUSE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_INUSE"); - } + winpr_str_append("SCARD_STATE_INUSE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_MUTE) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_MUTE"); - } + winpr_str_append("SCARD_STATE_MUTE", buffer, size, "|"); if (dwReaderState & SCARD_STATE_UNPOWERED) - { - if (szReaderState[0]) - strcat(szReaderState, " | "); - - strcat(szReaderState, "SCARD_STATE_UNPOWERED"); - } + winpr_str_append("SCARD_STATE_UNPOWERED", buffer, size, "|"); - if (!szReaderState[0]) - strcat(szReaderState, "SCARD_STATE_UNAWARE"); + if (!buffer[0]) + winpr_str_append("SCARD_STATE_UNAWARE", buffer, size, "|"); - return szReaderState; + return buffer; } diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c index 95603718dee7..0e12a3429712 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm.c @@ -22,8 +22,10 @@ #endif #include +#include #include #include +#include #include #include #include @@ -49,6 +51,8 @@ static int ntlm_SetContextWorkstation(NTLM_CONTEXT* context, char* Workstation) DWORD nSize = 0; CHAR* computerName; + WINPR_ASSERT(context); + if (!Workstation) { if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || @@ -91,6 +95,8 @@ static int ntlm_SetContextWorkstation(NTLM_CONTEXT* context, char* Workstation) static int ntlm_SetContextServicePrincipalNameW(NTLM_CONTEXT* context, LPWSTR ServicePrincipalName) { + WINPR_ASSERT(context); + if (!ServicePrincipalName) { context->ServicePrincipalName.Buffer = NULL; @@ -116,6 +122,8 @@ static int ntlm_SetContextTargetName(NTLM_CONTEXT* context, char* TargetName) DWORD nSize = 0; CHAR* computerName = NULL; + WINPR_ASSERT(context); + if (!name) { if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || @@ -254,7 +262,7 @@ static NTLM_CONTEXT* ntlm_ContextNew(void) context->NegotiateFlags = 0; context->LmCompatibilityLevel = 3; - context->state = NTLM_STATE_INITIAL; + ntlm_change_state(context, NTLM_STATE_INITIAL); FillMemory(context->MachineID, sizeof(context->MachineID), 0xAA); if (context->NTLMv2) @@ -414,79 +422,87 @@ ntlm_AcceptSecurityContext(PCredHandle phCredential, PCtxtHandle phContext, PSec sspi_SecureHandleSetUpperPointer(phNewContext, (void*)NTLM_PACKAGE_NAME); } - if (context->state == NTLM_STATE_INITIAL) + switch (ntlm_get_state(context)) { - context->state = NTLM_STATE_NEGOTIATE; - - if (!pInput) - return SEC_E_INVALID_TOKEN; - - if (pInput->cBuffers < 1) - return SEC_E_INVALID_TOKEN; - - input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + case NTLM_STATE_INITIAL: + { + ntlm_change_state(context, NTLM_STATE_NEGOTIATE); - if (!input_buffer) - return SEC_E_INVALID_TOKEN; + if (!pInput) + return SEC_E_INVALID_TOKEN; - if (input_buffer->cbBuffer < 1) - return SEC_E_INVALID_TOKEN; + if (pInput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; - status = ntlm_read_NegotiateMessage(context, input_buffer); + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); - if (context->state == NTLM_STATE_CHALLENGE) - { - if (!pOutput) + if (!input_buffer) return SEC_E_INVALID_TOKEN; - if (pOutput->cBuffers < 1) + if (input_buffer->cbBuffer < 1) return SEC_E_INVALID_TOKEN; - output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); + status = ntlm_read_NegotiateMessage(context, input_buffer); + if (status != SEC_I_CONTINUE_NEEDED) + return status; - if (!output_buffer->BufferType) - return SEC_E_INVALID_TOKEN; + if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE) + { + if (!pOutput) + return SEC_E_INVALID_TOKEN; - if (output_buffer->cbBuffer < 1) - return SEC_E_INSUFFICIENT_MEMORY; + if (pOutput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; - return ntlm_write_ChallengeMessage(context, output_buffer); - } + output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN); - return SEC_E_OUT_OF_SEQUENCE; - } - else if (context->state == NTLM_STATE_AUTHENTICATE) - { - if (!pInput) - return SEC_E_INVALID_TOKEN; + if (!output_buffer->BufferType) + return SEC_E_INVALID_TOKEN; - if (pInput->cBuffers < 1) - return SEC_E_INVALID_TOKEN; + if (output_buffer->cbBuffer < 1) + return SEC_E_INSUFFICIENT_MEMORY; - input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); + return ntlm_write_ChallengeMessage(context, output_buffer); + } - if (!input_buffer) - return SEC_E_INVALID_TOKEN; + return SEC_E_OUT_OF_SEQUENCE; + } + break; + case NTLM_STATE_AUTHENTICATE: + { + if (!pInput) + return SEC_E_INVALID_TOKEN; - if (input_buffer->cbBuffer < 1) - return SEC_E_INVALID_TOKEN; + if (pInput->cBuffers < 1) + return SEC_E_INVALID_TOKEN; - status = ntlm_read_AuthenticateMessage(context, input_buffer); + input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN); - if (pOutput) - { - ULONG i; + if (!input_buffer) + return SEC_E_INVALID_TOKEN; - for (i = 0; i < pOutput->cBuffers; i++) + if (input_buffer->cbBuffer < 1) + return SEC_E_INVALID_TOKEN; + + status = ntlm_read_AuthenticateMessage(context, input_buffer); + + if (pOutput) { - pOutput->pBuffers[i].cbBuffer = 0; - pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN; + ULONG i; + + for (i = 0; i < pOutput->cBuffers; i++) + { + pOutput->pBuffers[i].cbBuffer = 0; + pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN; + } } - } - return status; + return status; + } + break; + default: + break; } - return SEC_E_OUT_OF_SEQUENCE; } @@ -540,7 +556,7 @@ static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW( sspi_SecureHandleSetUpperPointer(phNewContext, (void*)NTLM_PACKAGE_NAME); } - if ((!pInput) || (context->state == NTLM_STATE_AUTHENTICATE)) + if ((!pInput) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)) { if (!pOutput) return SEC_E_INVALID_TOKEN; @@ -556,10 +572,10 @@ static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW( if (output_buffer->cbBuffer < 1) return SEC_E_INVALID_TOKEN; - if (context->state == NTLM_STATE_INITIAL) - context->state = NTLM_STATE_NEGOTIATE; + if (ntlm_get_state(context) == NTLM_STATE_INITIAL) + ntlm_change_state(context, NTLM_STATE_NEGOTIATE); - if (context->state == NTLM_STATE_NEGOTIATE) + if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE) return ntlm_write_NegotiateMessage(context, output_buffer); return SEC_E_OUT_OF_SEQUENCE; @@ -585,10 +601,13 @@ static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW( context->Bindings.Bindings = (SEC_CHANNEL_BINDINGS*)channel_bindings->pvBuffer; } - if (context->state == NTLM_STATE_CHALLENGE) + if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE) { status = ntlm_read_ChallengeMessage(context, input_buffer); + if (status != SEC_I_CONTINUE_NEEDED) + return status; + if (!pOutput) return SEC_E_INVALID_TOKEN; @@ -603,7 +622,7 @@ static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW( if (output_buffer->cbBuffer < 1) return SEC_E_INSUFFICIENT_MEMORY; - if (context->state == NTLM_STATE_AUTHENTICATE) + if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE) return ntlm_write_AuthenticateMessage(context, output_buffer); } @@ -672,7 +691,12 @@ static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContex SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof) { BYTE* blob; - SecBuffer* target = &ntlm->ChallengeTargetInfo; + SecBuffer* target; + + WINPR_ASSERT(ntlm); + WINPR_ASSERT(ntproof); + + target = &ntlm->ChallengeTargetInfo; if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer)) return SEC_E_INSUFFICIENT_MEMORY; @@ -693,8 +717,13 @@ SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof) SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue) { BYTE* blob; - ULONG msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer + - ntlm->AuthenticateMessage.cbBuffer; + ULONG msgSize; + + WINPR_ASSERT(ntlm); + WINPR_ASSERT(micvalue); + + msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer + + ntlm->AuthenticateMessage.cbBuffer; if (!sspi_SecBufferAlloc(micvalue, msgSize)) return SEC_E_INSUFFICIENT_MEMORY; @@ -969,8 +998,8 @@ static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext, ULON void* data; UINT32 SeqNo; UINT32 value; - BYTE digest[WINPR_MD5_DIGEST_LENGTH]; - BYTE checksum[8]; + BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + BYTE checksum[8] = { 0 }; BYTE* signature; ULONG version = 1; WINPR_HMAC_CTX* hmac; @@ -1066,12 +1095,12 @@ static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, PSec void* data; UINT32 SeqNo; UINT32 value; - BYTE digest[WINPR_MD5_DIGEST_LENGTH]; - BYTE checksum[8]; + BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + BYTE checksum[8] = { 0 }; UINT32 version = 1; WINPR_HMAC_CTX* hmac; NTLM_CONTEXT* context; - BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH]; + BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = { 0 }; PSecBuffer data_buffer = NULL; PSecBuffer signature_buffer = NULL; SeqNo = (UINT32)MessageSeqNo; @@ -1258,3 +1287,86 @@ const SecPkgInfoW NTLM_SecPkgInfoW = { NTLM_SecPkgInfoW_Name, /* Name */ NTLM_SecPkgInfoW_Comment /* Comment */ }; + +char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags) +{ + int x; + if (!buffer || (size == 0)) + return buffer; + + _snprintf(buffer, size, "[0x%08" PRIx32 "] ", flags); + + for (x = 0; x < 31; x++) + { + const UINT32 mask = 1 << x; + size_t len = strnlen(buffer, size); + if (flags & mask) + { + const char* str = ntlm_get_negotiate_string(mask); + const size_t flen = strlen(str); + + if ((len > 0) && (buffer[len - 1] != ' ')) + { + if (size - len < 1) + break; + winpr_str_append("|", buffer, size, NULL); + len++; + } + + if (size - len < flen) + break; + winpr_str_append(str, buffer, size, NULL); + } + } + + return buffer; +} + +const char* ntlm_message_type_string(UINT32 messageType) +{ + switch (messageType) + { + case MESSAGE_TYPE_NEGOTIATE: + return "MESSAGE_TYPE_NEGOTIATE"; + case MESSAGE_TYPE_CHALLENGE: + return "MESSAGE_TYPE_CHALLENGE"; + case MESSAGE_TYPE_AUTHENTICATE: + return "MESSAGE_TYPE_AUTHENTICATE"; + default: + return "MESSAGE_TYPE_UNKNOWN"; + } +} + +const char* ntlm_state_string(NTLM_STATE state) +{ + switch (state) + { + case NTLM_STATE_INITIAL: + return "NTLM_STATE_INITIAL"; + case NTLM_STATE_NEGOTIATE: + return "NTLM_STATE_NEGOTIATE"; + case NTLM_STATE_CHALLENGE: + return "NTLM_STATE_CHALLENGE"; + case NTLM_STATE_AUTHENTICATE: + return "NTLM_STATE_AUTHENTICATE"; + case NTLM_STATE_COMPLETION: + return "NTLM_STATE_COMPLETION"; + case NTLM_STATE_FINAL: + return "NTLM_STATE_FINAL"; + default: + return "NTLM_STATE_UNKNOWN"; + } +} +void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state) +{ + WINPR_ASSERT(ntlm); + WLog_DBG(TAG, "change state from %s to %s", ntlm_state_string(ntlm->state), + ntlm_state_string(state)); + ntlm->state = state; +} + +NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm) +{ + WINPR_ASSERT(ntlm); + return ntlm->state; +} diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.h b/winpr/libwinpr/sspi/NTLM/ntlm.h index c6216ea41a43..a9fe5c2b0b5b 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm.h +++ b/winpr/libwinpr/sspi/NTLM/ntlm.h @@ -89,7 +89,7 @@ enum _NTLM_AV_ID MsvAvTimestamp, MsvAvSingleHost, MsvAvTargetName, - MsvChannelBindings + MsvAvChannelBindings }; typedef enum _NTLM_AV_ID NTLM_AV_ID; @@ -176,8 +176,7 @@ typedef struct _NTLM_MESSAGE_HEADER NTLM_MESSAGE_HEADER; struct _NTLM_NEGOTIATE_MESSAGE { - BYTE Signature[8]; - UINT32 MessageType; + NTLM_MESSAGE_HEADER header; UINT32 NegotiateFlags; NTLM_VERSION_INFO Version; NTLM_MESSAGE_FIELDS DomainName; @@ -187,8 +186,7 @@ typedef struct _NTLM_NEGOTIATE_MESSAGE NTLM_NEGOTIATE_MESSAGE; struct _NTLM_CHALLENGE_MESSAGE { - BYTE Signature[8]; - UINT32 MessageType; + NTLM_MESSAGE_HEADER header; UINT32 NegotiateFlags; BYTE ServerChallenge[8]; BYTE Reserved[8]; @@ -200,8 +198,7 @@ typedef struct _NTLM_CHALLENGE_MESSAGE NTLM_CHALLENGE_MESSAGE; struct _NTLM_AUTHENTICATE_MESSAGE { - BYTE Signature[8]; - UINT32 MessageType; + NTLM_MESSAGE_HEADER header; UINT32 NegotiateFlags; NTLM_VERSION_INFO Version; NTLM_MESSAGE_FIELDS DomainName; @@ -251,7 +248,7 @@ struct _NTLM_CONTEXT NTLM_NEGOTIATE_MESSAGE NEGOTIATE_MESSAGE; NTLM_CHALLENGE_MESSAGE CHALLENGE_MESSAGE; NTLM_AUTHENTICATE_MESSAGE AUTHENTICATE_MESSAGE; - UINT32 MessageIntegrityCheckOffset; + size_t MessageIntegrityCheckOffset; SecBuffer NegotiateMessage; SecBuffer ChallengeMessage; SecBuffer AuthenticateMessage; @@ -280,6 +277,13 @@ struct _NTLM_CONTEXT }; typedef struct _NTLM_CONTEXT NTLM_CONTEXT; +char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags); +const char* ntlm_message_type_string(UINT32 messageType); + +const char* ntlm_state_string(NTLM_STATE state); +void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state); +NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm); + SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof); SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue); diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c index 47669bb2e8aa..9b2efd37a96b 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c @@ -21,12 +21,13 @@ #include "config.h" #endif -#include +#include #include "ntlm.h" #include "../sspi.h" #include +#include #include #include #include @@ -75,8 +76,8 @@ static const char* get_av_pair_string(UINT16 pair) return "MsvAvSingleHost"; case MsvAvTargetName: return "MsvAvTargetName"; - case MsvChannelBindings: - return "MsvChannelBindings"; + case MsvAvChannelBindings: + return "MsvAvChannelBindings"; default: return "UNKNOWN"; } @@ -87,11 +88,13 @@ static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPairList, size_t* pcbAvP static INLINE void ntlm_av_pair_set_id(NTLM_AV_PAIR* pAvPair, UINT16 id) { + WINPR_ASSERT(pAvPair); Data_Write_UINT16(&pAvPair->AvId, id); } static INLINE void ntlm_av_pair_set_len(NTLM_AV_PAIR* pAvPair, UINT16 len) { + WINPR_ASSERT(pAvPair); Data_Write_UINT16(&pAvPair->AvLen, len); } @@ -124,6 +127,7 @@ static INLINE BOOL ntlm_av_pair_get_id(const NTLM_AV_PAIR* pAvPair, size_t size, ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList) { + size_t size; size_t cbAvPair; NTLM_AV_PAIR* pAvPair; @@ -131,7 +135,9 @@ ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList) if (!pAvPair) return 0; - return ((PBYTE)pAvPair - (PBYTE)pAvPairList) + sizeof(NTLM_AV_PAIR); + size = ((PBYTE)pAvPair - (PBYTE)pAvPairList) + sizeof(NTLM_AV_PAIR); + WINPR_ASSERT(size <= ULONG_MAX); + return (ULONG)size; } static INLINE BOOL ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pAvLen) @@ -149,6 +155,7 @@ static INLINE BOOL ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t size return TRUE; } +#ifdef WITH_DEBUG_NTLM void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList) { UINT16 pair; @@ -158,19 +165,20 @@ void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList) if (!ntlm_av_pair_check(pAvPair, cbAvPair)) return; - WLog_INFO(TAG, "AV_PAIRs ="); + WLog_VRB(TAG, "AV_PAIRs ="); while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair) && (pair != MsvAvEOL)) { size_t cbLen = 0; ntlm_av_pair_get_len(pAvPair, cbAvPair, &cbLen); - WLog_INFO(TAG, "\t%s AvId: %" PRIu16 " AvLen: %" PRIu16 "", get_av_pair_string(pair), pair); - winpr_HexDump(TAG, WLOG_INFO, ntlm_av_pair_get_value_pointer(pAvPair), cbLen); + WLog_VRB(TAG, "\t%s AvId: %" PRIu16 " AvLen: %" PRIu16 "", get_av_pair_string(pair), pair); + winpr_HexDump(TAG, WLOG_TRACE, ntlm_av_pair_get_value_pointer(pAvPair), cbLen); pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair); } } +#endif static ULONG ntlm_av_pair_list_size(ULONG AvPairsCount, ULONG AvPairsValueLength) { @@ -180,6 +188,7 @@ static ULONG ntlm_av_pair_list_size(ULONG AvPairsCount, ULONG AvPairsValueLength PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair) { + WINPR_ASSERT(pAvPair); return (PBYTE)pAvPair + sizeof(NTLM_AV_PAIR); } @@ -259,11 +268,11 @@ static BOOL ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTL if (!pAvPair || cbAvPair < 2 * sizeof(NTLM_AV_PAIR) + AvLen) return FALSE; - ntlm_av_pair_set_id(pAvPair, AvId); + ntlm_av_pair_set_id(pAvPair, (UINT16)AvId); ntlm_av_pair_set_len(pAvPair, AvLen); if (AvLen) { - assert(Value != NULL); + WINPR_ASSERT(Value != NULL); CopyMemory(ntlm_av_pair_get_value_pointer(pAvPair), Value, AvLen); } @@ -286,8 +295,9 @@ static BOOL ntlm_av_pair_add_copy(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &avLen)) return FALSE; + WINPR_ASSERT(avLen <= UINT16_MAX); return ntlm_av_pair_add(pAvPairList, cbAvPairList, pair, - ntlm_av_pair_get_value_pointer(pAvPair), avLen); + ntlm_av_pair_get_value_pointer(pAvPair), (UINT16)avLen); } static int ntlm_get_target_computer_name(PUNICODE_STRING pName, COMPUTER_NAME_FORMAT type) @@ -297,6 +307,8 @@ static int ntlm_get_target_computer_name(PUNICODE_STRING pName, COMPUTER_NAME_FO DWORD nSize = 0; CHAR* computerName; + WINPR_ASSERT(pName); + if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || GetLastError() != ERROR_MORE_DATA) return -1; @@ -394,6 +406,9 @@ static void ntlm_compute_channel_bindings(NTLM_CONTEXT* context) BYTE* ChannelBindingToken; UINT32 ChannelBindingTokenLength; SEC_CHANNEL_BINDINGS* ChannelBindings; + + WINPR_ASSERT(context); + ZeroMemory(context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH); ChannelBindings = context->Bindings.Bindings; @@ -436,6 +451,7 @@ static void ntlm_compute_channel_bindings(NTLM_CONTEXT* context) static void ntlm_compute_single_host_data(NTLM_CONTEXT* context) { + WINPR_ASSERT(context); /** * The Single_Host_Data structure allows a client to send machine-specific information * within an authentication exchange to services on the same machine. The client can @@ -451,10 +467,10 @@ static void ntlm_compute_single_host_data(NTLM_CONTEXT* context) FillMemory(context->SingleHostData.MachineID, 32, 0xAA); } -int ntlm_construct_challenge_target_info(NTLM_CONTEXT* context) +BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context) { - int rc = -1; - int length; + BOOL rc = FALSE; + ULONG length; ULONG AvPairsCount; ULONG AvPairsLength; NTLM_AV_PAIR* pAvPairList; @@ -464,6 +480,8 @@ int ntlm_construct_challenge_target_info(NTLM_CONTEXT* context) UNICODE_STRING DnsDomainName = { 0 }; UNICODE_STRING DnsComputerName = { 0 }; + WINPR_ASSERT(context); + if (ntlm_get_target_computer_name(&NbDomainName, ComputerNameNetBIOS) < 0) goto fail; @@ -516,7 +534,7 @@ int ntlm_construct_challenge_target_info(NTLM_CONTEXT* context) sizeof(context->Timestamp))) goto fail; - rc = 1; + rc = TRUE; fail: ntlm_free_unicode_string(&NbDomainName); ntlm_free_unicode_string(&NbComputerName); @@ -525,7 +543,7 @@ int ntlm_construct_challenge_target_info(NTLM_CONTEXT* context) return rc; } -int ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context) +BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context) { ULONG size; ULONG AvPairsCount; @@ -546,6 +564,9 @@ int ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context) size_t cbAvDnsTreeName; size_t cbChallengeTargetInfo; size_t cbAuthenticateTargetInfo; + + WINPR_ASSERT(context); + AvPairsCount = 1; AvPairsValueLength = 0; ChallengeTargetInfo = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer; @@ -635,7 +656,7 @@ int ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context) * SEC_CHANNEL_BINDINGS structure * http://msdn.microsoft.com/en-us/library/windows/desktop/dd919963/ */ - AvPairsCount++; /* MsvChannelBindings */ + AvPairsCount++; /* MsvAvChannelBindings */ AvPairsValueLength += 16; ntlm_compute_channel_bindings(context); @@ -714,15 +735,17 @@ int ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context) if (context->SendSingleHostData) { + WINPR_ASSERT(context->SingleHostData.Size <= UINT16_MAX); if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvSingleHost, - (PBYTE)&context->SingleHostData, context->SingleHostData.Size)) + (PBYTE)&context->SingleHostData, + (UINT16)context->SingleHostData.Size)) goto fail; } if (!context->SuppressExtendedProtection) { - if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvChannelBindings, - context->ChannelBindingsHash, 16)) + if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, + MsvAvChannelBindings, context->ChannelBindingsHash, 16)) goto fail; if (context->ServicePrincipalName.Length > 0) @@ -745,8 +768,8 @@ int ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context) ZeroMemory(AvEOL, sizeof(NTLM_AV_PAIR)); } - return 1; + return TRUE; fail: sspi_SecBufferFree(&context->AuthenticateTargetInfo); - return -1; + return FALSE; } diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h index ac228a86972e..82d2364b215a 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h +++ b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h @@ -30,7 +30,7 @@ PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair); NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId, size_t* pcbAvPairListRemaining); -int ntlm_construct_challenge_target_info(NTLM_CONTEXT* context); -int ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context); +BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context); +BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context); #endif /* WINPR_SSPI_NTLM_AV_PAIRS_H */ diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c index dbd7f7fb0e06..4e33aedec0e9 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c @@ -21,7 +21,7 @@ #include "config.h" #endif -#include +#include #include "ntlm.h" #include "../sspi.h" @@ -38,16 +38,10 @@ #include "../../log.h" #define TAG WINPR_TAG("sspi.NTLM") -const char LM_MAGIC[] = "KGS!@#$%"; - -static const char NTLM_CLIENT_SIGN_MAGIC[] = - "session key to client-to-server signing key magic constant"; -static const char NTLM_SERVER_SIGN_MAGIC[] = - "session key to server-to-client signing key magic constant"; -static const char NTLM_CLIENT_SEAL_MAGIC[] = - "session key to client-to-server sealing key magic constant"; -static const char NTLM_SERVER_SEAL_MAGIC[] = - "session key to server-to-client sealing key magic constant"; +static char NTLM_CLIENT_SIGN_MAGIC[] = "session key to client-to-server signing key magic constant"; +static char NTLM_SERVER_SIGN_MAGIC[] = "session key to server-to-client signing key magic constant"; +static char NTLM_CLIENT_SEAL_MAGIC[] = "session key to client-to-server sealing key magic constant"; +static char NTLM_SERVER_SEAL_MAGIC[] = "session key to server-to-client sealing key magic constant"; static const BYTE NTLM_NULL_BUFFER[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -58,16 +52,21 @@ static const BYTE NTLM_NULL_BUFFER[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 * @param s */ -void ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo) +BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo) { - OSVERSIONINFOA osVersionInfo; + OSVERSIONINFOA osVersionInfo = { 0 }; + + WINPR_ASSERT(versionInfo); + osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); - GetVersionExA(&osVersionInfo); + if (!GetVersionExA(&osVersionInfo)) + return FALSE; versionInfo->ProductMajorVersion = (UINT8)osVersionInfo.dwMajorVersion; versionInfo->ProductMinorVersion = (UINT8)osVersionInfo.dwMinorVersion; versionInfo->ProductBuild = (UINT16)osVersionInfo.dwBuildNumber; ZeroMemory(versionInfo->Reserved, sizeof(versionInfo->Reserved)); versionInfo->NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3; + return TRUE; } /** @@ -76,17 +75,24 @@ void ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo) * @param s */ -int ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo) +BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo) { + WINPR_ASSERT(s); + WINPR_ASSERT(versionInfo); + if (Stream_GetRemainingLength(s) < 8) - return -1; + { + WLog_ERR(TAG, "NTLM_VERSION_INFO short header %" PRIuz ", expected %" PRIuz, + Stream_GetRemainingLength(s), 8); + return FALSE; + } Stream_Read_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */ Stream_Read_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */ Stream_Read_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */ Stream_Read(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */ Stream_Read_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */ - return 1; + return TRUE; } /** @@ -95,13 +101,24 @@ int ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo) * @param s */ -void ntlm_write_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo) +BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo) { + WINPR_ASSERT(s); + WINPR_ASSERT(versionInfo); + + if (Stream_GetRemainingCapacity(s) < 5 + sizeof(versionInfo->Reserved)) + { + WLog_ERR(TAG, "NTLM_VERSION_INFO short header %" PRIuz ", expected %" PRIuz, + Stream_GetRemainingCapacity(s), 5 + sizeof(versionInfo->Reserved)); + return FALSE; + } + Stream_Write_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */ Stream_Write_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */ Stream_Write_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */ Stream_Write(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */ Stream_Write_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */ + return TRUE; } /** @@ -109,23 +126,33 @@ void ntlm_write_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo) * VERSION @msdn{cc236654} * @param s */ - -void ntlm_print_version_info(NTLM_VERSION_INFO* versionInfo) +#ifdef WITH_DEBUG_NTLM +void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo) { - WLog_INFO(TAG, "VERSION ={"); - WLog_INFO(TAG, "\tProductMajorVersion: %" PRIu8 "", versionInfo->ProductMajorVersion); - WLog_INFO(TAG, "\tProductMinorVersion: %" PRIu8 "", versionInfo->ProductMinorVersion); - WLog_INFO(TAG, "\tProductBuild: %" PRIu16 "", versionInfo->ProductBuild); - WLog_INFO(TAG, "\tReserved: 0x%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "", versionInfo->Reserved[0], - versionInfo->Reserved[1], versionInfo->Reserved[2]); - WLog_INFO(TAG, "\tNTLMRevisionCurrent: 0x%02" PRIX8 "", versionInfo->NTLMRevisionCurrent); + WINPR_ASSERT(versionInfo); + + WLog_VRB(TAG, "VERSION ={"); + WLog_VRB(TAG, "\tProductMajorVersion: %" PRIu8 "", versionInfo->ProductMajorVersion); + WLog_VRB(TAG, "\tProductMinorVersion: %" PRIu8 "", versionInfo->ProductMinorVersion); + WLog_VRB(TAG, "\tProductBuild: %" PRIu16 "", versionInfo->ProductBuild); + WLog_VRB(TAG, "\tReserved: 0x%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "", versionInfo->Reserved[0], + versionInfo->Reserved[1], versionInfo->Reserved[2]); + WLog_VRB(TAG, "\tNTLMRevisionCurrent: 0x%02" PRIX8 "", versionInfo->NTLMRevisionCurrent); } +#endif -static int ntlm_read_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge) +static BOOL ntlm_read_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge) { size_t size; + WINPR_ASSERT(s); + WINPR_ASSERT(challenge); + if (Stream_GetRemainingLength(s) < 28) - return -1; + { + WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE expected 28bytes, got %" PRIuz "bytes", + Stream_GetRemainingLength(s)); + return FALSE; + } Stream_Read_UINT8(s, challenge->RespType); Stream_Read_UINT8(s, challenge->HiRespType); @@ -137,21 +164,39 @@ static int ntlm_read_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENG size = Stream_Length(s) - Stream_GetPosition(s); if (size > UINT32_MAX) - return -1; + { + WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::cbAvPairs too large, got %" PRIuz "bytes", size); + return FALSE; + } - challenge->cbAvPairs = size; + challenge->cbAvPairs = (UINT32)size; challenge->AvPairs = (NTLM_AV_PAIR*)malloc(challenge->cbAvPairs); if (!challenge->AvPairs) - return -1; + { + WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::AvPairs failed to allocate %" PRIu32 "bytes", + challenge->cbAvPairs); + return FALSE; + } Stream_Read(s, challenge->AvPairs, size); - return 1; + return TRUE; } -static int ntlm_write_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge) +static BOOL ntlm_write_ntlm_v2_client_challenge(wStream* s, + const NTLMv2_CLIENT_CHALLENGE* challenge) { ULONG length; + + WINPR_ASSERT(s); + WINPR_ASSERT(challenge); + + if (Stream_GetRemainingCapacity(s) < 28) + { + WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE expected 28bytes, have %" PRIuz "bytes", + Stream_GetRemainingCapacity(s)); + return FALSE; + } Stream_Write_UINT8(s, challenge->RespType); Stream_Write_UINT8(s, challenge->HiRespType); Stream_Write_UINT16(s, challenge->Reserved1); @@ -160,20 +205,44 @@ static int ntlm_write_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLEN Stream_Write(s, challenge->ClientChallenge, 8); Stream_Write_UINT32(s, challenge->Reserved3); length = ntlm_av_pair_list_length(challenge->AvPairs, challenge->cbAvPairs); + if (Stream_GetRemainingCapacity(s) < length) + { + WLog_ERR(TAG, + "NTLMv2_CLIENT_CHALLENGE::AvPairs expected %" PRIu32 "bytes, have %" PRIuz "bytes", + length, Stream_GetRemainingCapacity(s)); + return FALSE; + } Stream_Write(s, challenge->AvPairs, length); - return 1; + return TRUE; } -int ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response) +BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response) { + WINPR_ASSERT(s); + WINPR_ASSERT(response); + if (Stream_GetRemainingLength(s) < 16) - return -1; + { + WLog_ERR(TAG, "NTLMv2_RESPONSE expected 16bytes, have %" PRIuz "bytes", + Stream_GetRemainingLength(s)); + + return FALSE; + } Stream_Read(s, response->Response, 16); return ntlm_read_ntlm_v2_client_challenge(s, &(response->Challenge)); } -int ntlm_write_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response) +BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response) { + WINPR_ASSERT(s); + WINPR_ASSERT(response); + + if (Stream_GetRemainingCapacity(s) < 16) + { + WLog_ERR(TAG, "NTLMv2_RESPONSE expected 16bytes, have %" PRIuz "bytes", + Stream_GetRemainingCapacity(s)); + return FALSE; + } Stream_Write(s, response->Response, 16); return ntlm_write_ntlm_v2_client_challenge(s, &(response->Challenge)); } @@ -185,8 +254,11 @@ int ntlm_write_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response) void ntlm_current_time(BYTE* timestamp) { - FILETIME filetime; - ULARGE_INTEGER time64; + FILETIME filetime = { 0 }; + ULARGE_INTEGER time64 = { 0 }; + + WINPR_ASSERT(timestamp); + GetSystemTimeAsFileTime(&filetime); time64.u.LowPart = filetime.dwLowDateTime; time64.u.HighPart = filetime.dwHighDateTime; @@ -200,127 +272,137 @@ void ntlm_current_time(BYTE* timestamp) void ntlm_generate_timestamp(NTLM_CONTEXT* context) { + WINPR_ASSERT(context); + if (memcmp(context->ChallengeTimestamp, NTLM_NULL_BUFFER, 8) != 0) CopyMemory(context->Timestamp, context->ChallengeTimestamp, 8); else ntlm_current_time(context->Timestamp); } -static int ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) +static BOOL ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) { - WINPR_SAM* sam; - WINPR_SAM_ENTRY* entry; - SSPI_CREDENTIALS* credentials = context->credentials; + BOOL rc = FALSE; + WINPR_SAM* sam = NULL; + WINPR_SAM_ENTRY* entry = NULL; + SSPI_CREDENTIALS* credentials; + + WINPR_ASSERT(context); + WINPR_ASSERT(hash); + + credentials = context->credentials; sam = SamOpen(context->SamFile, TRUE); if (!sam) - return -1; + goto fail; entry = SamLookupUserW( - sam, (LPWSTR)credentials->identity.User, credentials->identity.UserLength * 2, - (LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * 2); + sam, (LPWSTR)credentials->identity.User, credentials->identity.UserLength * sizeof(WCHAR), + (LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * sizeof(WCHAR)); - if (entry) + if (!entry) { -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "NTLM Hash:"); - winpr_HexDump(TAG, WLOG_DEBUG, entry->NtHash, 16); -#endif - NTOWFv2FromHashW(entry->NtHash, (LPWSTR)credentials->identity.User, - credentials->identity.UserLength * 2, (LPWSTR)credentials->identity.Domain, - credentials->identity.DomainLength * 2, (BYTE*)hash); - SamFreeEntry(sam, entry); - SamClose(sam); - return 1; + entry = SamLookupUserW(sam, (LPWSTR)credentials->identity.User, + credentials->identity.UserLength * sizeof(WCHAR), NULL, 0); } - entry = SamLookupUserW(sam, (LPWSTR)credentials->identity.User, - credentials->identity.UserLength * 2, NULL, 0); + if (!entry) + goto fail; - if (entry) - { #ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "NTLM Hash:"); + WLog_VRB(TAG, "NTLM Hash:"); winpr_HexDump(TAG, WLOG_DEBUG, entry->NtHash, 16); #endif - NTOWFv2FromHashW(entry->NtHash, (LPWSTR)credentials->identity.User, - credentials->identity.UserLength * 2, (LPWSTR)credentials->identity.Domain, - credentials->identity.DomainLength * 2, (BYTE*)hash); - SamFreeEntry(sam, entry); - SamClose(sam); - return 1; - } - else - { - SamClose(sam); - WLog_ERR(TAG, "Error: Could not find user in SAM database"); - return 0; - } + NTOWFv2FromHashW(entry->NtHash, (LPWSTR)credentials->identity.User, + credentials->identity.UserLength * sizeof(WCHAR), + (LPWSTR)credentials->identity.Domain, + credentials->identity.DomainLength * sizeof(WCHAR), (BYTE*)hash); + + rc = TRUE; +fail: + SamFreeEntry(sam, entry); SamClose(sam); - return 1; + if (!rc) + WLog_ERR(TAG, "Error: Could not find user in SAM database"); + + return rc; } static int ntlm_convert_password_hash(NTLM_CONTEXT* context, BYTE* hash) { int status; - int i, hn, ln; + int i; char* PasswordHash = NULL; - UINT32 PasswordHashLength = 0; - SSPI_CREDENTIALS* credentials = context->credentials; + INT64 PasswordHashLength = 0; + SSPI_CREDENTIALS* credentials; + + WINPR_ASSERT(context); + WINPR_ASSERT(hash); + + credentials = context->credentials; /* Password contains a password hash of length (PasswordLength - * SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) */ PasswordHashLength = credentials->identity.PasswordLength - SSPI_CREDENTIALS_HASH_LENGTH_OFFSET; + WINPR_ASSERT(PasswordHashLength >= 0); + WINPR_ASSERT(PasswordHashLength <= INT_MAX); status = ConvertFromUnicode(CP_UTF8, 0, (LPCWSTR)credentials->identity.Password, - PasswordHashLength, &PasswordHash, 0, NULL, NULL); + (int)PasswordHashLength, &PasswordHash, 0, NULL, NULL); if (status <= 0) return -1; - CharUpperBuffA(PasswordHash, PasswordHashLength); + CharUpperBuffA(PasswordHash, (DWORD)PasswordHashLength); for (i = 0; i < 32; i += 2) { - hn = PasswordHash[i] > '9' ? PasswordHash[i] - 'A' + 10 : PasswordHash[i] - '0'; - ln = PasswordHash[i + 1] > '9' ? PasswordHash[i + 1] - 'A' + 10 : PasswordHash[i + 1] - '0'; - hash[i / 2] = (hn << 4) | ln; + BYTE hn = + (BYTE)(PasswordHash[i] > '9' ? PasswordHash[i] - 'A' + 10 : PasswordHash[i] - '0'); + BYTE ln = (BYTE)(PasswordHash[i + 1] > '9' ? PasswordHash[i + 1] - 'A' + 10 + : PasswordHash[i + 1] - '0'); + hash[i / 2] = (BYTE)((hn << 4) | ln); } free(PasswordHash); return 1; } -static int ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) +static BOOL ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) { - SSPI_CREDENTIALS* credentials = context->credentials; + SSPI_CREDENTIALS* credentials; + + WINPR_ASSERT(context); + WINPR_ASSERT(hash); + + credentials = context->credentials; #ifdef WITH_DEBUG_NTLM if (credentials) { - WLog_DBG(TAG, "Password (length = %" PRIu32 ")", credentials->identity.PasswordLength * 2); - winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*)credentials->identity.Password, + WLog_VRB(TAG, "Password (length = %" PRIu32 ")", credentials->identity.PasswordLength * 2); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Password, credentials->identity.PasswordLength * 2); - WLog_DBG(TAG, "Username (length = %" PRIu32 ")", credentials->identity.UserLength * 2); - winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*)credentials->identity.User, + WLog_VRB(TAG, "Username (length = %" PRIu32 ")", credentials->identity.UserLength * 2); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.User, credentials->identity.UserLength * 2); - WLog_DBG(TAG, "Domain (length = %" PRIu32 ")", credentials->identity.DomainLength * 2); - winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*)credentials->identity.Domain, + WLog_VRB(TAG, "Domain (length = %" PRIu32 ")", credentials->identity.DomainLength * 2); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Domain, credentials->identity.DomainLength * 2); } else - WLog_DBG(TAG, "Strange, NTLM_CONTEXT is missing valid credentials..."); + WLog_VRB(TAG, "Strange, NTLM_CONTEXT is missing valid credentials..."); - WLog_DBG(TAG, "Workstation (length = %" PRIu16 ")", context->Workstation.Length); - winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*)context->Workstation.Buffer, context->Workstation.Length); - WLog_DBG(TAG, "NTOWFv2, NTLMv2 Hash"); - winpr_HexDump(TAG, WLOG_DEBUG, context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH); + WLog_VRB(TAG, "Workstation (length = %" PRIu16 ")", context->Workstation.Length); + winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)context->Workstation.Buffer, context->Workstation.Length); + WLog_VRB(TAG, "NTOWFv2, NTLMv2 Hash"); + winpr_HexDump(TAG, WLOG_TRACE, context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH); #endif if (memcmp(context->NtlmV2Hash, NTLM_NULL_BUFFER, 16) != 0) - return 1; + return TRUE; if (!credentials) - return -1; + return FALSE; else if (memcmp(context->NtlmHash, NTLM_NULL_BUFFER, 16) != 0) { NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User, @@ -331,7 +413,7 @@ static int ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) { /* Special case for WinPR: password hash */ if (ntlm_convert_password_hash(context, context->NtlmHash) < 0) - return -1; + return FALSE; NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User, credentials->identity.UserLength * 2, (LPWSTR)credentials->identity.Domain, @@ -350,55 +432,57 @@ static int ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash) SecBuffer proofValue, micValue; if (ntlm_computeProofValue(context, &proofValue) != SEC_E_OK) - return -1; + return FALSE; if (ntlm_computeMicValue(context, &micValue) != SEC_E_OK) { sspi_SecBufferFree(&proofValue); - return -1; + return FALSE; } ret = context->HashCallback(context->HashCallbackArg, &credentials->identity, &proofValue, context->EncryptedRandomSessionKey, - (&context->AUTHENTICATE_MESSAGE)->MessageIntegrityCheck, - &micValue, hash); + context->AUTHENTICATE_MESSAGE.MessageIntegrityCheck, &micValue, + hash); sspi_SecBufferFree(&proofValue); sspi_SecBufferFree(&micValue); - return ret ? 1 : -1; + return ret ? TRUE : FALSE; } else if (context->UseSamFileDatabase) { return ntlm_fetch_ntlm_v2_hash(context, hash); } - return 1; + return TRUE; } -int ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) +BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) { BYTE* response; - BYTE value[WINPR_MD5_DIGEST_LENGTH]; + BYTE value[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + + WINPR_ASSERT(context); if (context->LmCompatibilityLevel < 2) { if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24)) - return -1; + return FALSE; ZeroMemory(context->LmChallengeResponse.pvBuffer, 24); - return 1; + return TRUE; } /* Compute the NTLMv2 hash */ - if (ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash) < 0) - return -1; + if (!ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash)) + return FALSE; /* Concatenate the server and client challenges */ CopyMemory(value, context->ServerChallenge, 8); CopyMemory(&value[8], context->ClientChallenge, 8); if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24)) - return -1; + return FALSE; response = (BYTE*)context->LmChallengeResponse.pvBuffer; /* Compute the HMAC-MD5 hash of the resulting value using the NTLMv2 hash as the key */ @@ -407,7 +491,7 @@ int ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) /* Concatenate the resulting HMAC-MD5 hash and the client challenge, giving us the LMv2 response * (24 bytes) */ CopyMemory(&response[16], context->ClientChallenge, 8); - return 1; + return TRUE; } /** @@ -417,13 +501,17 @@ int ntlm_compute_lm_v2_response(NTLM_CONTEXT* context) * @param NTLM context */ -int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) +BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) { BYTE* blob; SecBuffer ntlm_v2_temp = { 0 }; SecBuffer ntlm_v2_temp_chal = { 0 }; - PSecBuffer TargetInfo = &context->ChallengeTargetInfo; - int ret = -1; + PSecBuffer TargetInfo; + + WINPR_ASSERT(context); + + TargetInfo = &context->ChallengeTargetInfo; + BOOL ret = FALSE; if (!sspi_SecBufferAlloc(&ntlm_v2_temp, TargetInfo->cbBuffer + 28)) goto exit; @@ -432,7 +520,7 @@ int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) blob = (BYTE*)ntlm_v2_temp.pvBuffer; /* Compute the NTLMv2 hash */ - if (ntlm_compute_ntlm_v2_hash(context, (BYTE*)context->NtlmV2Hash) < 0) + if (!ntlm_compute_ntlm_v2_hash(context, (BYTE*)context->NtlmV2Hash)) goto exit; /* Construct temp */ @@ -445,8 +533,8 @@ int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) /* Reserved3 (4 bytes) */ CopyMemory(&blob[28], TargetInfo->pvBuffer, TargetInfo->cbBuffer); #ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "NTLMv2 Response Temp Blob"); - winpr_HexDump(TAG, WLOG_DEBUG, ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer); + WLog_VRB(TAG, "NTLMv2 Response Temp Blob"); + winpr_HexDump(TAG, WLOG_TRACE, ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer); #endif /* Concatenate server challenge with temp */ @@ -473,7 +561,7 @@ int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH, context->NtProofString, WINPR_MD5_DIGEST_LENGTH, context->SessionBaseKey, WINPR_MD5_DIGEST_LENGTH); - ret = 1; + ret = TRUE; exit: sspi_SecBufferFree(&ntlm_v2_temp); sspi_SecBufferFree(&ntlm_v2_temp_chal); @@ -488,7 +576,7 @@ int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context) * @param ciphertext cipher text */ -void ntlm_rc4k(BYTE* key, int length, BYTE* plaintext, BYTE* ciphertext) +void ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext) { WINPR_RC4_CTX* rc4 = winpr_RC4_New(key, 16); @@ -506,9 +594,11 @@ void ntlm_rc4k(BYTE* key, int length, BYTE* plaintext, BYTE* ciphertext) void ntlm_generate_client_challenge(NTLM_CONTEXT* context) { + WINPR_ASSERT(context); + /* ClientChallenge is used in computation of LMv2 and NTLMv2 responses */ - if (memcmp(context->ClientChallenge, NTLM_NULL_BUFFER, 8) == 0) - winpr_RAND(context->ClientChallenge, 8); + if (memcmp(context->ClientChallenge, NTLM_NULL_BUFFER, sizeof(context->ClientChallenge)) == 0) + winpr_RAND(context->ClientChallenge, sizeof(context->ClientChallenge)); } /** @@ -518,8 +608,10 @@ void ntlm_generate_client_challenge(NTLM_CONTEXT* context) void ntlm_generate_server_challenge(NTLM_CONTEXT* context) { - if (memcmp(context->ServerChallenge, NTLM_NULL_BUFFER, 8) == 0) - winpr_RAND(context->ServerChallenge, 8); + WINPR_ASSERT(context); + + if (memcmp(context->ServerChallenge, NTLM_NULL_BUFFER, sizeof(context->ServerChallenge)) == 0) + winpr_RAND(context->ServerChallenge, sizeof(context->ServerChallenge)); } /** @@ -530,8 +622,11 @@ void ntlm_generate_server_challenge(NTLM_CONTEXT* context) void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context) { + WINPR_ASSERT(context); + WINPR_ASSERT(sizeof(context->KeyExchangeKey) == sizeof(context->SessionBaseKey)); + /* In NTLMv2, KeyExchangeKey is the 128-bit SessionBaseKey */ - CopyMemory(context->KeyExchangeKey, context->SessionBaseKey, 16); + CopyMemory(context->KeyExchangeKey, context->SessionBaseKey, sizeof(context->KeyExchangeKey)); } /** @@ -541,7 +636,8 @@ void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context) void ntlm_generate_random_session_key(NTLM_CONTEXT* context) { - winpr_RAND(context->RandomSessionKey, 16); + WINPR_ASSERT(context); + winpr_RAND(context->RandomSessionKey, sizeof(context->RandomSessionKey)); } /** @@ -551,7 +647,10 @@ void ntlm_generate_random_session_key(NTLM_CONTEXT* context) void ntlm_generate_exported_session_key(NTLM_CONTEXT* context) { - CopyMemory(context->ExportedSessionKey, context->RandomSessionKey, 16); + WINPR_ASSERT(context); + + CopyMemory(context->ExportedSessionKey, context->RandomSessionKey, + sizeof(context->ExportedSessionKey)); } /** @@ -563,6 +662,7 @@ void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context) { /* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the * KeyExchangeKey */ + WINPR_ASSERT(context); ntlm_rc4k(context->KeyExchangeKey, 16, context->RandomSessionKey, context->EncryptedRandomSessionKey); } @@ -574,6 +674,8 @@ void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context) void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context) { + WINPR_ASSERT(context); + /* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the * KeyExchangeKey */ @@ -583,10 +685,18 @@ void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context) * AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey) else Set RandomSessionKey to KeyExchangeKey */ if (context->NegotiateKeyExchange) - ntlm_rc4k(context->KeyExchangeKey, 16, context->EncryptedRandomSessionKey, - context->RandomSessionKey); + { + WINPR_ASSERT(sizeof(context->EncryptedRandomSessionKey) == + sizeof(context->RandomSessionKey)); + ntlm_rc4k(context->KeyExchangeKey, sizeof(context->EncryptedRandomSessionKey), + context->EncryptedRandomSessionKey, context->RandomSessionKey); + } else - CopyMemory(context->RandomSessionKey, context->KeyExchangeKey, 16); + { + WINPR_ASSERT(sizeof(context->RandomSessionKey) == sizeof(context->KeyExchangeKey)); + CopyMemory(context->RandomSessionKey, context->KeyExchangeKey, + sizeof(context->RandomSessionKey)); + } } /** @@ -597,29 +707,32 @@ void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context) * @param signing_key Destination signing key */ -static int ntlm_generate_signing_key(BYTE* exported_session_key, PSecBuffer sign_magic, - BYTE* signing_key) +static BOOL ntlm_generate_signing_key(BYTE* exported_session_key, const SecBuffer* sign_magic, + BYTE* signing_key) { - int length; - BYTE* value; + BOOL rc = FALSE; + size_t length; + BYTE* value = NULL; + + WINPR_ASSERT(exported_session_key); + WINPR_ASSERT(sign_magic); + WINPR_ASSERT(signing_key); + length = WINPR_MD5_DIGEST_LENGTH + sign_magic->cbBuffer; value = (BYTE*)malloc(length); if (!value) - return -1; + goto out; /* Concatenate ExportedSessionKey with sign magic */ CopyMemory(value, exported_session_key, WINPR_MD5_DIGEST_LENGTH); CopyMemory(&value[WINPR_MD5_DIGEST_LENGTH], sign_magic->pvBuffer, sign_magic->cbBuffer); - if (!winpr_Digest(WINPR_MD_MD5, value, length, signing_key, WINPR_MD5_DIGEST_LENGTH)) - { - free(value); - return -1; - } + rc = winpr_Digest(WINPR_MD_MD5, value, length, signing_key, WINPR_MD5_DIGEST_LENGTH); +out: free(value); - return 1; + return rc; } /** @@ -628,12 +741,13 @@ static int ntlm_generate_signing_key(BYTE* exported_session_key, PSecBuffer sign * @param NTLM context */ -void ntlm_generate_client_signing_key(NTLM_CONTEXT* context) +BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context) { - SecBuffer signMagic; - signMagic.pvBuffer = (void*)NTLM_CLIENT_SIGN_MAGIC; - signMagic.cbBuffer = sizeof(NTLM_CLIENT_SIGN_MAGIC); - ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, context->ClientSigningKey); + const SecBuffer signMagic = { sizeof(NTLM_CLIENT_SIGN_MAGIC), 0, NTLM_CLIENT_SIGN_MAGIC }; + + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, + context->ClientSigningKey); } /** @@ -642,45 +756,13 @@ void ntlm_generate_client_signing_key(NTLM_CONTEXT* context) * @param NTLM context */ -void ntlm_generate_server_signing_key(NTLM_CONTEXT* context) +BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context) { - SecBuffer signMagic; - signMagic.pvBuffer = (void*)NTLM_SERVER_SIGN_MAGIC; - signMagic.cbBuffer = sizeof(NTLM_SERVER_SIGN_MAGIC); - ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, context->ServerSigningKey); -} - -/** - * Generate sealing key. - * @msdn{cc236712} - * @param exported_session_key ExportedSessionKey - * @param seal_magic Seal magic string - * @param sealing_key Destination sealing key - */ - -static int ntlm_generate_sealing_key(BYTE* exported_session_key, PSecBuffer seal_magic, - BYTE* sealing_key) -{ - BYTE* p; - SecBuffer buffer; - - if (!sspi_SecBufferAlloc(&buffer, WINPR_MD5_DIGEST_LENGTH + seal_magic->cbBuffer)) - return -1; + const SecBuffer signMagic = { sizeof(NTLM_SERVER_SIGN_MAGIC), 0, NTLM_SERVER_SIGN_MAGIC }; - p = (BYTE*)buffer.pvBuffer; - /* Concatenate ExportedSessionKey with seal magic */ - CopyMemory(p, exported_session_key, WINPR_MD5_DIGEST_LENGTH); - CopyMemory(&p[WINPR_MD5_DIGEST_LENGTH], seal_magic->pvBuffer, seal_magic->cbBuffer); - - if (!winpr_Digest(WINPR_MD_MD5, buffer.pvBuffer, buffer.cbBuffer, sealing_key, - WINPR_MD5_DIGEST_LENGTH)) - { - sspi_SecBufferFree(&buffer); - return -1; - } - - sspi_SecBufferFree(&buffer); - return 1; + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, + context->ServerSigningKey); } /** @@ -689,12 +771,13 @@ static int ntlm_generate_sealing_key(BYTE* exported_session_key, PSecBuffer seal * @param NTLM context */ -void ntlm_generate_client_sealing_key(NTLM_CONTEXT* context) +BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context) { - SecBuffer sealMagic; - sealMagic.pvBuffer = (void*)NTLM_CLIENT_SEAL_MAGIC; - sealMagic.cbBuffer = sizeof(NTLM_CLIENT_SEAL_MAGIC); - ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, context->ClientSealingKey); + const SecBuffer sealMagic = { sizeof(NTLM_CLIENT_SEAL_MAGIC), 0, NTLM_CLIENT_SEAL_MAGIC }; + + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, + context->ClientSealingKey); } /** @@ -703,12 +786,13 @@ void ntlm_generate_client_sealing_key(NTLM_CONTEXT* context) * @param NTLM context */ -void ntlm_generate_server_sealing_key(NTLM_CONTEXT* context) +BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context) { - SecBuffer sealMagic; - sealMagic.pvBuffer = (void*)NTLM_SERVER_SEAL_MAGIC; - sealMagic.cbBuffer = sizeof(NTLM_SERVER_SEAL_MAGIC); - ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, context->ServerSealingKey); + const SecBuffer sealMagic = { sizeof(NTLM_SERVER_SEAL_MAGIC), 0, NTLM_SERVER_SEAL_MAGIC }; + + WINPR_ASSERT(context); + return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, + context->ServerSealingKey); } /** @@ -718,14 +802,17 @@ void ntlm_generate_server_sealing_key(NTLM_CONTEXT* context) void ntlm_init_rc4_seal_states(NTLM_CONTEXT* context) { + WINPR_ASSERT(context); if (context->server) { context->SendSigningKey = context->ServerSigningKey; context->RecvSigningKey = context->ClientSigningKey; context->SendSealingKey = context->ClientSealingKey; context->RecvSealingKey = context->ServerSealingKey; - context->SendRc4Seal = winpr_RC4_New(context->ServerSealingKey, 16); - context->RecvRc4Seal = winpr_RC4_New(context->ClientSealingKey, 16); + context->SendRc4Seal = + winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey)); + context->RecvRc4Seal = + winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey)); } else { @@ -733,22 +820,29 @@ void ntlm_init_rc4_seal_states(NTLM_CONTEXT* context) context->RecvSigningKey = context->ServerSigningKey; context->SendSealingKey = context->ServerSealingKey; context->RecvSealingKey = context->ClientSealingKey; - context->SendRc4Seal = winpr_RC4_New(context->ClientSealingKey, 16); - context->RecvRc4Seal = winpr_RC4_New(context->ServerSealingKey, 16); + context->SendRc4Seal = + winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey)); + context->RecvRc4Seal = + winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey)); } } -void ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size) +BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size) { + BOOL rc = FALSE; /* * Compute the HMAC-MD5 hash of ConcatenationOf(NEGOTIATE_MESSAGE, * CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE) using the ExportedSessionKey */ WINPR_HMAC_CTX* hmac = winpr_HMAC_New(); - assert(size >= WINPR_MD5_DIGEST_LENGTH); + WINPR_ASSERT(context); + WINPR_ASSERT(mic); + WINPR_ASSERT(size >= WINPR_MD5_DIGEST_LENGTH); + + memset(mic, 0, size); if (!hmac) - return; + return FALSE; if (winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->ExportedSessionKey, WINPR_MD5_DIGEST_LENGTH)) { @@ -756,10 +850,27 @@ void ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT context->NegotiateMessage.cbBuffer); winpr_HMAC_Update(hmac, (BYTE*)context->ChallengeMessage.pvBuffer, context->ChallengeMessage.cbBuffer); - winpr_HMAC_Update(hmac, (BYTE*)context->AuthenticateMessage.pvBuffer, - context->AuthenticateMessage.cbBuffer); + + if (context->MessageIntegrityCheckOffset > 0) + { + const BYTE* auth = (BYTE*)context->AuthenticateMessage.pvBuffer; + const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + const size_t rest = context->MessageIntegrityCheckOffset + sizeof(data); + + WINPR_ASSERT(rest <= context->AuthenticateMessage.cbBuffer); + winpr_HMAC_Update(hmac, &auth[0], context->MessageIntegrityCheckOffset); + winpr_HMAC_Update(hmac, data, sizeof(data)); + winpr_HMAC_Update(hmac, &auth[rest], context->AuthenticateMessage.cbBuffer - rest); + } + else + { + winpr_HMAC_Update(hmac, (BYTE*)context->AuthenticateMessage.pvBuffer, + context->AuthenticateMessage.cbBuffer); + } winpr_HMAC_Final(hmac, mic, WINPR_MD5_DIGEST_LENGTH); + rc = TRUE; } winpr_HMAC_Free(hmac); + return rc; } diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.h b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h index 47f6c5dc84b4..a5572b504fa2 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_compute.h +++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h @@ -24,13 +24,16 @@ #include "ntlm_av_pairs.h" -void ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo); -int ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo); -void ntlm_write_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo); -void ntlm_print_version_info(NTLM_VERSION_INFO* versionInfo); +BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo); +BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo); +BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo); -int ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response); -int ntlm_write_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response); +#ifdef WITH_DEBUG_NTLM +void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo); +#endif + +BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response); +BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response); void ntlm_output_target_name(NTLM_CONTEXT* context); void ntlm_output_channel_bindings(NTLM_CONTEXT* context); @@ -38,10 +41,10 @@ void ntlm_output_channel_bindings(NTLM_CONTEXT* context); void ntlm_current_time(BYTE* timestamp); void ntlm_generate_timestamp(NTLM_CONTEXT* context); -int ntlm_compute_lm_v2_response(NTLM_CONTEXT* context); -int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context); +BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context); +BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context); -void ntlm_rc4k(BYTE* key, int length, BYTE* plaintext, BYTE* ciphertext); +void ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext); void ntlm_generate_client_challenge(NTLM_CONTEXT* context); void ntlm_generate_server_challenge(NTLM_CONTEXT* context); void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context); @@ -50,12 +53,12 @@ void ntlm_generate_exported_session_key(NTLM_CONTEXT* context); void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context); void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context); -void ntlm_generate_client_signing_key(NTLM_CONTEXT* context); -void ntlm_generate_server_signing_key(NTLM_CONTEXT* context); -void ntlm_generate_client_sealing_key(NTLM_CONTEXT* context); -void ntlm_generate_server_sealing_key(NTLM_CONTEXT* context); +BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context); +BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context); +BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context); +BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context); void ntlm_init_rc4_seal_states(NTLM_CONTEXT* context); -void ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size); +BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size); #endif /* WINPR_AUTH_NTLM_COMPUTE_H */ diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.c b/winpr/libwinpr/sspi/NTLM/ntlm_message.c index 7502ecfc2bf0..ccb2e1078642 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_message.c +++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.c @@ -25,6 +25,7 @@ #include "../sspi.h" #include +#include #include #include #include @@ -33,143 +34,388 @@ #include "ntlm_message.h" -#include "../log.h" +#include "../../log.h" #define TAG WINPR_TAG("sspi.NTLM") +static wStream* Stream_StaticConstInit(wStream* buffer, const BYTE* data, size_t size) +{ + Stream_StaticInit(buffer, data, size); + return buffer; +} + +static wStream* Stream_StaticInit2(wStream* buffer, BYTE* data, size_t size) +{ + Stream_StaticInit(buffer, data, size); + return buffer; +} + static const char NTLM_SIGNATURE[8] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' }; -#ifdef WITH_DEBUG_NTLM -static const char* const NTLM_NEGOTIATE_STRINGS[] = { "NTLMSSP_NEGOTIATE_56", - "NTLMSSP_NEGOTIATE_KEY_EXCH", - "NTLMSSP_NEGOTIATE_128", - "NTLMSSP_RESERVED1", - "NTLMSSP_RESERVED2", - "NTLMSSP_RESERVED3", - "NTLMSSP_NEGOTIATE_VERSION", - "NTLMSSP_RESERVED4", - "NTLMSSP_NEGOTIATE_TARGET_INFO", - "NTLMSSP_REQUEST_NON_NT_SESSION_KEY", - "NTLMSSP_RESERVED5", - "NTLMSSP_NEGOTIATE_IDENTIFY", - "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY", - "NTLMSSP_RESERVED6", - "NTLMSSP_TARGET_TYPE_SERVER", - "NTLMSSP_TARGET_TYPE_DOMAIN", - "NTLMSSP_NEGOTIATE_ALWAYS_SIGN", - "NTLMSSP_RESERVED7", - "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED", - "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED", - "NTLMSSP_NEGOTIATE_ANONYMOUS", - "NTLMSSP_RESERVED8", - "NTLMSSP_NEGOTIATE_NTLM", - "NTLMSSP_RESERVED9", - "NTLMSSP_NEGOTIATE_LM_KEY", - "NTLMSSP_NEGOTIATE_DATAGRAM", - "NTLMSSP_NEGOTIATE_SEAL", - "NTLMSSP_NEGOTIATE_SIGN", - "NTLMSSP_RESERVED10", - "NTLMSSP_REQUEST_TARGET", - "NTLMSSP_NEGOTIATE_OEM", - "NTLMSSP_NEGOTIATE_UNICODE" }; +static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields); + +const char* ntlm_get_negotiate_string(UINT32 flag) +{ + if (flag & NTLMSSP_NEGOTIATE_56) + return "NTLMSSP_NEGOTIATE_56"; + if (flag & NTLMSSP_NEGOTIATE_KEY_EXCH) + return "NTLMSSP_NEGOTIATE_KEY_EXCH"; + if (flag & NTLMSSP_NEGOTIATE_128) + return "NTLMSSP_NEGOTIATE_128"; + if (flag & NTLMSSP_RESERVED1) + return "NTLMSSP_RESERVED1"; + if (flag & NTLMSSP_RESERVED2) + return "NTLMSSP_RESERVED2"; + if (flag & NTLMSSP_RESERVED3) + return "NTLMSSP_RESERVED3"; + if (flag & NTLMSSP_NEGOTIATE_VERSION) + return "NTLMSSP_NEGOTIATE_VERSION"; + if (flag & NTLMSSP_RESERVED4) + return "NTLMSSP_RESERVED4"; + if (flag & NTLMSSP_NEGOTIATE_TARGET_INFO) + return "NTLMSSP_NEGOTIATE_TARGET_INFO"; + if (flag & NTLMSSP_REQUEST_NON_NT_SESSION_KEY) + return "NTLMSSP_REQUEST_NON_NT_SESSION_KEY"; + if (flag & NTLMSSP_RESERVED5) + return "NTLMSSP_RESERVED5"; + if (flag & NTLMSSP_NEGOTIATE_IDENTIFY) + return "NTLMSSP_NEGOTIATE_IDENTIFY"; + if (flag & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY) + return "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY"; + if (flag & NTLMSSP_RESERVED6) + return "NTLMSSP_RESERVED6"; + if (flag & NTLMSSP_TARGET_TYPE_SERVER) + return "NTLMSSP_TARGET_TYPE_SERVER"; + if (flag & NTLMSSP_TARGET_TYPE_DOMAIN) + return "NTLMSSP_TARGET_TYPE_DOMAIN"; + if (flag & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + return "NTLMSSP_NEGOTIATE_ALWAYS_SIGN"; + if (flag & NTLMSSP_RESERVED7) + return "NTLMSSP_RESERVED7"; + if (flag & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) + return "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED"; + if (flag & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) + return "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED"; + if (flag & NTLMSSP_NEGOTIATE_ANONYMOUS) + return "NTLMSSP_NEGOTIATE_ANONYMOUS"; + if (flag & NTLMSSP_RESERVED8) + return "NTLMSSP_RESERVED8"; + if (flag & NTLMSSP_NEGOTIATE_NTLM) + return "NTLMSSP_NEGOTIATE_NTLM"; + if (flag & NTLMSSP_RESERVED9) + return "NTLMSSP_RESERVED9"; + if (flag & NTLMSSP_NEGOTIATE_LM_KEY) + return "NTLMSSP_NEGOTIATE_LM_KEY"; + if (flag & NTLMSSP_NEGOTIATE_DATAGRAM) + return "NTLMSSP_NEGOTIATE_DATAGRAM"; + if (flag & NTLMSSP_NEGOTIATE_SEAL) + return "NTLMSSP_NEGOTIATE_SEAL"; + if (flag & NTLMSSP_NEGOTIATE_SIGN) + return "NTLMSSP_NEGOTIATE_SIGN"; + if (flag & NTLMSSP_RESERVED10) + return "NTLMSSP_RESERVED10"; + if (flag & NTLMSSP_REQUEST_TARGET) + return "NTLMSSP_REQUEST_TARGET"; + if (flag & NTLMSSP_NEGOTIATE_OEM) + return "NTLMSSP_NEGOTIATE_OEM"; + if (flag & NTLMSSP_NEGOTIATE_UNICODE) + return "NTLMSSP_NEGOTIATE_UNICODE"; + return "NTLMSSP_NEGOTIATE_UNKNOWN"; +} + +#if defined(WITH_DEBUG_NTLM) +static void ntlm_print_message_fields(const NTLM_MESSAGE_FIELDS* fields, const char* name) +{ + WINPR_ASSERT(fields); + WINPR_ASSERT(name); + + WLog_VRB(TAG, "%s (Len: %" PRIu16 " MaxLen: %" PRIu16 " BufferOffset: %" PRIu32 ")", name, + fields->Len, fields->MaxLen, fields->BufferOffset); + + if (fields->Len > 0) + winpr_HexDump(TAG, WLOG_TRACE, fields->Buffer, fields->Len); +} static void ntlm_print_negotiate_flags(UINT32 flags) { int i; - const char* str; - WLog_INFO(TAG, "negotiateFlags \"0x%08" PRIX32 "\"", flags); + + WLog_VRB(TAG, "negotiateFlags \"0x%08" PRIX32 "\"", flags); for (i = 31; i >= 0; i--) { if ((flags >> i) & 1) { - str = NTLM_NEGOTIATE_STRINGS[(31 - i)]; - WLog_INFO(TAG, "\t%s (%d),", str, (31 - i)); + const char* str = ntlm_get_negotiate_string(1 << i); + WLog_VRB(TAG, "\t%s (%d),", str, (31 - i)); } } } + +static void ntlm_print_negotiate_message(const SecBuffer* NegotiateMessage, + const NTLM_NEGOTIATE_MESSAGE* message) +{ + WINPR_ASSERT(NegotiateMessage); + WINPR_ASSERT(message); + + WLog_VRB(TAG, "NEGOTIATE_MESSAGE (length = %" PRIu32 ")", NegotiateMessage->cbBuffer); + winpr_HexDump(TAG, WLOG_TRACE, NegotiateMessage->pvBuffer, NegotiateMessage->cbBuffer); + ntlm_print_negotiate_flags(message->NegotiateFlags); + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_print_version_info(&(message->Version)); +} + +static void ntlm_print_challenge_message(const SecBuffer* ChallengeMessage, + const NTLM_CHALLENGE_MESSAGE* message, + const SecBuffer* ChallengeTargetInfo) +{ + WINPR_ASSERT(ChallengeMessage); + WINPR_ASSERT(message); + + WLog_VRB(TAG, "CHALLENGE_MESSAGE (length = %" PRIu32 ")", ChallengeMessage->cbBuffer); + winpr_HexDump(TAG, WLOG_TRACE, ChallengeMessage->pvBuffer, ChallengeMessage->cbBuffer); + ntlm_print_negotiate_flags(message->NegotiateFlags); + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_print_version_info(&(message->Version)); + + ntlm_print_message_fields(&(message->TargetName), "TargetName"); + ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo"); + + if (ChallengeTargetInfo && (ChallengeTargetInfo->cbBuffer > 0)) + { + WLog_VRB(TAG, "ChallengeTargetInfo (%" PRIu32 "):", ChallengeTargetInfo->cbBuffer); + ntlm_print_av_pair_list(ChallengeTargetInfo->pvBuffer, ChallengeTargetInfo->cbBuffer); + } +} + +static void ntlm_print_authenticate_message(const SecBuffer* AuthenticateMessage, + const NTLM_AUTHENTICATE_MESSAGE* message, UINT32 flags, + const SecBuffer* AuthenticateTargetInfo) +{ + WINPR_ASSERT(AuthenticateMessage); + WINPR_ASSERT(message); + + WLog_VRB(TAG, "AUTHENTICATE_MESSAGE (length = %" PRIu32 ")", AuthenticateMessage->cbBuffer); + winpr_HexDump(TAG, WLOG_TRACE, AuthenticateMessage->pvBuffer, AuthenticateMessage->cbBuffer); + ntlm_print_negotiate_flags(message->NegotiateFlags); + + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) + ntlm_print_version_info(&(message->Version)); + + if (AuthenticateTargetInfo && (AuthenticateTargetInfo->cbBuffer > 0)) + { + WLog_VRB(TAG, "AuthenticateTargetInfo (%" PRIu32 "):", AuthenticateTargetInfo->cbBuffer); + ntlm_print_av_pair_list(AuthenticateTargetInfo->pvBuffer, AuthenticateTargetInfo->cbBuffer); + } + + ntlm_print_message_fields(&(message->DomainName), "DomainName"); + ntlm_print_message_fields(&(message->UserName), "UserName"); + ntlm_print_message_fields(&(message->Workstation), "Workstation"); + ntlm_print_message_fields(&(message->LmChallengeResponse), "LmChallengeResponse"); + ntlm_print_message_fields(&(message->NtChallengeResponse), "NtChallengeResponse"); + ntlm_print_message_fields(&(message->EncryptedRandomSessionKey), "EncryptedRandomSessionKey"); + + if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK) + { + WLog_VRB(TAG, "MessageIntegrityCheck (length = 16)"); + winpr_HexDump(TAG, WLOG_TRACE, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck)); + } +} + +static void ntlm_print_authentication_complete(const NTLM_CONTEXT* context) +{ + WINPR_ASSERT(context); + + WLog_VRB(TAG, "ClientChallenge"); + winpr_HexDump(TAG, WLOG_TRACE, context->ClientChallenge, 8); + WLog_VRB(TAG, "ServerChallenge"); + winpr_HexDump(TAG, WLOG_TRACE, context->ServerChallenge, 8); + WLog_VRB(TAG, "SessionBaseKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->SessionBaseKey, 16); + WLog_VRB(TAG, "KeyExchangeKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->KeyExchangeKey, 16); + WLog_VRB(TAG, "ExportedSessionKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ExportedSessionKey, 16); + WLog_VRB(TAG, "RandomSessionKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->RandomSessionKey, 16); + WLog_VRB(TAG, "ClientSigningKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ClientSigningKey, 16); + WLog_VRB(TAG, "ClientSealingKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ClientSealingKey, 16); + WLog_VRB(TAG, "ServerSigningKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ServerSigningKey, 16); + WLog_VRB(TAG, "ServerSealingKey"); + winpr_HexDump(TAG, WLOG_TRACE, context->ServerSealingKey, 16); + WLog_VRB(TAG, "Timestamp"); + winpr_HexDump(TAG, WLOG_TRACE, context->Timestamp, 8); +} #endif -static int ntlm_read_message_header(wStream* s, NTLM_MESSAGE_HEADER* header) +static BOOL ntlm_read_message_header(wStream* s, NTLM_MESSAGE_HEADER* header, UINT32 expected) { + WINPR_ASSERT(s); + WINPR_ASSERT(header); + if (Stream_GetRemainingLength(s) < 12) - return -1; + { + WLog_ERR(TAG, "Short NTLM_MESSAGE_HEADER::header %" PRIuz ", expected 12", + Stream_GetRemainingLength(s)); + return FALSE; + } Stream_Read(s, header->Signature, 8); Stream_Read_UINT32(s, header->MessageType); if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0) - return -1; + { + WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid signature, got %s, expected %s", + header->Signature, NTLM_SIGNATURE); + return FALSE; + } - return 1; + if (header->MessageType != expected) + { + WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid message tyep, got %s, expected %s", + ntlm_message_type_string(header->MessageType), ntlm_message_type_string(expected)); + return FALSE; + } + + return TRUE; } -static void ntlm_write_message_header(wStream* s, NTLM_MESSAGE_HEADER* header) +static BOOL ntlm_write_message_header(wStream* s, const NTLM_MESSAGE_HEADER* header) { + WINPR_ASSERT(s); + WINPR_ASSERT(header); + + if (Stream_GetRemainingCapacity(s) < sizeof(NTLM_SIGNATURE) + 4) + { + WLog_ERR(TAG, "Short NTLM_MESSAGE_HEADER::header %" PRIuz ", expected 12", + Stream_GetRemainingCapacity(s)); + return FALSE; + } + Stream_Write(s, header->Signature, sizeof(NTLM_SIGNATURE)); Stream_Write_UINT32(s, header->MessageType); + + return TRUE; } -static void ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType) +static BOOL ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType) { + WINPR_ASSERT(header); + CopyMemory(header->Signature, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE)); header->MessageType = MessageType; + return TRUE; } -static int ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields) +static BOOL ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields) { + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + if (Stream_GetRemainingLength(s) < 8) - return -1; + { + WLog_ERR(TAG, "Short NTLM_MESSAGE_FIELDS::header %" PRIuz ", expected %" PRIuz, + Stream_GetRemainingLength(s), 8); + return FALSE; + } + + ntlm_free_message_fields_buffer(fields); Stream_Read_UINT16(s, fields->Len); /* Len (2 bytes) */ Stream_Read_UINT16(s, fields->MaxLen); /* MaxLen (2 bytes) */ Stream_Read_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */ - return 1; + return TRUE; } -static void ntlm_write_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields) +static BOOL ntlm_write_message_fields(wStream* s, const NTLM_MESSAGE_FIELDS* fields) { + UINT16 MaxLen; + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + + MaxLen = fields->MaxLen; if (fields->MaxLen < 1) - fields->MaxLen = fields->Len; + MaxLen = fields->Len; + if (Stream_GetRemainingCapacity(s) < 8) + { + WLog_ERR(TAG, "Short NTLM_MESSAGE_FIELDS::header %" PRIuz ", expected %" PRIuz, + Stream_GetRemainingCapacity(s), 8); + return FALSE; + } Stream_Write_UINT16(s, fields->Len); /* Len (2 bytes) */ - Stream_Write_UINT16(s, fields->MaxLen); /* MaxLen (2 bytes) */ + Stream_Write_UINT16(s, MaxLen); /* MaxLen (2 bytes) */ Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */ + return TRUE; } -static int ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields) +static BOOL ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields) { + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + if (fields->Len > 0) { const UINT32 offset = fields->BufferOffset + fields->Len; if (fields->BufferOffset > UINT32_MAX - fields->Len) - return -1; + { + WLog_ERR(TAG, + "NTLM_MESSAGE_FIELDS::BufferOffset %" PRIu32 + " too large, maximum allowed is %" PRIu32, + fields->BufferOffset, UINT32_MAX - fields->Len); + return FALSE; + } if (offset > Stream_Length(s)) - return -1; + { + WLog_ERR(TAG, + "NTLM_MESSAGE_FIELDS::Buffer offset %" PRIu32 " beyond received data %" PRIuz, + offset, Stream_Length(s)); + return FALSE; + } fields->Buffer = (PBYTE)malloc(fields->Len); if (!fields->Buffer) - return -1; + { + WLog_ERR(TAG, "NTLM_MESSAGE_FIELDS::Buffer allocation of %" PRIu16 "bytes failed", + fields->Len); + return FALSE; + } Stream_SetPosition(s, fields->BufferOffset); Stream_Read(s, fields->Buffer, fields->Len); } - return 1; + return TRUE; } -static void ntlm_write_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields) +static BOOL ntlm_write_message_fields_buffer(wStream* s, const NTLM_MESSAGE_FIELDS* fields) { + WINPR_ASSERT(s); + WINPR_ASSERT(fields); + if (fields->Len > 0) { Stream_SetPosition(s, fields->BufferOffset); + if (Stream_GetRemainingCapacity(s) < fields->Len) + { + WLog_ERR(TAG, "Short NTLM_MESSAGE_FIELDS::Len %" PRIuz ", expected %" PRIu16, + Stream_GetRemainingCapacity(s), fields->Len); + return FALSE; + } Stream_Write(s, fields->Buffer, fields->Len); } + return TRUE; } -static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields) +void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields) { if (fields) { @@ -184,122 +430,208 @@ static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields) } } -#ifdef WITH_DEBUG_NTLM -static void ntlm_print_message_fields(NTLM_MESSAGE_FIELDS* fields, const char* name) +static BOOL ntlm_read_negotiate_flags(wStream* s, UINT32* flags, UINT32 required, const char* name) { - WLog_DBG(TAG, "%s (Len: %" PRIu16 " MaxLen: %" PRIu16 " BufferOffset: %" PRIu32 ")", name, - fields->Len, fields->MaxLen, fields->BufferOffset); + UINT32 NegotiateFlags = 0; + char buffer[1024] = { 0 }; + WINPR_ASSERT(s); + WINPR_ASSERT(flags); + WINPR_ASSERT(name); - if (fields->Len > 0) - winpr_HexDump(TAG, WLOG_DEBUG, fields->Buffer, fields->Len); + if (Stream_GetRemainingLength(s) < 4) + { + WLog_ERR(TAG, "%s::NegotiateFlags expected 4bytes, have %" PRIuz "bytes", name, + Stream_GetRemainingLength(s)); + return FALSE; + } + + Stream_Read_UINT32(s, NegotiateFlags); /* NegotiateFlags (4 bytes) */ + + if ((NegotiateFlags & required) != required) + { + WLog_ERR(TAG, "%s::NegotiateFlags invalid flags 0x08%" PRIx32 ", 0x%08" PRIx32 " required", + name, NegotiateFlags, required); + return FALSE; + } + + WLog_DBG(TAG, "Read flags %s", + ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), NegotiateFlags)); + *flags = NegotiateFlags; + return TRUE; +} + +static BOOL ntlm_write_negotiate_flags(wStream* s, UINT32 flags, const char* name) +{ + char buffer[1024] = { 0 }; + WINPR_ASSERT(s); + WINPR_ASSERT(name); + + if (Stream_GetRemainingCapacity(s) < 4) + { + WLog_ERR(TAG, "%s::NegotiateFlags expected 4bytes, have %" PRIuz "bytes", name, + Stream_GetRemainingCapacity(s)); + return FALSE; + } + + WLog_DBG(TAG, "Write flags %s", ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), flags)); + Stream_Write_UINT32(s, flags); /* NegotiateFlags (4 bytes) */ + return TRUE; +} + +static BOOL ntlm_read_message_integrity_check(wStream* s, size_t* offset, BYTE* data, size_t size, + const char* name) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(offset); + WINPR_ASSERT(data); + WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH); + WINPR_ASSERT(name); + + *offset = Stream_GetPosition(s); + + if (Stream_GetRemainingLength(s) < size) + { + WLog_ERR(TAG, + "%s::MessageIntegrityCheckOffset expected %" PRIuz "bytes, got " + "%" PRIuz "byets", + name, size, Stream_GetRemainingLength(s)); + return FALSE; + } + + Stream_Read(s, data, size); + return TRUE; +} + +static BOOL ntlm_write_message_integrity_check(wStream* s, size_t offset, const BYTE* data, + size_t size, const char* name) +{ + size_t pos; + + WINPR_ASSERT(s); + WINPR_ASSERT(data); + WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH); + WINPR_ASSERT(name); + + pos = Stream_GetPosition(s); + + if (offset + size > Stream_Capacity(s)) + { + WLog_ERR(TAG, + "%s::MessageIntegrityCheck invalid offset[length] %" PRIuz "[%" PRIuz + "], got %" PRIuz, + name, offset, size, Stream_GetRemainingCapacity(s)); + return FALSE; + } + Stream_SetPosition(s, offset); + if (Stream_GetRemainingCapacity(s) < size) + { + WLog_ERR(TAG, "%s::MessageIntegrityCheck expected %" PRIuz "bytes, got %" PRIuz "bytes", + name, size, Stream_GetRemainingCapacity(s)); + return FALSE; + } + + Stream_Write(s, data, size); + Stream_SetPosition(s, pos); + return TRUE; } -#endif SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer) { + wStream sbuffer; wStream* s; size_t length; + const NTLM_NEGOTIATE_MESSAGE empty = { 0 }; NTLM_NEGOTIATE_MESSAGE* message; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + message = &context->NEGOTIATE_MESSAGE; - ZeroMemory(message, sizeof(NTLM_NEGOTIATE_MESSAGE)); - s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer); + WINPR_ASSERT(message); - if (!s) - return SEC_E_INTERNAL_ERROR; + *message = empty; - if (ntlm_read_message_header(s, (NTLM_MESSAGE_HEADER*)message) < 0) - { - Stream_Free(s, FALSE); - return SEC_E_INVALID_TOKEN; - } + s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); - if (message->MessageType != MESSAGE_TYPE_NEGOTIATE) - { - Stream_Free(s, FALSE); - return SEC_E_INVALID_TOKEN; - } + if (!s) + return SEC_E_INTERNAL_ERROR; - if (Stream_GetRemainingLength(s) < 4) - { - Stream_Free(s, FALSE); + if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_NEGOTIATE)) return SEC_E_INVALID_TOKEN; - } - Stream_Read_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */ - if (!((message->NegotiateFlags & NTLMSSP_REQUEST_TARGET) && - (message->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM) && - (message->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE))) - { - Stream_Free(s, FALSE); + if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, + NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_NEGOTIATE_UNICODE, + "NTLM_NEGOTIATE_MESSAGE")) return SEC_E_INVALID_TOKEN; - } context->NegotiateFlags = message->NegotiateFlags; /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */ - - if (ntlm_read_message_fields(s, &(message->DomainName)) < 0) /* DomainNameFields (8 bytes) */ + // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) { - Stream_Free(s, FALSE); - return SEC_E_INVALID_TOKEN; + if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */ + return SEC_E_INVALID_TOKEN; } /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */ - - if (ntlm_read_message_fields(s, &(message->Workstation)) < 0) /* WorkstationFields (8 bytes) */ + // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) { - Stream_Free(s, FALSE); - return SEC_E_INVALID_TOKEN; + if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */ + return SEC_E_INVALID_TOKEN; } if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) { - if (ntlm_read_version_info(s, &(message->Version)) < 0) /* Version (8 bytes) */ - { - Stream_Free(s, FALSE); + if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */ return SEC_E_INVALID_TOKEN; - } } + if (!ntlm_read_message_fields_buffer(s, &message->DomainName)) + return SEC_E_INVALID_TOKEN; + + if (!ntlm_read_message_fields_buffer(s, &message->Workstation)) + return SEC_E_INVALID_TOKEN; + length = Stream_GetPosition(s); - buffer->cbBuffer = length; + WINPR_ASSERT(length <= ULONG_MAX); + buffer->cbBuffer = (ULONG)length; - if (!sspi_SecBufferAlloc(&context->NegotiateMessage, length)) - { - Stream_Free(s, FALSE); + if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length)) return SEC_E_INTERNAL_ERROR; - } CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer); context->NegotiateMessage.BufferType = buffer->BufferType; -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "NEGOTIATE_MESSAGE (length = %" PRIu32 ")", context->NegotiateMessage.cbBuffer); - winpr_HexDump(TAG, WLOG_DEBUG, context->NegotiateMessage.pvBuffer, - context->NegotiateMessage.cbBuffer); - ntlm_print_negotiate_flags(message->NegotiateFlags); - - if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_print_version_info(&(message->Version)); - +#if defined(WITH_DEBUG_NTLM) + ntlm_print_negotiate_message(&context->NegotiateMessage, message); #endif - context->state = NTLM_STATE_CHALLENGE; - Stream_Free(s, FALSE); + ntlm_change_state(context, NTLM_STATE_CHALLENGE); return SEC_I_CONTINUE_NEEDED; } SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer) { + wStream sbuffer; wStream* s; size_t length; + const NTLM_NEGOTIATE_MESSAGE empty = { 0 }; NTLM_NEGOTIATE_MESSAGE* message; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + message = &context->NEGOTIATE_MESSAGE; - ZeroMemory(message, sizeof(NTLM_NEGOTIATE_MESSAGE)); - s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer); + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticInit2(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); if (!s) return SEC_E_INTERNAL_ERROR; - ntlm_populate_message_header((NTLM_MESSAGE_HEADER*)message, MESSAGE_TYPE_NEGOTIATE); + if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_NEGOTIATE)) + return SEC_E_INTERNAL_ERROR; if (context->NTLMv2) { @@ -329,96 +661,101 @@ SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer bu context->NegotiateFlags = message->NegotiateFlags; /* Message Header (12 bytes) */ - ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*)message); - Stream_Write_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */ + if (!ntlm_write_message_header(s, &message->header)) + return SEC_E_INTERNAL_ERROR; + + if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_NEGOTIATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; + /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */ /* DomainNameFields (8 bytes) */ - ntlm_write_message_fields(s, &(message->DomainName)); + if (!ntlm_write_message_fields(s, &(message->DomainName))) + return SEC_E_INTERNAL_ERROR; + /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */ /* WorkstationFields (8 bytes) */ - ntlm_write_message_fields(s, &(message->Workstation)); + if (!ntlm_write_message_fields(s, &(message->Workstation))) + return SEC_E_INTERNAL_ERROR; if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_write_version_info(s, &(message->Version)); + { + if (!ntlm_write_version_info(s, &(message->Version))) + return SEC_E_INTERNAL_ERROR; + } length = Stream_GetPosition(s); - buffer->cbBuffer = length; + WINPR_ASSERT(length <= ULONG_MAX); + buffer->cbBuffer = (ULONG)length; - if (!sspi_SecBufferAlloc(&context->NegotiateMessage, length)) - { - Stream_Free(s, FALSE); + if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length)) return SEC_E_INTERNAL_ERROR; - } CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer); context->NegotiateMessage.BufferType = buffer->BufferType; -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "NEGOTIATE_MESSAGE (length = %d)", length); - winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(s), length); - - if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_print_version_info(&(message->Version)); - +#if defined(WITH_DEBUG_NTLM) + ntlm_print_negotiate_message(&context->NegotiateMessage, message); #endif - context->state = NTLM_STATE_CHALLENGE; - Stream_Free(s, FALSE); + ntlm_change_state(context, NTLM_STATE_CHALLENGE); return SEC_I_CONTINUE_NEEDED; } SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer) { SECURITY_STATUS status = SEC_E_INVALID_TOKEN; + wStream sbuffer; wStream* s; size_t length; size_t StartOffset; size_t PayloadOffset; NTLM_AV_PAIR* AvTimestamp; + const NTLM_CHALLENGE_MESSAGE empty = { 0 }; NTLM_CHALLENGE_MESSAGE* message; + if (!context || !buffer) return SEC_E_INTERNAL_ERROR; ntlm_generate_client_challenge(context); message = &context->CHALLENGE_MESSAGE; - ZeroMemory(message, sizeof(NTLM_CHALLENGE_MESSAGE)); - s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer); + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); if (!s) return SEC_E_INTERNAL_ERROR; StartOffset = Stream_GetPosition(s); - if (ntlm_read_message_header(s, (NTLM_MESSAGE_HEADER*)message) < 0) - goto fail; - - if (message->MessageType != MESSAGE_TYPE_CHALLENGE) + if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_CHALLENGE)) goto fail; - if (ntlm_read_message_fields(s, &(message->TargetName)) < 0) /* TargetNameFields (8 bytes) */ + if (!ntlm_read_message_fields(s, &(message->TargetName))) /* TargetNameFields (8 bytes) */ goto fail; - if (Stream_GetRemainingLength(s) < 4) + if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_CHALLENGE_MESSAGE")) goto fail; - Stream_Read_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */ context->NegotiateFlags = message->NegotiateFlags; - if (Stream_GetRemainingLength(s) < 8) + if (Stream_GetRemainingLength(s) < 16) + { + WLog_ERR(TAG, + "NTLM_CHALLENGE_MESSAGE::ServerChallenge expected 16bytes, got %" PRIuz "bytes", + Stream_GetRemainingLength(s)); goto fail; + } Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */ CopyMemory(context->ServerChallenge, message->ServerChallenge, 8); - - if (Stream_GetRemainingLength(s) < 8) - goto fail; - Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */ - if (ntlm_read_message_fields(s, &(message->TargetInfo)) < 0) /* TargetInfoFields (8 bytes) */ + if (!ntlm_read_message_fields(s, &(message->TargetInfo))) /* TargetInfoFields (8 bytes) */ goto fail; if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) { - if (ntlm_read_version_info(s, &(message->Version)) < 0) /* Version (8 bytes) */ + if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */ goto fail; } @@ -428,7 +765,7 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buf status = SEC_E_INTERNAL_ERROR; if (message->TargetName.Len > 0) { - if (ntlm_read_message_fields_buffer(s, &(message->TargetName)) < 0) + if (!ntlm_read_message_fields_buffer(s, &(message->TargetName))) goto fail; } @@ -436,7 +773,7 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buf { size_t cbAvTimestamp; - if (ntlm_read_message_fields_buffer(s, &(message->TargetInfo)) < 0) + if (!ntlm_read_message_fields_buffer(s, &(message->TargetInfo))) goto fail; context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer; @@ -462,36 +799,19 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buf if (length > buffer->cbBuffer) goto fail; - if (!sspi_SecBufferAlloc(&context->ChallengeMessage, length)) + if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length)) goto fail; if (context->ChallengeMessage.pvBuffer) CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s) + StartOffset, length); -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "CHALLENGE_MESSAGE (length = %d)", length); - winpr_HexDump(TAG, WLOG_DEBUG, context->ChallengeMessage.pvBuffer, - context->ChallengeMessage.cbBuffer); - ntlm_print_negotiate_flags(context->NegotiateFlags); - - if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_print_version_info(&(message->Version)); - - ntlm_print_message_fields(&(message->TargetName), "TargetName"); - ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo"); - - if (context->ChallengeTargetInfo.cbBuffer > 0) - { - WLog_DBG(TAG, "ChallengeTargetInfo (%" PRIu32 "):", context->ChallengeTargetInfo.cbBuffer); - ntlm_print_av_pair_list(context->ChallengeTargetInfo.pvBuffer, - context->ChallengeTargetInfo.cbBuffer); - } - +#if defined(WITH_DEBUG_NTLM) + ntlm_print_challenge_message(&context->ChallengeMessage, message, NULL); #endif /* AV_PAIRs */ if (context->NTLMv2) { - if (ntlm_construct_authenticate_target_info(context) < 0) + if (!ntlm_construct_authenticate_target_info(context)) goto fail; sspi_SecBufferFree(&context->ChallengeTargetInfo); @@ -501,10 +821,10 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buf ntlm_generate_timestamp(context); /* Timestamp */ - if (ntlm_compute_lm_v2_response(context) < 0) /* LmChallengeResponse */ + if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */ goto fail; - if (ntlm_compute_ntlm_v2_response(context) < 0) /* NtChallengeResponse */ + if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */ goto fail; ntlm_generate_key_exchange_key(context); /* KeyExchangeKey */ @@ -512,54 +832,45 @@ SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buf ntlm_generate_exported_session_key(context); /* ExportedSessionKey */ ntlm_encrypt_random_session_key(context); /* EncryptedRandomSessionKey */ /* Generate signing keys */ - ntlm_generate_client_signing_key(context); - ntlm_generate_server_signing_key(context); + if (!ntlm_generate_client_signing_key(context)) + goto fail; + if (!ntlm_generate_server_signing_key(context)) + goto fail; /* Generate sealing keys */ - ntlm_generate_client_sealing_key(context); - ntlm_generate_server_sealing_key(context); + if (!ntlm_generate_client_sealing_key(context)) + goto fail; + if (!ntlm_generate_server_sealing_key(context)) + goto fail; /* Initialize RC4 seal state using client sealing key */ ntlm_init_rc4_seal_states(context); -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "ClientChallenge"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ClientChallenge, 8); - WLog_DBG(TAG, "ServerChallenge"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ServerChallenge, 8); - WLog_DBG(TAG, "SessionBaseKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->SessionBaseKey, 16); - WLog_DBG(TAG, "KeyExchangeKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->KeyExchangeKey, 16); - WLog_DBG(TAG, "ExportedSessionKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ExportedSessionKey, 16); - WLog_DBG(TAG, "RandomSessionKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->RandomSessionKey, 16); - WLog_DBG(TAG, "ClientSigningKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSigningKey, 16); - WLog_DBG(TAG, "ClientSealingKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSealingKey, 16); - WLog_DBG(TAG, "ServerSigningKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSigningKey, 16); - WLog_DBG(TAG, "ServerSealingKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSealingKey, 16); - WLog_DBG(TAG, "Timestamp"); - winpr_HexDump(TAG, WLOG_DEBUG, context->Timestamp, 8); +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authentication_complete(context); #endif - context->state = NTLM_STATE_AUTHENTICATE; + ntlm_change_state(context, NTLM_STATE_AUTHENTICATE); status = SEC_I_CONTINUE_NEEDED; fail: ntlm_free_message_fields_buffer(&(message->TargetName)); - Stream_Free(s, FALSE); return status; } SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer) { + wStream sbuffer; wStream* s; size_t length; UINT32 PayloadOffset; + const NTLM_CHALLENGE_MESSAGE empty = { 0 }; NTLM_CHALLENGE_MESSAGE* message; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + message = &context->CHALLENGE_MESSAGE; - ZeroMemory(message, sizeof(NTLM_CHALLENGE_MESSAGE)); - s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer); + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticInit2(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); if (!s) return SEC_E_INTERNAL_ERROR; @@ -568,17 +879,17 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer bu ntlm_generate_server_challenge(context); /* Server Challenge */ ntlm_generate_timestamp(context); /* Timestamp */ - if (ntlm_construct_challenge_target_info(context) < 0) /* TargetInfo */ - { - Stream_Free(s, FALSE); + if (!ntlm_construct_challenge_target_info(context)) /* TargetInfo */ return SEC_E_INTERNAL_ERROR; - } CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */ message->NegotiateFlags = context->NegotiateFlags; - ntlm_populate_message_header((NTLM_MESSAGE_HEADER*)message, MESSAGE_TYPE_CHALLENGE); + if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_CHALLENGE)) + return SEC_E_INTERNAL_ERROR; + /* Message Header (12 bytes) */ - ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*)message); + if (!ntlm_write_message_header(s, &message->header)) + return SEC_E_INTERNAL_ERROR; if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET) { @@ -602,99 +913,119 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer bu message->TargetName.BufferOffset = PayloadOffset; message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len; /* TargetNameFields (8 bytes) */ - ntlm_write_message_fields(s, &(message->TargetName)); - Stream_Write_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */ + if (!ntlm_write_message_fields(s, &(message->TargetName))) + return SEC_E_INTERNAL_ERROR; + + if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_CHALLENGE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; + + if (Stream_GetRemainingCapacity(s) < 16) + { + WLog_ERR(TAG, + "NTLM_CHALLENGE_MESSAGE::ServerChallenge expected 16bytes, got %" PRIuz "bytes", + Stream_GetRemainingCapacity(s)); + return SEC_E_INTERNAL_ERROR; + } + Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */ Stream_Write(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */ + /* TargetInfoFields (8 bytes) */ - ntlm_write_message_fields(s, &(message->TargetInfo)); + if (!ntlm_write_message_fields(s, &(message->TargetInfo))) + return SEC_E_INTERNAL_ERROR; if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_write_version_info(s, &(message->Version)); /* Version (8 bytes) */ + { + if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + } /* Payload (variable) */ - if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET) - ntlm_write_message_fields_buffer(s, &(message->TargetName)); + { + if (!ntlm_write_message_fields_buffer(s, &(message->TargetName))) + return SEC_E_INTERNAL_ERROR; + } if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO) - ntlm_write_message_fields_buffer(s, &(message->TargetInfo)); + { + if (!ntlm_write_message_fields_buffer(s, &(message->TargetInfo))) + return SEC_E_INTERNAL_ERROR; + } length = Stream_GetPosition(s); - buffer->cbBuffer = length; + WINPR_ASSERT(length <= ULONG_MAX); + buffer->cbBuffer = (ULONG)length; - if (!sspi_SecBufferAlloc(&context->ChallengeMessage, length)) - { - Stream_Free(s, FALSE); + if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length)) return SEC_E_INTERNAL_ERROR; - } CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s), length); -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "CHALLENGE_MESSAGE (length = %d)", length); - winpr_HexDump(TAG, WLOG_DEBUG, context->ChallengeMessage.pvBuffer, - context->ChallengeMessage.cbBuffer); - ntlm_print_negotiate_flags(message->NegotiateFlags); - - if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_print_version_info(&(message->Version)); - - ntlm_print_message_fields(&(message->TargetName), "TargetName"); - ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo"); +#if defined(WITH_DEBUG_NTLM) + ntlm_print_challenge_message(&context->ChallengeMessage, message, + &context->ChallengeTargetInfo); #endif - context->state = NTLM_STATE_AUTHENTICATE; - Stream_Free(s, FALSE); + ntlm_change_state(context, NTLM_STATE_AUTHENTICATE); return SEC_I_CONTINUE_NEEDED; } SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer) { SECURITY_STATUS status = SEC_E_INVALID_TOKEN; + wStream sbuffer; wStream* s; size_t length; UINT32 flags = 0; NTLM_AV_PAIR* AvFlags = NULL; - UINT32 PayloadBufferOffset; + size_t PayloadBufferOffset; + const NTLM_AUTHENTICATE_MESSAGE empty = { 0 }; NTLM_AUTHENTICATE_MESSAGE* message; - SSPI_CREDENTIALS* credentials = context->credentials; + SSPI_CREDENTIALS* credentials; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + + credentials = context->credentials; + WINPR_ASSERT(credentials); message = &context->AUTHENTICATE_MESSAGE; - ZeroMemory(message, sizeof(NTLM_AUTHENTICATE_MESSAGE)); - s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer); + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); if (!s) return SEC_E_INTERNAL_ERROR; - if (ntlm_read_message_header(s, (NTLM_MESSAGE_HEADER*)message) < 0) + if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_AUTHENTICATE)) goto fail; - if (message->MessageType != MESSAGE_TYPE_AUTHENTICATE) + if (!ntlm_read_message_fields( + s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */ goto fail; - if (ntlm_read_message_fields(s, &(message->LmChallengeResponse)) < - 0) /* LmChallengeResponseFields (8 bytes) */ + if (!ntlm_read_message_fields( + s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */ goto fail; - if (ntlm_read_message_fields(s, &(message->NtChallengeResponse)) < - 0) /* NtChallengeResponseFields (8 bytes) */ + if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */ goto fail; - if (ntlm_read_message_fields(s, &(message->DomainName)) < 0) /* DomainNameFields (8 bytes) */ + if (!ntlm_read_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */ goto fail; - if (ntlm_read_message_fields(s, &(message->UserName)) < 0) /* UserNameFields (8 bytes) */ + if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */ goto fail; - if (ntlm_read_message_fields(s, &(message->Workstation)) < 0) /* WorkstationFields (8 bytes) */ + if (!ntlm_read_message_fields( + s, + &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */ goto fail; - if (ntlm_read_message_fields(s, &(message->EncryptedRandomSessionKey)) < - 0) /* EncryptedRandomSessionKeyFields (8 bytes) */ + if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_AUTHENTICATE_MESSAGE")) goto fail; - if (Stream_GetRemainingLength(s) < 4) - goto fail; - Stream_Read_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */ context->NegotiateKeyExchange = (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) ? TRUE : FALSE; @@ -704,44 +1035,42 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) { - if (ntlm_read_version_info(s, &(message->Version)) < 0) /* Version (8 bytes) */ + if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */ goto fail; } PayloadBufferOffset = Stream_GetPosition(s); status = SEC_E_INTERNAL_ERROR; - if (ntlm_read_message_fields_buffer(s, &(message->DomainName)) < 0) /* DomainName */ + if (!ntlm_read_message_fields_buffer(s, &(message->DomainName))) /* DomainName */ goto fail; - if (ntlm_read_message_fields_buffer(s, &(message->UserName)) < 0) /* UserName */ + if (!ntlm_read_message_fields_buffer(s, &(message->UserName))) /* UserName */ goto fail; - if (ntlm_read_message_fields_buffer(s, &(message->Workstation)) < 0) /* Workstation */ + if (!ntlm_read_message_fields_buffer(s, &(message->Workstation))) /* Workstation */ goto fail; - if (ntlm_read_message_fields_buffer(s, &(message->LmChallengeResponse)) < - 0) /* LmChallengeResponse */ + if (!ntlm_read_message_fields_buffer(s, + &(message->LmChallengeResponse))) /* LmChallengeResponse */ goto fail; - if (ntlm_read_message_fields_buffer(s, &(message->NtChallengeResponse)) < - 0) /* NtChallengeResponse */ + if (!ntlm_read_message_fields_buffer(s, + &(message->NtChallengeResponse))) /* NtChallengeResponse */ goto fail; if (message->NtChallengeResponse.Len > 0) { - int rc; size_t cbAvFlags; - wStream* snt = - Stream_New(message->NtChallengeResponse.Buffer, message->NtChallengeResponse.Len); + wStream ssbuffer; + wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer, + message->NtChallengeResponse.Len); if (!snt) goto fail; status = SEC_E_INVALID_TOKEN; - rc = ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)); - Stream_Free(snt, FALSE); - if (rc < 0) + if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response))) goto fail; status = SEC_E_INTERNAL_ERROR; @@ -759,8 +1088,8 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer Data_Read_UINT32(ntlm_av_pair_get_value_pointer(AvFlags), flags); } - if (ntlm_read_message_fields_buffer(s, &(message->EncryptedRandomSessionKey)) < - 0) /* EncryptedRandomSessionKey */ + if (!ntlm_read_message_fields_buffer( + s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */ goto fail; if (message->EncryptedRandomSessionKey.Len > 0) @@ -773,51 +1102,28 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer } length = Stream_GetPosition(s); + WINPR_ASSERT(length <= ULONG_MAX); - if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, length)) + if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length)) goto fail; CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length); - buffer->cbBuffer = length; + buffer->cbBuffer = (ULONG)length; Stream_SetPosition(s, PayloadBufferOffset); if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK) { - context->MessageIntegrityCheckOffset = (UINT32)Stream_GetPosition(s); - status = SEC_E_INVALID_TOKEN; - if (Stream_GetRemainingLength(s) < 16) + if (!ntlm_read_message_integrity_check( + s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE")) goto fail; - - Stream_Read(s, message->MessageIntegrityCheck, 16); } status = SEC_E_INTERNAL_ERROR; -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "AUTHENTICATE_MESSAGE (length = %" PRIu32 ")", - context->AuthenticateMessage.cbBuffer); - winpr_HexDump(TAG, WLOG_DEBUG, context->AuthenticateMessage.pvBuffer, - context->AuthenticateMessage.cbBuffer); - - if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_print_version_info(&(message->Version)); - - ntlm_print_message_fields(&(message->DomainName), "DomainName"); - ntlm_print_message_fields(&(message->UserName), "UserName"); - ntlm_print_message_fields(&(message->Workstation), "Workstation"); - ntlm_print_message_fields(&(message->LmChallengeResponse), "LmChallengeResponse"); - ntlm_print_message_fields(&(message->NtChallengeResponse), "NtChallengeResponse"); - ntlm_print_message_fields(&(message->EncryptedRandomSessionKey), "EncryptedRandomSessionKey"); - ntlm_print_av_pair_list(context->NTLMv2Response.Challenge.AvPairs, - context->NTLMv2Response.Challenge.cbAvPairs); - - if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK) - { - WLog_DBG(TAG, "MessageIntegrityCheck:"); - winpr_HexDump(TAG, WLOG_DEBUG, message->MessageIntegrityCheck, 16); - } - +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authenticate_message(&context->AuthenticateMessage, message, flags, NULL); #endif if (message->UserName.Len > 0) @@ -843,13 +1149,11 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer credentials->identity.DomainLength = message->DomainName.Len / 2; } - Stream_Free(s, FALSE); /* Computations beyond this point require the NTLM hash of the password */ - context->state = NTLM_STATE_COMPLETION; + ntlm_change_state(context, NTLM_STATE_COMPLETION); return SEC_I_COMPLETE_NEEDED; fail: - Stream_Free(s, FALSE); return status; } @@ -862,14 +1166,26 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer) { + wStream sbuffer; wStream* s; size_t length; UINT32 PayloadBufferOffset; + const NTLM_AUTHENTICATE_MESSAGE empty = { 0 }; NTLM_AUTHENTICATE_MESSAGE* message; - SSPI_CREDENTIALS* credentials = context->credentials; + SSPI_CREDENTIALS* credentials; + + WINPR_ASSERT(context); + WINPR_ASSERT(buffer); + + credentials = context->credentials; + WINPR_ASSERT(credentials); + message = &context->AUTHENTICATE_MESSAGE; - ZeroMemory(message, sizeof(NTLM_AUTHENTICATE_MESSAGE)); - s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer); + WINPR_ASSERT(message); + + *message = empty; + + s = Stream_StaticInit2(&sbuffer, buffer->pvBuffer, buffer->cbBuffer); if (!s) return SEC_E_INTERNAL_ERROR; @@ -948,95 +1264,103 @@ SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer message->LmChallengeResponse.BufferOffset + message->LmChallengeResponse.Len; message->EncryptedRandomSessionKey.BufferOffset = message->NtChallengeResponse.BufferOffset + message->NtChallengeResponse.Len; - ntlm_populate_message_header((NTLM_MESSAGE_HEADER*)message, MESSAGE_TYPE_AUTHENTICATE); - ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*)message); /* Message Header (12 bytes) */ - ntlm_write_message_fields( - s, &(message->LmChallengeResponse)); /* LmChallengeResponseFields (8 bytes) */ - ntlm_write_message_fields( - s, &(message->NtChallengeResponse)); /* NtChallengeResponseFields (8 bytes) */ - ntlm_write_message_fields(s, &(message->DomainName)); /* DomainNameFields (8 bytes) */ - ntlm_write_message_fields(s, &(message->UserName)); /* UserNameFields (8 bytes) */ - ntlm_write_message_fields(s, &(message->Workstation)); /* WorkstationFields (8 bytes) */ - ntlm_write_message_fields( - s, &(message->EncryptedRandomSessionKey)); /* EncryptedRandomSessionKeyFields (8 bytes) */ - Stream_Write_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */ + if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_AUTHENTICATE)) + return SEC_E_INVALID_TOKEN; + if (!ntlm_write_message_header(s, &message->header)) /* Message Header (12 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields( + s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields( + s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_message_fields( + s, + &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_AUTHENTICATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_write_version_info(s, &(message->Version)); /* Version (8 bytes) */ + { + if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */ + return SEC_E_INTERNAL_ERROR; + } if (context->UseMIC) { - context->MessageIntegrityCheckOffset = (UINT32)Stream_GetPosition(s); - Stream_Zero(s, 16); /* Message Integrity Check (16 bytes) */ + const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 }; + + context->MessageIntegrityCheckOffset = Stream_GetPosition(s); + if (!ntlm_write_message_integrity_check(s, Stream_GetPosition(s), data, sizeof(data), + "NTLM_AUTHENTICATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; } if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) - ntlm_write_message_fields_buffer(s, &(message->DomainName)); /* DomainName */ + { + if (!ntlm_write_message_fields_buffer(s, &(message->DomainName))) /* DomainName */ + return SEC_E_INTERNAL_ERROR; + } - ntlm_write_message_fields_buffer(s, &(message->UserName)); /* UserName */ + if (!ntlm_write_message_fields_buffer(s, &(message->UserName))) /* UserName */ + return SEC_E_INTERNAL_ERROR; if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) - ntlm_write_message_fields_buffer(s, &(message->Workstation)); /* Workstation */ + { + if (!ntlm_write_message_fields_buffer(s, &(message->Workstation))) /* Workstation */ + return SEC_E_INTERNAL_ERROR; + } - ntlm_write_message_fields_buffer(s, &(message->LmChallengeResponse)); /* LmChallengeResponse */ - ntlm_write_message_fields_buffer(s, &(message->NtChallengeResponse)); /* NtChallengeResponse */ + if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) + { + if (!ntlm_write_message_fields_buffer( + s, &(message->LmChallengeResponse))) /* LmChallengeResponse */ + return SEC_E_INTERNAL_ERROR; + } + if (!ntlm_write_message_fields_buffer( + s, &(message->NtChallengeResponse))) /* NtChallengeResponse */ + return SEC_E_INTERNAL_ERROR; if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) - ntlm_write_message_fields_buffer( - s, &(message->EncryptedRandomSessionKey)); /* EncryptedRandomSessionKey */ + { + if (!ntlm_write_message_fields_buffer( + s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */ + return SEC_E_INTERNAL_ERROR; + } length = Stream_GetPosition(s); + WINPR_ASSERT(length <= ULONG_MAX); - if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, length)) - { - Stream_Free(s, FALSE); + if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length)) return SEC_E_INTERNAL_ERROR; - } CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length); - buffer->cbBuffer = length; + buffer->cbBuffer = (ULONG)length; if (context->UseMIC) { /* Message Integrity Check */ - ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck, 16); - Stream_SetPosition(s, context->MessageIntegrityCheckOffset); - Stream_Write(s, message->MessageIntegrityCheck, 16); - Stream_SetPosition(s, length); - } - -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "AUTHENTICATE_MESSAGE (length = %d)", length); - winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(s), length); - ntlm_print_negotiate_flags(message->NegotiateFlags); - - if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION) - ntlm_print_version_info(&(message->Version)); - - if (context->AuthenticateTargetInfo.cbBuffer > 0) - { - WLog_DBG(TAG, - "AuthenticateTargetInfo (%" PRIu32 "):", context->AuthenticateTargetInfo.cbBuffer); - ntlm_print_av_pair_list(context->AuthenticateTargetInfo.pvBuffer, - context->AuthenticateTargetInfo.cbBuffer); - } - - ntlm_print_message_fields(&(message->DomainName), "DomainName"); - ntlm_print_message_fields(&(message->UserName), "UserName"); - ntlm_print_message_fields(&(message->Workstation), "Workstation"); - ntlm_print_message_fields(&(message->LmChallengeResponse), "LmChallengeResponse"); - ntlm_print_message_fields(&(message->NtChallengeResponse), "NtChallengeResponse"); - ntlm_print_message_fields(&(message->EncryptedRandomSessionKey), "EncryptedRandomSessionKey"); - - if (context->UseMIC) - { - WLog_DBG(TAG, "MessageIntegrityCheck (length = 16)"); - winpr_HexDump(TAG, WLOG_DEBUG, message->MessageIntegrityCheck, 16); + ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck)); + if (!ntlm_write_message_integrity_check( + s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE")) + return SEC_E_INTERNAL_ERROR; } +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authenticate_message(&context->AuthenticateMessage, message, + context->UseMIC ? MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK : 0, + &context->AuthenticateTargetInfo); #endif - context->state = NTLM_STATE_FINAL; - Stream_Free(s, FALSE); + ntlm_change_state(context, NTLM_STATE_FINAL); return SEC_E_OK; } @@ -1046,25 +1370,29 @@ SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context) size_t cbAvFlags; NTLM_AV_PAIR* AvFlags = NULL; NTLM_AUTHENTICATE_MESSAGE* message; - BYTE messageIntegrityCheck[16]; if (!context) return SEC_E_INVALID_PARAMETER; - if (context->state != NTLM_STATE_COMPLETION) + if (ntlm_get_state(context) != NTLM_STATE_COMPLETION) return SEC_E_OUT_OF_SEQUENCE; message = &context->AUTHENTICATE_MESSAGE; + WINPR_ASSERT(message); + AvFlags = ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs, context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags); if (AvFlags) Data_Read_UINT32(ntlm_av_pair_get_value_pointer(AvFlags), flags); - if (ntlm_compute_lm_v2_response(context) < 0) /* LmChallengeResponse */ - return SEC_E_INTERNAL_ERROR; + if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY) + { + if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */ + return SEC_E_INTERNAL_ERROR; + } - if (ntlm_compute_ntlm_v2_response(context) < 0) /* NtChallengeResponse */ + if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */ return SEC_E_INTERNAL_ERROR; /* KeyExchangeKey */ @@ -1076,16 +1404,16 @@ SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context) if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK) { - ZeroMemory( - &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset], - 16); + BYTE messageIntegrityCheck[16] = { 0 }; + ntlm_compute_message_integrity_check(context, messageIntegrityCheck, sizeof(messageIntegrityCheck)); CopyMemory( &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset], - message->MessageIntegrityCheck, 16); + message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck)); - if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck, 16) != 0) + if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck, + sizeof(message->MessageIntegrityCheck)) != 0) { WLog_ERR(TAG, "Message Integrity Check (MIC) verification failed!"); #ifdef WITH_DEBUG_NTLM @@ -1111,7 +1439,7 @@ SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context) */ #ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "No MIC present, using NtProofString for verification."); + WLog_VRB(TAG, "No MIC present, using NtProofString for verification."); #endif if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0) @@ -1129,38 +1457,21 @@ SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context) } /* Generate signing keys */ - ntlm_generate_client_signing_key(context); - ntlm_generate_server_signing_key(context); + if (!ntlm_generate_client_signing_key(context)) + return SEC_E_INTERNAL_ERROR; + if (!ntlm_generate_server_signing_key(context)) + return SEC_E_INTERNAL_ERROR; /* Generate sealing keys */ - ntlm_generate_client_sealing_key(context); - ntlm_generate_server_sealing_key(context); + if (!ntlm_generate_client_sealing_key(context)) + return SEC_E_INTERNAL_ERROR; + if (!ntlm_generate_server_sealing_key(context)) + return SEC_E_INTERNAL_ERROR; /* Initialize RC4 seal state */ ntlm_init_rc4_seal_states(context); -#ifdef WITH_DEBUG_NTLM - WLog_DBG(TAG, "ClientChallenge"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ClientChallenge, 8); - WLog_DBG(TAG, "ServerChallenge"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ServerChallenge, 8); - WLog_DBG(TAG, "SessionBaseKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->SessionBaseKey, 16); - WLog_DBG(TAG, "KeyExchangeKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->KeyExchangeKey, 16); - WLog_DBG(TAG, "ExportedSessionKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ExportedSessionKey, 16); - WLog_DBG(TAG, "RandomSessionKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->RandomSessionKey, 16); - WLog_DBG(TAG, "ClientSigningKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSigningKey, 16); - WLog_DBG(TAG, "ClientSealingKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSealingKey, 16); - WLog_DBG(TAG, "ServerSigningKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSigningKey, 16); - WLog_DBG(TAG, "ServerSealingKey"); - winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSealingKey, 16); - WLog_DBG(TAG, "Timestamp"); - winpr_HexDump(TAG, WLOG_DEBUG, context->Timestamp, 8); +#if defined(WITH_DEBUG_NTLM) + ntlm_print_authentication_complete(context); #endif - context->state = NTLM_STATE_FINAL; + ntlm_change_state(context, NTLM_STATE_FINAL); ntlm_free_message_fields_buffer(&(message->DomainName)); ntlm_free_message_fields_buffer(&(message->UserName)); ntlm_free_message_fields_buffer(&(message->Workstation)); diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.h b/winpr/libwinpr/sspi/NTLM/ntlm_message.h index 100d95a8ec08..58ff35dd5a6a 100644 --- a/winpr/libwinpr/sspi/NTLM/ntlm_message.h +++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.h @@ -23,12 +23,14 @@ #include "ntlm.h" SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer); -SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer); +SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer); SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer); -SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer); +SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, const PSecBuffer buffer); SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer); -SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer); +SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer); SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context); +const char* ntlm_get_negotiate_string(UINT32 flag); + #endif /* WINPR_SSPI_NTLM_MESSAGE_H */ diff --git a/winpr/libwinpr/sspi/test/TestNTLM.c b/winpr/libwinpr/sspi/test/TestNTLM.c index 82d266fe6031..9260bf0df647 100644 --- a/winpr/libwinpr/sspi/test/TestNTLM.c +++ b/winpr/libwinpr/sspi/test/TestNTLM.c @@ -467,18 +467,15 @@ void test_ntlm_server_free(TEST_NTLM_SERVER* ntlm) free(ntlm); } -int TestNTLM(int argc, char* argv[]) +static BOOL test_default(void) { int status; - int rc = -1; + BOOL rc = FALSE; PSecBuffer pSecBuffer; TEST_NTLM_CLIENT* client = NULL; TEST_NTLM_SERVER* server = NULL; BOOL DynamicTest = TRUE; - WINPR_UNUSED(argc); - WINPR_UNUSED(argv); - /** * Client Initialization */ @@ -684,7 +681,7 @@ int TestNTLM(int argc, char* argv[]) goto fail; } - rc = 0; + rc = TRUE; fail: /** @@ -694,3 +691,13 @@ int TestNTLM(int argc, char* argv[]) test_ntlm_server_free(server); return rc; } + +int TestNTLM(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_default()) + return -1; + return 0; +} diff --git a/winpr/libwinpr/sspicli/sspicli.c b/winpr/libwinpr/sspicli/sspicli.c index 039416994137..75585dd51efd 100644 --- a/winpr/libwinpr/sspicli/sspicli.c +++ b/winpr/libwinpr/sspicli/sspicli.c @@ -21,6 +21,7 @@ #include "config.h" #endif +#include #include /** @@ -60,6 +61,12 @@ #include #endif +#if defined(HAVE_GETPWUID_R) +#include +#include +#include +#endif + #include #include @@ -200,31 +207,38 @@ BOOL LogonUserExW(LPCWSTR lpszUsername, LPCWSTR lpszDomain, LPCWSTR lpszPassword BOOL GetUserNameExA(EXTENDED_NAME_FORMAT NameFormat, LPSTR lpNameBuffer, PULONG nSize) { - size_t length; - char login[MAX_PATH]; + WINPR_ASSERT(lpNameBuffer); + WINPR_ASSERT(nSize); switch (NameFormat) { case NameSamCompatible: -#ifndef HAVE_GETLOGIN_R - strncpy(login, getlogin(), sizeof(login)); +#if defined(HAVE_GETPWUID_R) + { + int rc; + struct passwd pwd = { 0 }; + struct passwd* result = NULL; + uid_t uid = getuid(); + + rc = getpwuid_r(uid, &pwd, lpNameBuffer, *nSize, &result); + if (rc != 0) + return FALSE; + if (result == NULL) + return FALSE; + } +#elif defined(HAVE_GETLOGIN_R) + if (getlogin_r(lpNameBuffer, *nSize) != 0) + return FALSE; #else - if (getlogin_r(login, sizeof(login)) != 0) + { + const char* name = getlogin(); + if (!name) return FALSE; + strncpy(lpNameBuffer, name, strnlen(name, *nSize)); + } #endif - length = strlen(login); - - if (*nSize >= length) - { - CopyMemory(lpNameBuffer, login, length + 1); - return TRUE; - } - else - { - *nSize = length + 1; - } - - break; + *nSize = strnlen(lpNameBuffer, *nSize); + return TRUE; case NameFullyQualifiedDN: case NameDisplay: @@ -245,7 +259,29 @@ BOOL GetUserNameExA(EXTENDED_NAME_FORMAT NameFormat, LPSTR lpNameBuffer, PULONG BOOL GetUserNameExW(EXTENDED_NAME_FORMAT NameFormat, LPWSTR lpNameBuffer, PULONG nSize) { - return 0; + int res; + BOOL rc = FALSE; + char* name; + + WINPR_ASSERT(nSize); + WINPR_ASSERT(lpNameBuffer); + + name = calloc(1, *nSize + 1); + if (!name) + goto fail; + + if (!GetUserNameExA(NameFormat, name, nSize)) + goto fail; + + res = ConvertToUnicode(CP_UTF8, 0, name, -1, &lpNameBuffer, *nSize); + if (res < 0) + goto fail; + + *nSize = res + 1; + rc = TRUE; +fail: + free(name); + return rc; } #endif diff --git a/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c b/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c index b0bf8f212031..30086d4329f0 100644 --- a/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c +++ b/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c @@ -1,15 +1,14 @@ - #include #include #include #include -#define THREADS 24 +#define THREADS 8 static DWORD WINAPI test_thread(LPVOID arg) { - long timeout = 100 + (rand() % 1000); + long timeout = 30 + (rand() % 100); WINPR_UNUSED(arg); Sleep(timeout); ExitThread(0); @@ -22,7 +21,7 @@ static int start_threads(DWORD count, HANDLE* threads) for (i = 0; i < count; i++) { - threads[i] = CreateThread(NULL, 0, test_thread, NULL, 0, NULL); + threads[i] = CreateThread(NULL, 0, test_thread, NULL, CREATE_SUSPENDED, NULL); if (!threads[i]) { @@ -31,38 +30,45 @@ static int start_threads(DWORD count, HANDLE* threads) } } + for (i = 0; i < count; i++) + ResumeThread(threads[i]); return 0; } static int close_threads(DWORD count, HANDLE* threads) { + int rc = 0; DWORD i; for (i = 0; i < count; i++) { + if (!threads[i]) + continue; + if (!CloseHandle(threads[i])) { fprintf(stderr, "%s: CloseHandle [%" PRIu32 "] failure\n", __FUNCTION__, i); - return -1; + rc = -1; } + threads[i] = NULL; } - return 0; + return rc; } static BOOL TestWaitForAll(void) { BOOL rc = FALSE; DWORD ret; - HANDLE threads[THREADS]; + HANDLE threads[THREADS] = { 0 }; /* WaitForAll, timeout */ if (start_threads(THREADS, threads)) { fprintf(stderr, "%s: start_threads failed\n", __FUNCTION__); - return FALSE; + goto fail; } - ret = WaitForMultipleObjects(THREADS, threads, TRUE, 50); + ret = WaitForMultipleObjects(THREADS, threads, TRUE, 10); if (ret != WAIT_TIMEOUT) { fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, timeout 50 failed, ret=%d\n", @@ -76,14 +82,14 @@ static BOOL TestWaitForAll(void) goto fail; } + rc = TRUE; +fail: if (close_threads(THREADS, threads)) { fprintf(stderr, "%s: close_threads failed\n", __FUNCTION__); return FALSE; } - rc = TRUE; -fail: return rc; } @@ -91,12 +97,12 @@ static BOOL TestWaitOne(void) { BOOL rc = FALSE; DWORD ret; - HANDLE threads[THREADS]; + HANDLE threads[THREADS] = { 0 }; /* WaitForAll, timeout */ if (start_threads(THREADS, threads)) { fprintf(stderr, "%s: start_threads failed\n", __FUNCTION__); - return FALSE; + goto fail; } ret = WaitForMultipleObjects(THREADS, threads, FALSE, INFINITE); @@ -112,14 +118,14 @@ static BOOL TestWaitOne(void) goto fail; } + rc = TRUE; +fail: if (close_threads(THREADS, threads)) { fprintf(stderr, "%s: close_threads failed\n", __FUNCTION__); return FALSE; } - rc = TRUE; -fail: return rc; } @@ -127,15 +133,15 @@ static BOOL TestWaitOneTimeout(void) { BOOL rc = FALSE; DWORD ret; - HANDLE threads[THREADS]; + HANDLE threads[THREADS] = { 0 }; /* WaitForAll, timeout */ if (start_threads(THREADS, threads)) { fprintf(stderr, "%s: start_threads failed\n", __FUNCTION__); - return FALSE; + goto fail; } - ret = WaitForMultipleObjects(THREADS, threads, FALSE, 50); + ret = WaitForMultipleObjects(THREADS, threads, FALSE, 1); if (ret != WAIT_TIMEOUT) { fprintf(stderr, "%s: WaitForMultipleObjects timeout 50 failed, ret=%d\n", __FUNCTION__, @@ -148,15 +154,14 @@ static BOOL TestWaitOneTimeout(void) fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __FUNCTION__); goto fail; } - + rc = TRUE; +fail: if (close_threads(THREADS, threads)) { fprintf(stderr, "%s: close_threads failed\n", __FUNCTION__); return FALSE; } - rc = TRUE; -fail: return rc; } @@ -164,12 +169,12 @@ static BOOL TestWaitOneTimeoutMultijoin(void) { BOOL rc = FALSE; DWORD ret, i; - HANDLE threads[THREADS]; + HANDLE threads[THREADS] = { 0 }; /* WaitForAll, timeout */ if (start_threads(THREADS, threads)) { fprintf(stderr, "%s: start_threads failed\n", __FUNCTION__); - return FALSE; + goto fail; } for (i = 0; i < THREADS; i++) @@ -177,7 +182,7 @@ static BOOL TestWaitOneTimeoutMultijoin(void) ret = WaitForMultipleObjects(THREADS, threads, FALSE, 0); if (ret != WAIT_TIMEOUT) { - fprintf(stderr, "%s: WaitForMultipleObjects timeout 50 failed, ret=%d\n", __FUNCTION__, + fprintf(stderr, "%s: WaitForMultipleObjects timeout 0 failed, ret=%d\n", __FUNCTION__, ret); goto fail; } @@ -189,34 +194,37 @@ static BOOL TestWaitOneTimeoutMultijoin(void) goto fail; } + rc = TRUE; +fail: if (close_threads(THREADS, threads)) { fprintf(stderr, "%s: close_threads failed\n", __FUNCTION__); return FALSE; } - rc = TRUE; -fail: return rc; } static BOOL TestDetach(void) { - HANDLE threads[THREADS]; + BOOL rc = FALSE; + HANDLE threads[THREADS] = { 0 }; /* WaitForAll, timeout */ if (start_threads(THREADS, threads)) { fprintf(stderr, "%s: start_threads failed\n", __FUNCTION__); - return FALSE; + goto fail; } + rc = TRUE; +fail: if (close_threads(THREADS, threads)) { fprintf(stderr, "%s: close_threads failed\n", __FUNCTION__); return FALSE; } - return TRUE; + return rc; } int TestSynchMultipleThreads(int argc, char* argv[]) diff --git a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c index a5a0bf19a123..d8d59d0c2c6d 100644 --- a/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c +++ b/winpr/libwinpr/sysinfo/cpufeatures/cpu-features.c @@ -69,6 +69,7 @@ #include #include #include +#include #include #include #include diff --git a/winpr/libwinpr/thread/test/TestThreadCreateProcess.c b/winpr/libwinpr/thread/test/TestThreadCreateProcess.c index 61659fe70844..7d662e207796 100644 --- a/winpr/libwinpr/thread/test/TestThreadCreateProcess.c +++ b/winpr/libwinpr/thread/test/TestThreadCreateProcess.c @@ -15,7 +15,13 @@ int TestThreadCreateProcess(int argc, char* argv[]) BOOL status; DWORD exitCode; LPCTSTR lpApplicationName; - LPTSTR lpCommandLine; + +#ifdef _WIN32 + TCHAR lpCommandLine[200] = _T("cmd /C set"); +#else + TCHAR lpCommandLine[200] = _T("printenv"); +#endif + LPSECURITY_ATTRIBUTES lpProcessAttributes; LPSECURITY_ATTRIBUTES lpThreadAttributes; BOOL bInheritHandles; @@ -36,12 +42,6 @@ int TestThreadCreateProcess(int argc, char* argv[]) lpApplicationName = NULL; -#ifdef _WIN32 - lpCommandLine = _T("cmd /C set"); -#else - lpCommandLine = _T("printenv"); -#endif - lpProcessAttributes = NULL; lpThreadAttributes = NULL; bInheritHandles = FALSE; diff --git a/winpr/libwinpr/thread/thread.c b/winpr/libwinpr/thread/thread.c index e671b6490458..3b95d1def216 100644 --- a/winpr/libwinpr/thread/thread.c +++ b/winpr/libwinpr/thread/thread.c @@ -24,7 +24,7 @@ #include "config.h" #endif -#include +#include #include @@ -98,7 +98,10 @@ #define TAG WINPR_TAG("thread") static WINPR_THREAD mainThread; + +#if defined(WITH_THREAD_LIST) static wListDictionary* thread_list = NULL; +#endif static BOOL ThreadCloseHandle(HANDLE handle); static void cleanup_handle(void* obj); @@ -126,14 +129,190 @@ static int ThreadGetFd(HANDLE handle) return pThread->event.fds[0]; } +#define run_mutex_init(fkt, mux, arg) run_mutex_init_(fkt, #fkt, mux, arg) +static BOOL run_mutex_init_(int (*fkt)(pthread_mutex_t*, const pthread_mutexattr_t*), + const char* name, pthread_mutex_t* mutex, + const pthread_mutexattr_t* mutexattr) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(mutex); + + rc = fkt(mutex, mutexattr); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +#define run_mutex_fkt(fkt, mux) run_mutex_fkt_(fkt, #fkt, mux) +static BOOL run_mutex_fkt_(int (*fkt)(pthread_mutex_t* mux), const char* name, + pthread_mutex_t* mutex) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(mutex); + + rc = fkt(mutex); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +#define run_cond_init(fkt, cond, arg) run_cond_init_(fkt, #fkt, cond, arg) +static BOOL run_cond_init_(int (*fkt)(pthread_cond_t*, const pthread_condattr_t*), const char* name, + pthread_cond_t* condition, const pthread_condattr_t* conditionattr) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(condition); + + rc = fkt(condition, conditionattr); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +#define run_cond_fkt(fkt, cond) run_cond_fkt_(fkt, #fkt, cond) +static BOOL run_cond_fkt_(int (*fkt)(pthread_cond_t* mux), const char* name, + pthread_cond_t* condition) +{ + int rc; + + WINPR_ASSERT(fkt); + WINPR_ASSERT(condition); + + rc = fkt(condition); + if (rc != 0) + { + WLog_WARN(TAG, "[%s] failed with [%s]", name, strerror(rc)); + } + return rc == 0; +} + +static int pthread_mutex_checked_unlock(pthread_mutex_t* mutex) +{ + WINPR_ASSERT(mutex); + WINPR_ASSERT(pthread_mutex_trylock(mutex) == EBUSY); + return pthread_mutex_unlock(mutex); +} + +static BOOL mux_condition_bundle_init(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + + bundle->val = FALSE; + if (!run_mutex_init(pthread_mutex_init, &bundle->mux, NULL)) + return FALSE; + + if (!run_cond_init(pthread_cond_init, &bundle->cond, NULL)) + return FALSE; + return TRUE; +} + +static void mux_condition_bundle_uninit(mux_condition_bundle* bundle) +{ + mux_condition_bundle empty = { 0 }; + + WINPR_ASSERT(bundle); + + run_cond_fkt(pthread_cond_destroy, &bundle->cond); + run_mutex_fkt(pthread_mutex_destroy, &bundle->mux); + *bundle = empty; +} + +static BOOL mux_condition_bundle_signal(mux_condition_bundle* bundle) +{ + BOOL rc = TRUE; + WINPR_ASSERT(bundle); + + if (!run_mutex_fkt(pthread_mutex_lock, &bundle->mux)) + return FALSE; + bundle->val = TRUE; + if (!run_cond_fkt(pthread_cond_signal, &bundle->cond)) + rc = FALSE; + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux)) + rc = FALSE; + return rc; +} + +static BOOL mux_condition_bundle_lock(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + return run_mutex_fkt(pthread_mutex_lock, &bundle->mux); +} + +static BOOL mux_condition_bundle_unlock(mux_condition_bundle* bundle) +{ + WINPR_ASSERT(bundle); + return run_mutex_fkt(pthread_mutex_checked_unlock, &bundle->mux); +} + +static BOOL mux_condition_bundle_wait(mux_condition_bundle* bundle, const char* name) +{ + BOOL rc = FALSE; + + WINPR_ASSERT(bundle); + WINPR_ASSERT(name); + WINPR_ASSERT(pthread_mutex_trylock(&bundle->mux) == EBUSY); + + while (!bundle->val) + { + int r = pthread_cond_wait(&bundle->cond, &bundle->mux); + if (r != 0) + { + WLog_ERR(TAG, "failed to wait for %s [%s]", name, strerror(r)); + switch (r) + { + case ENOTRECOVERABLE: + case EPERM: + case ETIMEDOUT: + case EINVAL: + goto fail; + + default: + break; + } + } + } + + rc = bundle->val; + +fail: + return rc; +} + +static BOOL signal_thread_ready(WINPR_THREAD* thread) +{ + WINPR_ASSERT(thread); + + return mux_condition_bundle_signal(&thread->isCreated); +} + +static BOOL signal_thread_is_running(WINPR_THREAD* thread) +{ + WINPR_ASSERT(thread); + + return mux_condition_bundle_signal(&thread->isRunning); +} + static DWORD ThreadCleanupHandle(HANDLE handle) { + DWORD status = WAIT_FAILED; WINPR_THREAD* thread = (WINPR_THREAD*)handle; if (!ThreadIsHandled(handle)) return WAIT_FAILED; - if (pthread_mutex_lock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) return WAIT_FAILED; if (!thread->joined) @@ -144,17 +323,19 @@ static DWORD ThreadCleanupHandle(HANDLE handle) if (status != 0) { WLog_ERR(TAG, "pthread_join failure: [%d] %s", status, strerror(status)); - pthread_mutex_unlock(&thread->mutex); - return WAIT_FAILED; + goto fail; } else thread->joined = TRUE; } - if (pthread_mutex_unlock(&thread->mutex)) + status = WAIT_OBJECT_0; + +fail: + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) return WAIT_FAILED; - return WAIT_OBJECT_0; + return status; } static HANDLE_OPS ops = { ThreadIsHandled, @@ -218,7 +399,8 @@ static void dump_thread(WINPR_THREAD* thread) free(msg); } - +#else + WINPR_UNUSED(thread); #endif } @@ -236,6 +418,7 @@ static BOOL reset_event(WINPR_THREAD* thread) return winpr_event_reset(&thread->event); } +#if defined(WITH_THREAD_LIST) static BOOL thread_compare(const void* a, const void* b) { const pthread_t* p1 = a; @@ -243,12 +426,13 @@ static BOOL thread_compare(const void* a, const void* b) BOOL rc = pthread_equal(*p1, *p2); return rc; } +#endif static INIT_ONCE threads_InitOnce = INIT_ONCE_STATIC_INIT; static pthread_t mainThreadId; static DWORD currentThreadTlsIndex = TLS_OUT_OF_INDEXES; -BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) +static BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) { if (!apc_init(&mainThread.apc)) { @@ -265,10 +449,54 @@ BOOL initializeThreads(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context) WLog_ERR(TAG, "Major bug, unable to allocate a TLS value for currentThread"); } +#if defined(WITH_THREAD_LIST) + thread_list = ListDictionary_New(TRUE); + + if (!thread_list) + { + WLog_ERR(TAG, "Couldn't create global thread list"); + goto error_thread_list; + } + + thread_list->objectKey.fnObjectEquals = thread_compare; +#endif + out: return TRUE; } +static BOOL signal_and_wait_for_ready(WINPR_THREAD* thread) +{ + BOOL res = FALSE; + + WINPR_ASSERT(thread); + + if (!mux_condition_bundle_lock(&thread->isRunning)) + return FALSE; + + if (!signal_thread_ready(thread)) + goto fail; + + if (!mux_condition_bundle_wait(&thread->isRunning, "threadIsRunning")) + goto fail; + +#if defined(WITH_THREAD_LIST) + if (!ListDictionary_Contains(thread_list, &thread->thread)) + { + WLog_ERR(TAG, "Thread not in thread_list, startup failed!"); + goto fail; + } +#endif + + res = TRUE; + +fail: + if (!mux_condition_bundle_unlock(&thread->isRunning)) + return FALSE; + + return res; +} + /* Thread launcher function responsible for registering * cleanup handlers and calling pthread_exit, if not done * in thread function. */ @@ -296,23 +524,9 @@ static void* thread_launcher(void* arg) goto exit; } - if (pthread_mutex_lock(&thread->threadIsReadyMutex)) + if (!signal_and_wait_for_ready(thread)) goto exit; - if (!ListDictionary_Contains(thread_list, &thread->thread)) - { - if (pthread_cond_wait(&thread->threadIsReady, &thread->threadIsReadyMutex) != 0) - { - WLog_ERR(TAG, "The thread could not be made ready"); - pthread_mutex_unlock(&thread->threadIsReadyMutex); - goto exit; - } - } - - if (pthread_mutex_unlock(&thread->threadIsReadyMutex)) - goto exit; - - assert(ListDictionary_Contains(thread_list, &thread->thread)); rc = fkt(thread->lpParameter); exit: @@ -325,6 +539,8 @@ static void* thread_launcher(void* arg) set_event(thread); + signal_thread_ready(thread); + if (thread->detached || !thread->started) cleanup_handle(thread); } @@ -334,7 +550,14 @@ static void* thread_launcher(void* arg) static BOOL winpr_StartThread(WINPR_THREAD* thread) { - pthread_attr_t attr; + BOOL rc = FALSE; + BOOL locked = FALSE; + pthread_attr_t attr = { 0 }; + + if (!mux_condition_bundle_lock(&thread->isCreated)) + return FALSE; + locked = TRUE; + pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); @@ -344,35 +567,44 @@ static BOOL winpr_StartThread(WINPR_THREAD* thread) thread->started = TRUE; reset_event(thread); +#if defined(WITH_THREAD_LIST) + if (!ListDictionary_Add(thread_list, &thread->thread, thread)) + { + WLog_ERR(TAG, "failed to add the thread to the thread list"); + goto error; + } +#endif + if (pthread_create(&thread->thread, &attr, thread_launcher, thread)) goto error; - if (pthread_mutex_lock(&thread->threadIsReadyMutex)) + if (!mux_condition_bundle_wait(&thread->isCreated, "threadIsCreated")) goto error; - if (!ListDictionary_Add(thread_list, &thread->thread, thread)) - { - WLog_ERR(TAG, "failed to add the thread to the thread list"); - pthread_mutex_unlock(&thread->threadIsReadyMutex); + locked = FALSE; + if (!mux_condition_bundle_unlock(&thread->isCreated)) goto error; - } - if (pthread_cond_signal(&thread->threadIsReady) != 0) + if (!signal_thread_is_running(thread)) { WLog_ERR(TAG, "failed to signal the thread was ready"); - pthread_mutex_unlock(&thread->threadIsReadyMutex); goto error; } - if (pthread_mutex_unlock(&thread->threadIsReadyMutex)) - goto error; - - pthread_attr_destroy(&attr); - dump_thread(thread); - return TRUE; + rc = TRUE; error: + if (locked) + { + if (!mux_condition_bundle_unlock(&thread->isCreated)) + rc = FALSE; + } + pthread_attr_destroy(&attr); - return FALSE; + + if (rc) + dump_thread(thread); + + return rc; } HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, @@ -380,8 +612,7 @@ HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize DWORD dwCreationFlags, LPDWORD lpThreadId) { HANDLE handle; - WINPR_THREAD* thread; - thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD)); + WINPR_THREAD* thread = (WINPR_THREAD*)calloc(1, sizeof(WINPR_THREAD)); if (!thread) return NULL; @@ -399,104 +630,66 @@ HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize if (!winpr_event_init(&thread->event)) { WLog_ERR(TAG, "failed to create event"); - goto error_event; + goto fail; } - if (pthread_mutex_init(&thread->mutex, NULL) != 0) + if (!run_mutex_init(pthread_mutex_init, &thread->mutex, NULL)) { WLog_ERR(TAG, "failed to initialize thread mutex"); - goto error_mutex; + goto fail; } if (!apc_init(&thread->apc)) { WLog_ERR(TAG, "failed to initialize APC"); - goto error_APC; + goto fail; } - if (pthread_mutex_init(&thread->threadIsReadyMutex, NULL) != 0) - { - WLog_ERR(TAG, "failed to initialize a mutex for a condition variable"); - goto error_thread_ready_mutex; - } - - if (pthread_cond_init(&thread->threadIsReady, NULL) != 0) - { - WLog_ERR(TAG, "failed to initialize a condition variable"); - goto error_thread_ready; - } + if (!mux_condition_bundle_init(&thread->isCreated)) + goto fail; + if (!mux_condition_bundle_init(&thread->isRunning)) + goto fail; WINPR_HANDLE_SET_TYPE_AND_MODE(thread, HANDLE_TYPE_THREAD, WINPR_FD_READ); handle = (HANDLE)thread; - if (!thread_list) - { - InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL); - thread_list = ListDictionary_New(TRUE); - - if (!thread_list) - { - WLog_ERR(TAG, "Couldn't create global thread list"); - goto error_thread_list; - } - - thread_list->objectKey.fnObjectEquals = thread_compare; - } + InitOnceExecuteOnce(&threads_InitOnce, initializeThreads, NULL, NULL); if (!(dwCreationFlags & CREATE_SUSPENDED)) { if (!winpr_StartThread(thread)) - goto error_thread_list; + goto fail; } else { if (!set_event(thread)) - goto error_thread_list; + goto fail; } return handle; -error_thread_list: - pthread_cond_destroy(&thread->threadIsReady); -error_thread_ready: - pthread_mutex_destroy(&thread->threadIsReadyMutex); -error_thread_ready_mutex: - apc_uninit(&thread->apc); -error_APC: - pthread_mutex_destroy(&thread->mutex); -error_mutex: - winpr_event_uninit(&thread->event); -error_event: - free(thread); +fail: + cleanup_handle(thread); return NULL; } void cleanup_handle(void* obj) { - int rc; WINPR_THREAD* thread = (WINPR_THREAD*)obj; + if (!thread) + return; if (!apc_uninit(&thread->apc)) WLog_ERR(TAG, "failed to destroy APC"); - rc = pthread_cond_destroy(&thread->threadIsReady); - if (rc) - WLog_ERR(TAG, "failed to destroy thread->threadIsReady [%d] %s (%d)", rc, strerror(errno), - errno); - - rc = pthread_mutex_destroy(&thread->threadIsReadyMutex); - if (rc) - WLog_ERR(TAG, "failed to destroy thread->threadIsReadyMutex [%d] %s (%d)", rc, - strerror(errno), errno); - - rc = pthread_mutex_destroy(&thread->mutex); - if (rc) - WLog_ERR(TAG, "failed to destroy thread->mutex [%d] %s (%d)", rc, strerror(errno), errno); + mux_condition_bundle_uninit(&thread->isCreated); + mux_condition_bundle_uninit(&thread->isRunning); + run_mutex_fkt(pthread_mutex_destroy, &thread->mutex); winpr_event_uninit(&thread->event); - if (thread_list && ListDictionary_Contains(thread_list, &thread->thread)) - ListDictionary_Remove(thread_list, &thread->thread); - +#if defined(WITH_THREAD_LIST) + ListDictionary_Remove(thread_list, &thread->thread); +#endif #if defined(WITH_DEBUG_THREADS) if (thread->create_stack) @@ -513,6 +706,7 @@ BOOL ThreadCloseHandle(HANDLE handle) { WINPR_THREAD* thread = (WINPR_THREAD*)handle; +#if defined(WITH_THREAD_LIST) if (!thread_list) { WLog_ERR(TAG, "Thread list does not exist, check call!"); @@ -526,6 +720,7 @@ BOOL ThreadCloseHandle(HANDLE handle) else { ListDictionary_Lock(thread_list); +#endif dump_thread(thread); if ((thread->started) && (WaitForSingleObject(thread, 0) != WAIT_OBJECT_0)) @@ -539,14 +734,10 @@ BOOL ThreadCloseHandle(HANDLE handle) cleanup_handle(thread); } +#if defined(WITH_THREAD_LIST) ListDictionary_Unlock(thread_list); - - if (ListDictionary_Count(thread_list) < 1) - { - ListDictionary_Free(thread_list); - thread_list = NULL; - } } +#endif return TRUE; } @@ -562,6 +753,7 @@ HANDLE CreateRemoteThread(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttribu VOID ExitThread(DWORD dwExitCode) { +#if defined(WITH_THREAD_LIST) DWORD rc; pthread_t tid = pthread_self(); @@ -586,7 +778,7 @@ VOID ExitThread(DWORD dwExitCode) WINPR_THREAD* thread; ListDictionary_Lock(thread_list); thread = ListDictionary_GetItemValue(thread_list, &tid); - assert(thread); + WINPR_ASSERT(thread); thread->exited = TRUE; thread->dwExitCode = dwExitCode; #if defined(WITH_DEBUG_THREADS) @@ -601,6 +793,9 @@ VOID ExitThread(DWORD dwExitCode) pthread_exit((void*)(size_t)rc); } +#else + WINPR_UNUSED(dwExitCode); +#endif } BOOL GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) @@ -657,7 +852,7 @@ typedef struct ULONG_PTR completionArg; } UserApcItem; -void userAPC(LPVOID arg) +static void userAPC(LPVOID arg) { UserApcItem* userApc = (UserApcItem*)arg; @@ -670,7 +865,6 @@ DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData) { ULONG Type; WINPR_HANDLE* Object; - WINPR_THREAD* thread; WINPR_APC_ITEM* apc; UserApcItem* apcItem; @@ -683,7 +877,6 @@ DWORD QueueUserAPC(PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData) SetLastError(ERROR_INVALID_PARAMETER); return (DWORD)0; } - thread = (WINPR_THREAD*)Object; apcItem = calloc(1, sizeof(*apcItem)); if (!apcItem) @@ -715,21 +908,21 @@ DWORD ResumeThread(HANDLE hThread) thread = (WINPR_THREAD*)Object; - if (pthread_mutex_lock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) return (DWORD)-1; if (!thread->started) { if (!winpr_StartThread(thread)) { - pthread_mutex_unlock(&thread->mutex); + run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex); return (DWORD)-1; } } else WLog_WARN(TAG, "Thread already started!"); - if (pthread_mutex_unlock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) return (DWORD)-1; return 0; @@ -767,7 +960,7 @@ BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) thread->exited = TRUE; thread->dwExitCode = dwExitCode; - if (pthread_mutex_lock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_lock, &thread->mutex)) return FALSE; #ifndef ANDROID @@ -776,16 +969,16 @@ BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode) WLog_ERR(TAG, "Function not supported on this platform!"); #endif - if (pthread_mutex_unlock(&thread->mutex)) + if (!run_mutex_fkt(pthread_mutex_checked_unlock, &thread->mutex)) return FALSE; set_event(thread); return TRUE; } -#if defined(WITH_DEBUG_THREADS) VOID DumpThreadHandles(void) { +#if defined(WITH_DEBUG_THREADS) char** msg; size_t used, i; void* stack = winpr_backtrace(20); @@ -801,6 +994,7 @@ VOID DumpThreadHandles(void) winpr_backtrace_free(stack); WLog_DBG(TAG, "---------------- Start Dumping thread handles -----------"); +#if defined(WITH_THREAD_LIST) if (!thread_list) { WLog_DBG(TAG, "All threads properly shut down and disposed of."); @@ -844,8 +1038,9 @@ VOID DumpThreadHandles(void) free(keys); ListDictionary_Unlock(thread_list); } +#endif WLog_DBG(TAG, "---------------- End Dumping thread handles -------------"); -} #endif +} #endif diff --git a/winpr/libwinpr/thread/thread.h b/winpr/libwinpr/thread/thread.h index f187e263093b..a533685e6585 100644 --- a/winpr/libwinpr/thread/thread.h +++ b/winpr/libwinpr/thread/thread.h @@ -32,47 +32,63 @@ #include "../synch/event.h" #include "apc.h" +#ifdef __GNUC__ +#define ALIGN64 __attribute__((aligned(8))) +#else +#ifdef _WIN32 +#define ALIGN64 __declspec(align(8)) +#else +#define ALIGN64 +#endif +#endif + typedef void* (*pthread_start_routine)(void*); typedef struct winpr_APC_item WINPR_APC_ITEM; +typedef struct +{ + ALIGN64 pthread_mutex_t mux; + ALIGN64 pthread_cond_t cond; + ALIGN64 BOOL val; +} mux_condition_bundle; + struct winpr_thread { WINPR_HANDLE_DEF(); - BOOL started; - WINPR_EVENT_IMPL event; - BOOL mainProcess; - BOOL detached; - BOOL joined; - BOOL exited; - DWORD dwExitCode; - pthread_t thread; - SIZE_T dwStackSize; - LPVOID lpParameter; - pthread_mutex_t mutex; - pthread_mutex_t threadIsReadyMutex; - pthread_cond_t threadIsReady; - LPTHREAD_START_ROUTINE lpStartAddress; - LPSECURITY_ATTRIBUTES lpThreadAttributes; - APC_QUEUE apc; + ALIGN64 BOOL started; + ALIGN64 WINPR_EVENT_IMPL event; + ALIGN64 BOOL mainProcess; + ALIGN64 BOOL detached; + ALIGN64 BOOL joined; + ALIGN64 BOOL exited; + ALIGN64 DWORD dwExitCode; + ALIGN64 pthread_t thread; + ALIGN64 SIZE_T dwStackSize; + ALIGN64 LPVOID lpParameter; + ALIGN64 pthread_mutex_t mutex; + mux_condition_bundle isRunning; + mux_condition_bundle isCreated; + ALIGN64 LPTHREAD_START_ROUTINE lpStartAddress; + ALIGN64 LPSECURITY_ATTRIBUTES lpThreadAttributes; + ALIGN64 APC_QUEUE apc; #if defined(WITH_DEBUG_THREADS) - void* create_stack; - void* exit_stack; + ALIGN64 void* create_stack; + ALIGN64 void* exit_stack; #endif }; typedef struct winpr_thread WINPR_THREAD; WINPR_THREAD* winpr_GetCurrentThread(VOID); -struct winpr_process +typedef struct { WINPR_HANDLE_DEF(); pid_t pid; int status; DWORD dwExitCode; -}; -typedef struct winpr_process WINPR_PROCESS; +} WINPR_PROCESS; #endif diff --git a/winpr/libwinpr/utils/CMakeLists.txt b/winpr/libwinpr/utils/CMakeLists.txt index 2027a607520b..b1acf9a9336a 100644 --- a/winpr/libwinpr/utils/CMakeLists.txt +++ b/winpr/libwinpr/utils/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(${MODULE_PREFIX}_COLLECTIONS_SRCS +set(COLLECTIONS_SRCS collections/Queue.c collections/Stack.c collections/PubSub.c @@ -35,11 +35,11 @@ set(${MODULE_PREFIX}_COLLECTIONS_SRCS collections/MessageQueue.c collections/MessagePipe.c) -set(${MODULE_PREFIX}_LODEPNG_SRCS +set(LODEPNG_SRCS lodepng/lodepng.c lodepng/lodepng.h) -set(${MODULE_PREFIX}_TRIO_SRCS +set(TRIO_SRCS trio/strio.h trio/trio.c trio/trio.h @@ -67,7 +67,7 @@ if (LIBSYSTEMD_FOUND) winpr_library_add_private(${LIBSYSTEMD_LIBRARY}) endif() -set(${MODULE_PREFIX}_WLOG_SRCS +set(WLOG_SRCS wlog/wlog.c wlog/wlog.h wlog/Layout.c @@ -97,7 +97,7 @@ set(${MODULE_PREFIX}_WLOG_SRCS ) -set(${MODULE_PREFIX}_SRCS +set(SRCS ini.c sam.c ntlm.c @@ -112,13 +112,45 @@ set(${MODULE_PREFIX}_SRCS if (ANDROID) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + if (NOT HAVE_UNWIND_H) + message("[backtrace] android NDK without unwind.h, falling back to corkscrew") + set(HAVE_CORKSCREW 1) + endif() endif() -winpr_module_add(${${MODULE_PREFIX}_SRCS} - ${${MODULE_PREFIX}_COLLECTIONS_SRCS} - ${${MODULE_PREFIX}_LODEPNG_SRCS} - ${${MODULE_PREFIX}_TRIO_SRCS} - ${${MODULE_PREFIX}_WLOG_SRCS}) +if (HAVE_CORKSCREW) + list(APPEND SRCS + corkscrew/debug.c + corkscrew/debug.h) +endif() + +if (WIN32) + list(APPEND SRCS + windows/debug.c + windows/debug.h) +endif() + +if (HAVE_EXECINFO_H) + list(APPEND SRCS + execinfo/debug.c + execinfo/debug.h) +endif() + +if (HAVE_UNWIND_H) + option(USE_UNWIND "Use unwind.h to generate backtraces" ON) + if (USE_UNWIND) + add_definitions(-DUSE_UNWIND) + list(APPEND SRCS + unwind/debug.c + unwind/debug.h) + endif() +endif() + +winpr_module_add(${SRCS} + ${COLLECTIONS_SRCS} + ${LODEPNG_SRCS} + ${TRIO_SRCS} + ${WLOG_SRCS}) winpr_include_directory_add( "lodepng" diff --git a/winpr/libwinpr/utils/collections/ArrayList.c b/winpr/libwinpr/utils/collections/ArrayList.c index f8fc3df065d0..cb7e39a35e7c 100644 --- a/winpr/libwinpr/utils/collections/ArrayList.c +++ b/winpr/libwinpr/utils/collections/ArrayList.c @@ -233,6 +233,18 @@ BOOL ArrayList_Contains(wArrayList* arrayList, void* obj) * Adds an object to the end of the ArrayList. */ +static int insert(wArrayList* arrayList, size_t index, void* obj) +{ + if (arrayList->object.fnObjectNew) + { + obj = arrayList->object.fnObjectNew(obj); + arrayList->array[index] = obj; + } + else + arrayList->array[index] = obj; + return index + 1; +} + int ArrayList_Add(wArrayList* arrayList, void* obj) { int index = -1; @@ -253,8 +265,7 @@ int ArrayList_Add(wArrayList* arrayList, void* obj) arrayList->capacity = newCapacity; } - arrayList->array[arrayList->size++] = obj; - index = arrayList->size; + index = arrayList->size = insert(arrayList, arrayList->size, obj); out: if (arrayList->synchronized) @@ -282,7 +293,7 @@ BOOL ArrayList_Insert(wArrayList* arrayList, int index, void* obj) } else { - arrayList->array[index] = obj; + insert(arrayList, index, obj); } } diff --git a/winpr/libwinpr/utils/collections/MessageQueue.c b/winpr/libwinpr/utils/collections/MessageQueue.c index d787f8ff3645..9d1d5d3c1d51 100644 --- a/winpr/libwinpr/utils/collections/MessageQueue.c +++ b/winpr/libwinpr/utils/collections/MessageQueue.c @@ -67,38 +67,77 @@ BOOL MessageQueue_Wait(wMessageQueue* queue) return status; } -BOOL MessageQueue_Dispatch(wMessageQueue* queue, wMessage* message) +static BOOL MessageQueue_EnsureCapacity(wMessageQueue* queue, size_t count) { - BOOL ret = FALSE; - if (!queue || !message) - return FALSE; + const size_t increment = 128; + WINPR_ASSERT(queue); - EnterCriticalSection(&queue->lock); + const size_t required = queue->size + count; + // check for overflow + if ((required < queue->size) || (required < count) || + (required > (SIZE_MAX - increment) / sizeof(wMessage))) + return FALSE; - if (queue->size == queue->capacity) + if (required > queue->capacity) { - int old_capacity; - int new_capacity; - wMessage* new_arr; + const size_t old_capacity = queue->capacity; + const size_t new_capacity = required + increment; - old_capacity = queue->capacity; - new_capacity = queue->capacity * 2; - - new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity); + wMessage* new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity); if (!new_arr) - goto out; + return FALSE; queue->array = new_arr; queue->capacity = new_capacity; ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(wMessage)); - /* rearrange wrapped entries */ + /* rearrange wrapped entries: + * fill up the newly available space and move tail + * back by the amount of elements that have been moved to the newly + * allocated space. + */ if (queue->tail <= queue->head) { - CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(wMessage)); - queue->tail += old_capacity; + size_t tocopy = queue->tail; + size_t slots = new_capacity - old_capacity; + const size_t batch = (tocopy < slots) ? tocopy : slots; + CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(wMessage)); + + /* Tail is decremented. if the whole thing is appended + * just move the existing tail by old_capacity */ + if (tocopy < slots) + { + ZeroMemory(queue->array, batch * sizeof(wMessage)); + queue->tail += old_capacity; + } + else + { + const size_t remain = queue->tail - batch; + const size_t movesize = remain * sizeof(wMessage); + memmove_s(queue->array, queue->tail * sizeof(wMessage), &queue->array[batch], + movesize); + + const size_t zerooffset = remain; + const size_t zerosize = (queue->tail - remain) * sizeof(wMessage); + ZeroMemory(&queue->array[zerooffset], zerosize); + queue->tail -= batch; + } } } + return TRUE; +} + +BOOL MessageQueue_Dispatch(wMessageQueue* queue, wMessage* message) +{ + BOOL ret = FALSE; + if (!queue || !message) + return FALSE; + + EnterCriticalSection(&queue->lock); + + if (!MessageQueue_EnsureCapacity(queue, 1)) + goto out; + CopyMemory(&(queue->array[queue->tail]), message, sizeof(wMessage)); message = &(queue->array[queue->tail]); @@ -200,14 +239,12 @@ wMessageQueue* MessageQueue_New(const wObject* callback) if (!queue) return NULL; - queue->capacity = 32; - queue->array = (wMessage*)calloc(queue->capacity, sizeof(wMessage)); - if (!queue->array) - goto error_array; - if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000)) goto error_spinlock; + if (!MessageQueue_EnsureCapacity(queue, 32)) + goto error_event; + queue->event = CreateEvent(NULL, TRUE, FALSE, NULL); if (!queue->event) goto error_event; diff --git a/winpr/libwinpr/utils/collections/PubSub.c b/winpr/libwinpr/utils/collections/PubSub.c index 466dde6897e7..d1eb1134d4a4 100644 --- a/winpr/libwinpr/utils/collections/PubSub.c +++ b/winpr/libwinpr/utils/collections/PubSub.c @@ -22,6 +22,7 @@ #endif #include +#include #include @@ -36,6 +37,9 @@ wEventType* PubSub_GetEventTypes(wPubSub* pubSub, int* count) { + if (!pubSub) + return NULL; + if (count) *count = pubSub->count; @@ -48,11 +52,15 @@ wEventType* PubSub_GetEventTypes(wPubSub* pubSub, int* count) void PubSub_Lock(wPubSub* pubSub) { + WINPR_ASSERT(pubSub); + EnterCriticalSection(&pubSub->lock); } void PubSub_Unlock(wPubSub* pubSub) { + WINPR_ASSERT(pubSub); + LeaveCriticalSection(&pubSub->lock); } @@ -61,6 +69,9 @@ wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName) int index; wEventType* event = NULL; + if (!pubSub) + return event; + for (index = 0; index < pubSub->count; index++) { if (strcmp(pubSub->events[index].EventName, EventName) == 0) @@ -75,6 +86,8 @@ wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName) void PubSub_AddEventTypes(wPubSub* pubSub, wEventType* events, int count) { + WINPR_ASSERT(pubSub); + if (pubSub->synchronized) PubSub_Lock(pubSub); @@ -103,6 +116,9 @@ int PubSub_Subscribe(wPubSub* pubSub, const char* EventName, pEventHandler Event wEventType* event; int status = -1; + if (!pubSub) + return status; + if (pubSub->synchronized) PubSub_Lock(pubSub); @@ -130,6 +146,9 @@ int PubSub_Unsubscribe(wPubSub* pubSub, const char* EventName, pEventHandler Eve wEventType* event; int status = -1; + if (!pubSub) + return status; + if (pubSub->synchronized) PubSub_Lock(pubSub); @@ -164,6 +183,9 @@ int PubSub_OnEvent(wPubSub* pubSub, const char* EventName, void* context, wEvent wEventType* event; int status = -1; + if (!pubSub) + return status; + if (pubSub->synchronized) PubSub_Lock(pubSub); diff --git a/winpr/libwinpr/utils/collections/Queue.c b/winpr/libwinpr/utils/collections/Queue.c index 2e8abd6c2832..57f5801a7928 100644 --- a/winpr/libwinpr/utils/collections/Queue.c +++ b/winpr/libwinpr/utils/collections/Queue.c @@ -137,43 +137,85 @@ BOOL Queue_Contains(wQueue* queue, void* obj) return found; } -/** - * Adds an object to the end of the Queue. - */ - -BOOL Queue_Enqueue(wQueue* queue, void* obj) +static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count) { - BOOL ret = TRUE; + const size_t blocksize = 32ull; + WINPR_ASSERT(queue); - if (queue->synchronized) - EnterCriticalSection(&queue->lock); + if (queue->growthFactor > SIZE_MAX / blocksize) + return FALSE; + + const size_t increment = blocksize * queue->growthFactor; + if (queue->size > SIZE_MAX - count) + return FALSE; - if (queue->size == queue->capacity) + const size_t required = queue->size + count; + if (required > queue->capacity) { - int old_capacity; - int new_capacity; - void** newArray; - old_capacity = queue->capacity; - new_capacity = queue->capacity * queue->growthFactor; - newArray = (void**)realloc(queue->array, sizeof(void*) * new_capacity); + const size_t old_capacity = queue->capacity; + if (required > SIZE_MAX - increment) + return FALSE; + + const size_t new_capacity = required + increment - required % increment; + if (new_capacity > SIZE_MAX / sizeof(BYTE*)) + return FALSE; + + void **newArray = (void**)realloc(queue->array, sizeof(void*) * new_capacity); if (!newArray) - { - ret = FALSE; - goto out; - } + return FALSE; queue->capacity = new_capacity; queue->array = newArray; - ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(void*)); + ZeroMemory(&(queue->array[old_capacity]), + (new_capacity - old_capacity) * sizeof(uintptr_t)); /* rearrange wrapped entries */ if (queue->tail <= queue->head) { - CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(void*)); - queue->tail += old_capacity; + const size_t tocopy = queue->tail; + const size_t slots = new_capacity - old_capacity; + const size_t batch = (tocopy < slots) ? tocopy : slots; + + CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(uintptr_t)); + + /* Tail is decremented. if the whole thing is appended + * just move the existing tail by old_capacity */ + if (tocopy < slots) + { + ZeroMemory(queue->array, batch * sizeof(uintptr_t)); + queue->tail += old_capacity; + } + else + { + const size_t remain = queue->tail - batch; + const size_t movesize = remain * sizeof(uintptr_t); + memmove_s(queue->array, queue->tail * sizeof(uintptr_t), &queue->array[batch], + movesize); + + const size_t zerooffset = remain; + const size_t zerosize = (queue->tail - remain) * sizeof(uintptr_t); + ZeroMemory(&queue->array[zerooffset], zerosize); + queue->tail -= batch; + } } } + return TRUE; +} + +/** + * Adds an object to the end of the Queue. + */ + +BOOL Queue_Enqueue(wQueue* queue, void* obj) +{ + BOOL ret = TRUE; + + if (queue->synchronized) + EnterCriticalSection(&queue->lock); + + if (!Queue_EnsureCapacity(queue, 1)) + goto out; queue->array[queue->tail] = obj; queue->tail = (queue->tail + 1) % queue->capacity; @@ -252,20 +294,18 @@ wQueue* Queue_New(BOOL synchronized, int capacity, int growthFactor) if (!queue) return NULL; - queue->capacity = 32; + size_t scapacity = 32; queue->growthFactor = 2; queue->synchronized = synchronized; if (capacity > 0) - queue->capacity = capacity; + scapacity = capacity; if (growthFactor > 0) queue->growthFactor = growthFactor; - queue->array = (void**)calloc(queue->capacity, sizeof(void*)); - - if (!queue->array) - goto out_free; + if (!Queue_EnsureCapacity(queue, scapacity)) + goto out_free_array; queue->event = CreateEvent(NULL, TRUE, FALSE, NULL); diff --git a/winpr/libwinpr/utils/corkscrew/debug.c b/winpr/libwinpr/utils/corkscrew/debug.c new file mode 100644 index 000000000000..0034fc9b3f09 --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/debug.c @@ -0,0 +1,262 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "debug.h" + +#define TAG "com.winpr.utils.debug" +#define LOGT(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_TRACE, __VA_ARGS__); \ + } while (0) +#define LOGD(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_DEBUG, __VA_ARGS__); \ + } while (0) +#define LOGI(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_INFO, __VA_ARGS__); \ + } while (0) +#define LOGW(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_WARN, __VA_ARGS__); \ + } while (0) +#define LOGE(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_ERROR, __VA_ARGS__); \ + } while (0) +#define LOGF(...) \ + do \ + { \ + WLog_Print(WLog_Get(TAG), WLOG_FATAL, __VA_ARGS__); \ + } while (0) + +static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!"; + +typedef struct +{ + backtrace_frame_t* buffer; + size_t max; + size_t used; +} t_corkscrew_data; + +typedef struct +{ + void* hdl; + ssize_t (*unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + ssize_t (*unwind_backtrace_thread)(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + ssize_t (*unwind_backtrace_ptrace)(pid_t tid, const ptrace_context_t* context, + backtrace_frame_t* backtrace, size_t ignore_depth, + size_t max_depth); + void (*get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + void (*get_backtrace_symbols_ptrace)(const ptrace_context_t* context, + const backtrace_frame_t* backtrace, size_t frames, + backtrace_symbol_t* backtrace_symbols); + void (*free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames); + void (*format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame, + const backtrace_symbol_t* symbol, char* buffer, + size_t bufferSize); +} t_corkscrew; + +static pthread_once_t initialized = PTHREAD_ONCE_INIT; +static t_corkscrew* fkt = NULL; + +void load_library(void) +{ + static t_corkscrew lib; + { + lib.hdl = dlopen("libcorkscrew.so", RTLD_LAZY); + + if (!lib.hdl) + { + LOGF("dlopen error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace = dlsym(lib.hdl, "unwind_backtrace"); + + if (!lib.unwind_backtrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace_thread = dlsym(lib.hdl, "unwind_backtrace_thread"); + + if (!lib.unwind_backtrace_thread) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.unwind_backtrace_ptrace = dlsym(lib.hdl, "unwind_backtrace_ptrace"); + + if (!lib.unwind_backtrace_ptrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.get_backtrace_symbols = dlsym(lib.hdl, "get_backtrace_symbols"); + + if (!lib.get_backtrace_symbols) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.get_backtrace_symbols_ptrace = dlsym(lib.hdl, "get_backtrace_symbols_ptrace"); + + if (!lib.get_backtrace_symbols_ptrace) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.free_backtrace_symbols = dlsym(lib.hdl, "free_backtrace_symbols"); + + if (!lib.free_backtrace_symbols) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + lib.format_backtrace_line = dlsym(lib.hdl, "format_backtrace_line"); + + if (!lib.format_backtrace_line) + { + LOGF("dlsym error %s", dlerror()); + goto fail; + } + + fkt = &lib; + return; + } +fail: +{ + if (lib.hdl) + dlclose(lib.hdl); + + fkt = NULL; +} +} + +void winpr_corkscrew_backtrace_free(void* buffer) +{ + t_corkscrew_data* data = (t_corkscrew_data*)buffer; + if (!data) + return; + + free(data->buffer); + free(data); +} + +void* winpr_corkscrew_backtrace(DWORD size) +{ + t_corkscrew_data* data = calloc(1, sizeof(t_corkscrew_data)); + + if (!data) + return NULL; + + data->buffer = calloc(size, sizeof(backtrace_frame_t)); + + if (!data->buffer) + { + free(data); + return NULL; + } + + pthread_once(&initialized, load_library); + data->max = size; + data->used = fkt->unwind_backtrace(data->buffer, 0, size); + return data; +} + +char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used) +{ + t_corkscrew_data* data = (t_corkscrew_data*)buffer; + if (used) + *used = 0; + + if (!data) + return NULL; + + pthread_once(&initialized, load_library); + + if (!fkt) + { + LOGF(support_msg); + return NULL; + } + else + { + size_t line_len = (data->max > 1024) ? data->max : 1024; + size_t i; + size_t array_size = data->used * sizeof(char*); + size_t lines_size = data->used * line_len; + char** vlines = calloc(1, array_size + lines_size); + backtrace_symbol_t* symbols = calloc(data->used, sizeof(backtrace_symbol_t)); + + if (!vlines || !symbols) + { + free(vlines); + free(symbols); + return NULL; + } + + /* Set the pointers in the allocated buffer's initial array section */ + for (i = 0; i < data->used; i++) + vlines[i] = (char*)vlines + array_size + i * line_len; + + fkt->get_backtrace_symbols(data->buffer, data->used, symbols); + + for (i = 0; i < data->used; i++) + fkt->format_backtrace_line(i, &data->buffer[i], &symbols[i], vlines[i], line_len); + + fkt->free_backtrace_symbols(symbols, data->used); + free(symbols); + + if (used) + *used = data->used; + + return vlines; + } +} + diff --git a/winpr/libwinpr/utils/corkscrew/debug.h b/winpr/libwinpr/utils/corkscrew/debug.h new file mode 100644 index 000000000000..e98b9bd531df --- /dev/null +++ b/winpr/libwinpr/utils/corkscrew/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_CORKSCREW_H +#define WINPR_DEBUG_CORKSCREW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_corkscrew_backtrace(DWORD size); + void winpr_corkscrew_backtrace_free(void* buffer); + char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used); + void winpr_corkscrew_backtrace_symbols_fd(void* buffer, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_CORKSCREW_H */ diff --git a/winpr/libwinpr/utils/debug.c b/winpr/libwinpr/utils/debug.c index 503f4afe1505..460b6740c7eb 100644 --- a/winpr/libwinpr/utils/debug.c +++ b/winpr/libwinpr/utils/debug.c @@ -24,21 +24,24 @@ #include #include + +#include #include #if defined(HAVE_EXECINFO_H) -#include +#include #endif -#if defined(ANDROID) -#include +#if defined(USE_UNWIND) +#include +#endif + +#if defined(HAVE_CORKSCREW) +#include #endif #if defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#define write _write +#include #endif #include @@ -79,205 +82,19 @@ static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!"; -#if defined(HAVE_EXECINFO_H) -typedef struct -{ - void** buffer; - size_t max; - size_t used; -} t_execinfo; -#endif - -#if defined(_WIN32) || defined(_WIN64) -typedef struct -{ - PVOID* stack; - ULONG used; - ULONG max; -} t_win_stack; -#endif - -#if defined(ANDROID) -#include -#include -#include - -typedef struct -{ - backtrace_frame_t* buffer; - size_t max; - size_t used; -} t_corkscrew_data; - -typedef struct -{ - void* hdl; - ssize_t (*unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth, - size_t max_depth); - ssize_t (*unwind_backtrace_thread)(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, - size_t max_depth); - ssize_t (*unwind_backtrace_ptrace)(pid_t tid, const ptrace_context_t* context, - backtrace_frame_t* backtrace, size_t ignore_depth, - size_t max_depth); - void (*get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols); - void (*get_backtrace_symbols_ptrace)(const ptrace_context_t* context, - const backtrace_frame_t* backtrace, size_t frames, - backtrace_symbol_t* backtrace_symbols); - void (*free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames); - void (*format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame, - const backtrace_symbol_t* symbol, char* buffer, - size_t bufferSize); -} t_corkscrew; - -static pthread_once_t initialized = PTHREAD_ONCE_INIT; -static t_corkscrew* fkt = NULL; - -void load_library(void) -{ - static t_corkscrew lib; - { - lib.hdl = dlopen("libcorkscrew.so", RTLD_LAZY); - - if (!lib.hdl) - { - LOGF("dlopen error %s", dlerror()); - goto fail; - } - - lib.unwind_backtrace = dlsym(lib.hdl, "unwind_backtrace"); - - if (!lib.unwind_backtrace) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.unwind_backtrace_thread = dlsym(lib.hdl, "unwind_backtrace_thread"); - - if (!lib.unwind_backtrace_thread) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.unwind_backtrace_ptrace = dlsym(lib.hdl, "unwind_backtrace_ptrace"); - - if (!lib.unwind_backtrace_ptrace) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.get_backtrace_symbols = dlsym(lib.hdl, "get_backtrace_symbols"); - - if (!lib.get_backtrace_symbols) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.get_backtrace_symbols_ptrace = dlsym(lib.hdl, "get_backtrace_symbols_ptrace"); - - if (!lib.get_backtrace_symbols_ptrace) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.free_backtrace_symbols = dlsym(lib.hdl, "free_backtrace_symbols"); - - if (!lib.free_backtrace_symbols) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - lib.format_backtrace_line = dlsym(lib.hdl, "format_backtrace_line"); - - if (!lib.format_backtrace_line) - { - LOGF("dlsym error %s", dlerror()); - goto fail; - } - - fkt = &lib; - return; - } -fail: -{ - if (lib.hdl) - dlclose(lib.hdl); - - fkt = NULL; -} -} -#endif - -#if defined(_WIN32) && (NTDDI_VERSION <= NTDDI_WINXP) - -typedef USHORT(WINAPI* PRTL_CAPTURE_STACK_BACK_TRACE_FN)(ULONG FramesToSkip, ULONG FramesToCapture, - PVOID* BackTrace, PULONG BackTraceHash); - -static HMODULE g_NTDLL_Library = NULL; -static BOOL g_RtlCaptureStackBackTrace_Detected = FALSE; -static BOOL g_RtlCaptureStackBackTrace_Available = FALSE; -static PRTL_CAPTURE_STACK_BACK_TRACE_FN g_pRtlCaptureStackBackTrace = NULL; - -USHORT RtlCaptureStackBackTrace(ULONG FramesToSkip, ULONG FramesToCapture, PVOID* BackTrace, - PULONG BackTraceHash) -{ - if (!g_RtlCaptureStackBackTrace_Detected) - { - g_NTDLL_Library = LoadLibraryA("kernel32.dll"); - - if (g_NTDLL_Library) - { - g_pRtlCaptureStackBackTrace = (PRTL_CAPTURE_STACK_BACK_TRACE_FN)GetProcAddress( - g_NTDLL_Library, "RtlCaptureStackBackTrace"); - g_RtlCaptureStackBackTrace_Available = (g_pRtlCaptureStackBackTrace) ? TRUE : FALSE; - } - else - { - g_RtlCaptureStackBackTrace_Available = FALSE; - } - - g_RtlCaptureStackBackTrace_Detected = TRUE; - } - - if (g_RtlCaptureStackBackTrace_Available) - { - return (*g_pRtlCaptureStackBackTrace)(FramesToSkip, FramesToCapture, BackTrace, - BackTraceHash); - } - - return 0; -} - -#endif - void winpr_backtrace_free(void* buffer) { if (!buffer) - { - LOGF(support_msg); return; - } -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = (t_execinfo*)buffer; - free(data->buffer); - free(data); -#elif defined(ANDROID) - t_corkscrew_data* data = (t_corkscrew_data*)buffer; - free(data->buffer); - free(data); +#if defined(USE_UNWIND) + winpr_unwind_backtrace_free(buffer); +#elif defined(HAVE_EXECINFO_H) + winpr_execinfo_backtrace_free(buffer); +#elif defined(HAVE_CORKSCREW) + winpr_corkscrew_backtrace_free(buffer); #elif defined(_WIN32) || defined(_WIN64) - { - t_win_stack* data = (t_win_stack*)buffer; - free(data->stack); - free(data); - } + winpr_win_backtrace_free(buffer); #else LOGF(support_msg); #endif @@ -285,60 +102,14 @@ void winpr_backtrace_free(void* buffer) void* winpr_backtrace(DWORD size) { -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = calloc(1, sizeof(t_execinfo)); - - if (!data) - return NULL; - - data->buffer = calloc(size, sizeof(void*)); - - if (!data->buffer) - { - free(data); - return NULL; - } - - data->max = size; - data->used = backtrace(data->buffer, size); - return data; -#elif defined(ANDROID) - t_corkscrew_data* data = calloc(1, sizeof(t_corkscrew_data)); - - if (!data) - return NULL; - - data->buffer = calloc(size, sizeof(backtrace_frame_t)); - - if (!data->buffer) - { - free(data); - return NULL; - } - - pthread_once(&initialized, load_library); - data->max = size; - data->used = fkt->unwind_backtrace(data->buffer, 0, size); - return data; +#if defined(USE_UNWIND) + return winpr_unwind_backtrace(size); +#elif defined(HAVE_EXECINFO_H) + return winpr_execinfo_backtrace(size); +#elif defined(HAVE_CORKSCREW) + return winpr_corkscrew_backtrace(size); #elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) - HANDLE process = GetCurrentProcess(); - t_win_stack* data = calloc(1, sizeof(t_win_stack)); - - if (!data) - return NULL; - - data->max = size; - data->stack = calloc(data->max, sizeof(PVOID)); - - if (!data->stack) - { - free(data); - return NULL; - } - - SymInitialize(process, NULL, TRUE); - data->used = RtlCaptureStackBackTrace(2, size, data->stack, NULL); - return data; + return winpr_win_backtrace(size); #else LOGF(support_msg); return NULL; @@ -356,113 +127,15 @@ char** winpr_backtrace_symbols(void* buffer, size_t* used) return NULL; } -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = (t_execinfo*)buffer; - - if (!data) - return NULL; - - if (used) - *used = data->used; - - return backtrace_symbols(data->buffer, data->used); -#elif defined(ANDROID) - t_corkscrew_data* data = (t_corkscrew_data*)buffer; - - if (!data) - return NULL; - - pthread_once(&initialized, load_library); - - if (!fkt) - { - LOGF(support_msg); - return NULL; - } - else - { - size_t line_len = (data->max > 1024) ? data->max : 1024; - size_t i; - size_t array_size = data->used * sizeof(char*); - size_t lines_size = data->used * line_len; - char** vlines = calloc(1, array_size + lines_size); - backtrace_symbol_t* symbols = calloc(data->used, sizeof(backtrace_symbol_t)); - - if (!vlines || !symbols) - { - free(vlines); - free(symbols); - return NULL; - } - - /* Set the pointers in the allocated buffer's initial array section */ - for (i = 0; i < data->used; i++) - vlines[i] = (char*)vlines + array_size + i * line_len; - - fkt->get_backtrace_symbols(data->buffer, data->used, symbols); - - for (i = 0; i < data->used; i++) - fkt->format_backtrace_line(i, &data->buffer[i], &symbols[i], vlines[i], line_len); - - fkt->free_backtrace_symbols(symbols, data->used); - free(symbols); - - if (used) - *used = data->used; - - return vlines; - } +#if defined(USE_UNWIND) + return winpr_unwind_backtrace_symbols(buffer, used); +#elif defined(HAVE_EXECINFO_H) + return winpr_execinfo_backtrace_symbols(buffer, used); +#elif defined(HAVE_CORKSCREW) + return winpr_corkscrew_backtrace_symbols(buffer, used); #elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP) - { - size_t i; - size_t line_len = 1024; - HANDLE process = GetCurrentProcess(); - t_win_stack* data = (t_win_stack*)buffer; - size_t array_size = data->used * sizeof(char*); - size_t lines_size = data->used * line_len; - char** vlines = calloc(1, array_size + lines_size); - SYMBOL_INFO* symbol = calloc(1, sizeof(SYMBOL_INFO) + line_len * sizeof(char)); - IMAGEHLP_LINE64* line = (IMAGEHLP_LINE64*)calloc(1, sizeof(IMAGEHLP_LINE64)); - - if (!vlines || !symbol || !line) - { - free(vlines); - free(symbol); - free(line); - return NULL; - } - - line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); - symbol->MaxNameLen = line_len; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - - /* Set the pointers in the allocated buffer's initial array section */ - for (i = 0; i < data->used; i++) - vlines[i] = (char*)vlines + array_size + i * line_len; - - for (i = 0; i < data->used; i++) - { - DWORD64 address = (DWORD64)(data->stack[i]); - DWORD displacement; - SymFromAddr(process, address, 0, symbol); - - if (SymGetLineFromAddr64(process, address, &displacement, line)) - { - sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s in %s:%" PRIu32, symbol->Address, - symbol->Name, line->FileName, line->LineNumber); - } - else - sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s", symbol->Address, symbol->Name); - } - - if (used) - *used = data->used; - - free(symbol); - free(line); - return vlines; - } + return winpr_win_backtrace_symbols(buffer, used); #else LOGF(support_msg); return NULL; @@ -477,25 +150,19 @@ void winpr_backtrace_symbols_fd(void* buffer, int fd) return; } -#if defined(HAVE_EXECINFO_H) - t_execinfo* data = (t_execinfo*)buffer; - - if (!data) - return; - - backtrace_symbols_fd(data->buffer, data->used, fd); -#elif defined(_WIN32) || defined(_WIN64) || defined(ANDROID) +#if defined(HAVE_EXECINFO_H) && !defined(USE_UNWIND) + winpr_execinfo_backtrace_symbols_fd(buffer, fd); +#elif !defined(ANDROID) { - DWORD i; - size_t used; - char** lines; - lines = winpr_backtrace_symbols(buffer, &used); + size_t i; + size_t used = 0; + char** lines = winpr_backtrace_symbols(buffer, &used); - if (lines) - { - for (i = 0; i < used; i++) - write(fd, lines[i], strlen(lines[i])); - } + if (!lines) + return; + + for (i = 0; i < used; i++) + _write(fd, lines[i], (unsigned)strnlen(lines[i], UINT32_MAX)); } #else LOGF(support_msg); @@ -527,47 +194,16 @@ void winpr_log_backtrace_ex(wLog* log, DWORD level, DWORD size) for (x = 0; x < used; x++) WLog_Print(log, level, "%" PRIuz ": %s\n", x, msg[x]); } - + free(msg); + +fail: winpr_backtrace_free(stack); } char* winpr_strerror(DWORD dw, char* dmsg, size_t size) { #if defined(_WIN32) - DWORD rc; - DWORD nSize = 0; - DWORD dwFlags = 0; - LPTSTR msg = NULL; - BOOL alloc = FALSE; - dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; -#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER - alloc = TRUE; - dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; -#else - nSize = (DWORD)(size * 2); - msg = (LPTSTR)calloc(nSize, sizeof(TCHAR)); -#endif - rc = FormatMessage(dwFlags, NULL, dw, 0, alloc ? (LPTSTR)&msg : msg, nSize, NULL); - - if (rc) - { -#if defined(UNICODE) - WideCharToMultiByte(CP_ACP, 0, msg, rc, dmsg, size - 1, NULL, NULL); -#else /* defined(UNICODE) */ - memcpy(dmsg, msg, min(rc, size - 1)); -#endif /* defined(UNICODE) */ - dmsg[min(rc, size - 1)] = 0; -#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER - LocalFree(msg); -#else - free(msg); -#endif - } - else - { - _snprintf(dmsg, size, "FAILURE: 0x%08" PRIX32 "", GetLastError()); - } - + return winpr_win_strerror(dw, dmsg, size); #else /* defined(_WIN32) */ _snprintf(dmsg, size, "%s", strerror(dw)); #endif /* defined(_WIN32) */ diff --git a/winpr/libwinpr/utils/execinfo/debug.c b/winpr/libwinpr/utils/execinfo/debug.c new file mode 100644 index 000000000000..509d1f7d71b9 --- /dev/null +++ b/winpr/libwinpr/utils/execinfo/debug.c @@ -0,0 +1,88 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "debug.h" + +typedef struct +{ + void** buffer; + size_t max; + size_t used; +} t_execinfo; + +void winpr_execinfo_backtrace_free(void* buffer) +{ + t_execinfo* data = (t_execinfo*)buffer; + if (!data) + return; + + free(data->buffer); + free(data); +} + +void* winpr_execinfo_backtrace(DWORD size) +{ + t_execinfo* data = calloc(1, sizeof(t_execinfo)); + + if (!data) + return NULL; + + data->buffer = calloc(size, sizeof(void*)); + + if (!data->buffer) + { + free(data); + return NULL; + } + + data->max = size; + data->used = backtrace(data->buffer, size); + return data; +} + +char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used) +{ + t_execinfo* data = (t_execinfo*)buffer; + if (used) + *used = 0; + + if (!data) + return NULL; + + if (used) + *used = data->used; + + return backtrace_symbols(data->buffer, data->used); +} + +void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd) +{ + t_execinfo* data = (t_execinfo*)buffer; + + if (!data) + return; + + backtrace_symbols_fd(data->buffer, data->used, fd); +} diff --git a/winpr/libwinpr/utils/execinfo/debug.h b/winpr/libwinpr/utils/execinfo/debug.h new file mode 100644 index 000000000000..aaf7748a3daa --- /dev/null +++ b/winpr/libwinpr/utils/execinfo/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_EXECINFO_H +#define WINPR_DEBUG_EXECINFO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_execinfo_backtrace(DWORD size); + void winpr_execinfo_backtrace_free(void* buffer); + char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used); + void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_EXECINFO_H */ diff --git a/winpr/libwinpr/utils/lodepng/lodepng.c b/winpr/libwinpr/utils/lodepng/lodepng.c index 2dd526a2cdc9..b5c23073eeb2 100644 --- a/winpr/libwinpr/utils/lodepng/lodepng.c +++ b/winpr/libwinpr/utils/lodepng/lodepng.c @@ -3815,7 +3815,7 @@ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, LodePNGCol { size_t i; ColorTree tree; - size_t numpixels = w * h * 1ULL; + size_t numpixels = 1ull * w * h; if (lodepng_color_mode_equal(mode_out, mode_in)) { @@ -3918,7 +3918,7 @@ unsigned get_color_profile(LodePNGColorProfile* profile, const unsigned char* in unsigned error = 0; size_t i; ColorTree tree; - size_t numpixels = w * h * 1ULL; + size_t numpixels = 1ull * w * h; unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; @@ -4539,7 +4539,7 @@ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsi if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) { CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); - removePaddingBits(out, in, w * bpp * 1ULL, ((w * bpp + 7ULL) / 8ULL) * 8ULL, h); + removePaddingBits(out, in, 1ull * w * bpp, ((w * bpp + 7ULL) / 8ULL) * 8ULL, h); } /*we can immediatly filter into the out buffer, no other steps needed*/ else @@ -4565,7 +4565,7 @@ static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsi bits between the different reduced images: each reduced image still starts nicely at a byte*/ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], - passw[i] * bpp * 1ULL, ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL, + 1ull * passw[i] * bpp, ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL, passh[i]); } } @@ -6056,7 +6056,7 @@ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const error = 83; /*alloc fail*/ if (!error) { - addPaddingBits(padded, in, ((w * bpp + 7ULL) / 8ULL) * 8ULL, w * bpp * 1ULL, h); + addPaddingBits(padded, in, ((w * bpp + 7ULL) / 8ULL) * 8ULL, 1ull * w * bpp, h); error = filter(*out, padded, w, h, &info_png->color, settings); } free(padded); @@ -6100,8 +6100,8 @@ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const if (!padded) ERROR_BREAK(83); /*alloc fail*/ addPaddingBits(padded, &adam7[passstart[i]], - ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL, passw[i] * bpp * 1ULL, - passh[i] * 1ULL); + ((passw[i] * bpp + 7ULL) / 8ULL) * 8ULL, 1ull * passw[i] * bpp, + 1ull * passh[i]); error = filter(&(*out)[filter_passstart[i]], padded, passw[i], passh[i], &info_png->color, settings); free(padded); diff --git a/winpr/libwinpr/utils/ntlm.c b/winpr/libwinpr/utils/ntlm.c index 3af38b642ebe..42eaf552e77b 100644 --- a/winpr/libwinpr/utils/ntlm.c +++ b/winpr/libwinpr/utils/ntlm.c @@ -48,16 +48,15 @@ BOOL NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash) { LPWSTR PasswordW = NULL; BOOL result = FALSE; + int PasswordLengthW = 0; if (!NtHash) return FALSE; - if (!(PasswordW = (LPWSTR)calloc(PasswordLength, 2))) - return FALSE; - - MultiByteToWideChar(CP_ACP, 0, Password, PasswordLength, PasswordW, PasswordLength); - - if (!NTOWFv1W(PasswordW, PasswordLength * 2, NtHash)) + PasswordLengthW = ConvertToUnicode(CP_ACP, 0, Password, PasswordLength, &PasswordW, 0); + if (PasswordLengthW < 0) + goto out_fail; + if (!NTOWFv1W(PasswordW, PasswordLengthW * sizeof(WCHAR), NtHash)) goto out_fail; result = TRUE; @@ -94,23 +93,26 @@ BOOL NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, UINT32 UserLeng LPWSTR DomainW = NULL; LPWSTR PasswordW = NULL; BOOL result = FALSE; + int UserLengthW = 0; + int DomainLengthW = 0; + int PasswordLengthW = 0; if (!NtHash) return FALSE; - UserW = (LPWSTR)calloc(UserLength, 2); - DomainW = (LPWSTR)calloc(DomainLength, 2); - PasswordW = (LPWSTR)calloc(PasswordLength, 2); + UserLengthW = ConvertToUnicode(CP_ACP, 0, User, UserLength, &UserW, 0); + DomainLengthW = ConvertToUnicode(CP_ACP, 0, Domain, DomainLength, &DomainW, 0); + PasswordLengthW = ConvertToUnicode(CP_ACP, 0, Password, PasswordLength, &PasswordW, 0); - if (!UserW || !DomainW || !PasswordW) + if (UserLengthW < 0) + goto out_fail; + if (DomainLengthW < 0) + goto out_fail; + if (PasswordLengthW < 0) goto out_fail; - MultiByteToWideChar(CP_ACP, 0, User, UserLength, UserW, UserLength); - MultiByteToWideChar(CP_ACP, 0, Domain, DomainLength, DomainW, DomainLength); - MultiByteToWideChar(CP_ACP, 0, Password, PasswordLength, PasswordW, PasswordLength); - - if (!NTOWFv2W(PasswordW, PasswordLength * 2, UserW, UserLength * 2, DomainW, DomainLength * 2, - NtHash)) + if (!NTOWFv2W(PasswordW, PasswordLengthW * sizeof(WCHAR), UserW, UserLengthW * sizeof(WCHAR), + DomainW, DomainLengthW * sizeof(WCHAR), NtHash)) goto out_fail; result = TRUE; @@ -160,20 +162,21 @@ BOOL NTOWFv2FromHashA(BYTE* NtHashV1, LPSTR User, UINT32 UserLength, LPSTR Domai LPWSTR UserW = NULL; LPWSTR DomainW = NULL; BOOL result = FALSE; + int UserLengthW = 0; + int DomainLengthW = 0; if (!NtHash) return FALSE; - UserW = (LPWSTR)calloc(UserLength, 2); - DomainW = (LPWSTR)calloc(DomainLength, 2); - - if (!UserW || !DomainW) + UserLengthW = ConvertToUnicode(CP_ACP, 0, User, UserLength, &UserW, 0); + DomainLengthW = ConvertToUnicode(CP_ACP, 0, Domain, DomainLength, &DomainW, 0); + if (UserLengthW < 0) + goto out_fail; + if (DomainLengthW < 0) goto out_fail; - MultiByteToWideChar(CP_ACP, 0, User, UserLength, UserW, UserLength); - MultiByteToWideChar(CP_ACP, 0, Domain, DomainLength, DomainW, DomainLength); - - if (!NTOWFv2FromHashW(NtHashV1, UserW, UserLength * 2, DomainW, DomainLength * 2, NtHash)) + if (!NTOWFv2FromHashW(NtHashV1, UserW, UserLength * sizeof(WCHAR), DomainW, + DomainLength * sizeof(WCHAR), NtHash)) goto out_fail; result = TRUE; diff --git a/winpr/libwinpr/utils/sam.c b/winpr/libwinpr/utils/sam.c index 094475fe9ffb..78ac5640008c 100644 --- a/winpr/libwinpr/utils/sam.c +++ b/winpr/libwinpr/utils/sam.c @@ -59,9 +59,11 @@ static WINPR_SAM_ENTRY* SamEntryFromDataA(LPCSTR User, DWORD UserLength, LPCSTR WINPR_SAM_ENTRY* entry = calloc(1, sizeof(WINPR_SAM_ENTRY)); if (!entry) return NULL; - entry->User = _strdup(User); + if (User && (UserLength > 0)) + entry->User = _strdup(User); entry->UserLength = UserLength; - entry->Domain = _strdup(Domain); + if (Domain && (DomainLength > 0)) + entry->Domain = _strdup(Domain); entry->DomainLength = DomainLength; return entry; } @@ -173,23 +175,29 @@ static void SamLookupFinish(WINPR_SAM* sam) static void HexStrToBin(const char* str, BYTE* bin, size_t length) { size_t i; - CharUpperBuffA(str, length * 2); for (i = 0; i < length; i++) { - bin[i] = 0; + char cur = str[2 * i]; + char curNext = str[2 * i + 1]; + BYTE* curBin = &bin[i]; - if ((str[i * 2] >= '0') && (str[i * 2] <= '9')) - bin[i] |= (str[i * 2] - '0') << 4; + CharUpperBuffA(&cur, 1); + CharUpperBuffA(&curNext, 1); - if ((str[i * 2] >= 'A') && (str[i * 2] <= 'F')) - bin[i] |= (str[i * 2] - 'A' + 10) << 4; + *curBin = 0; - if ((str[i * 2 + 1] >= '0') && (str[i * 2 + 1] <= '9')) - bin[i] |= (str[i * 2 + 1] - '0'); + if ((cur >= '0') && (cur <= '9')) + *curBin |= (cur - '0') << 4; - if ((str[i * 2 + 1] >= 'A') && (str[i * 2 + 1] <= 'F')) - bin[i] |= (str[i * 2 + 1] - 'A' + 10); + if ((cur >= 'A') && (cur <= 'F')) + *curBin |= (cur - 'A' + 10) << 4; + + if ((curNext >= '0') && (curNext <= '9')) + *curBin |= (curNext - '0'); + + if ((curNext >= 'A') && (curNext <= 'F')) + *curBin |= (curNext - 'A' + 10); } } diff --git a/winpr/libwinpr/utils/ssl.c b/winpr/libwinpr/utils/ssl.c index 74ef156e7b07..1924909d0cb8 100644 --- a/winpr/libwinpr/utils/ssl.c +++ b/winpr/libwinpr/utils/ssl.c @@ -33,11 +33,21 @@ #include #include +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +#include +#endif + #include "../log.h" #define TAG WINPR_TAG("utils.ssl") static BOOL g_winpr_openssl_initialized_by_winpr = FALSE; +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) +static OSSL_PROVIDER* s_winpr_openssl_provider_fips = NULL; +static OSSL_PROVIDER* s_winpr_openssl_provider_legacy = NULL; +static OSSL_PROVIDER* s_winpr_openssl_provider_default = NULL; +#endif + /** * Note from OpenSSL 1.1.0 "CHANGES": * OpenSSL now uses a new threading API. It is no longer necessary to @@ -242,9 +252,14 @@ static BOOL winpr_enable_fips(DWORD flags) WLog_ERR(TAG, "Openssl fips mode not available on openssl versions less than 1.0.1!"); return FALSE; #else - WLog_DBG(TAG, "Ensuring openssl fips mode is ENabled"); + WLog_DBG(TAG, "Ensuring openssl fips mode is enabled"); #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + s_winpr_openssl_provider_fips = OSSL_PROVIDER_load(NULL, "fips"); + if (s_winpr_openssl_provider_fips == NULL) + { + WLog_WARN(TAG, "OpenSSL FIPS provider failled to load"); + } if (!EVP_default_properties_is_fips_enabled(NULL)) #else if (FIPS_mode() != 1) @@ -255,10 +270,10 @@ static BOOL winpr_enable_fips(DWORD flags) #else if (FIPS_mode_set(1)) #endif - WLog_INFO(TAG, "Openssl fips mode ENabled!"); + WLog_INFO(TAG, "Openssl fips mode enabled!"); else { - WLog_ERR(TAG, "Openssl fips mode ENable failed!"); + WLog_ERR(TAG, "Openssl fips mode enable failed!"); return FALSE; } } @@ -305,8 +320,23 @@ static BOOL CALLBACK _winpr_openssl_initialize(PINIT_ONCE once, PVOID param, PVO return FALSE; #endif + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + /* The legacy provider is needed for MD4. */ + s_winpr_openssl_provider_legacy = OSSL_PROVIDER_load(NULL, "legacy"); + if (s_winpr_openssl_provider_legacy == NULL) + { + WLog_WARN(TAG, "OpenSSL LEGACY provider failed to load, no md4 support available!"); + } + s_winpr_openssl_provider_default = OSSL_PROVIDER_load(NULL, "default"); + if (s_winpr_openssl_provider_default == NULL) + { + WLog_WARN(TAG, "OpenSSL DEFAULT provider failed to load"); + } +#endif + g_winpr_openssl_initialized_by_winpr = TRUE; - return winpr_enable_fips(flags); + return TRUE; } /* exported functions */ @@ -356,6 +386,11 @@ BOOL winpr_CleanupSSL(DWORD flags) #endif } +#endif +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + OSSL_PROVIDER_unload(s_winpr_openssl_provider_fips); + OSSL_PROVIDER_unload(s_winpr_openssl_provider_legacy); + OSSL_PROVIDER_unload(s_winpr_openssl_provider_default); #endif return TRUE; } diff --git a/winpr/libwinpr/utils/stream.c b/winpr/libwinpr/utils/stream.c index 1271981b7860..009559dbd82f 100644 --- a/winpr/libwinpr/utils/stream.c +++ b/winpr/libwinpr/utils/stream.c @@ -28,43 +28,38 @@ BOOL Stream_EnsureCapacity(wStream* s, size_t size) { - if (s->capacity < size) - { - size_t position; - size_t old_capacity; - size_t new_capacity; - BYTE* new_buf; - - old_capacity = s->capacity; - new_capacity = old_capacity; - - do - { - new_capacity *= 2; - } while (new_capacity < size); - - position = Stream_GetPosition(s); - - if (!s->isOwner) - { - new_buf = (BYTE*)malloc(new_capacity); - CopyMemory(new_buf, s->buffer, s->capacity); - s->isOwner = TRUE; - } - else - { - new_buf = (BYTE*)realloc(s->buffer, new_capacity); - } + WINPR_ASSERT(s); + if (s->capacity >= size) + return TRUE; + + const size_t increment = 128ull; + const size_t old_capacity = s->capacity; + const size_t new_capacity = size + increment - size % increment; + const size_t position = Stream_GetPosition(s); + BYTE* new_buf = NULL; + if (!s->isOwner) + { + new_buf = (BYTE*)malloc(new_capacity); if (!new_buf) return FALSE; - s->buffer = new_buf; - s->capacity = new_capacity; - s->length = new_capacity; - ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity); - Stream_SetPosition(s, position); + CopyMemory(new_buf, s->buffer, s->capacity); + s->isOwner = TRUE; } + else + { + new_buf = (BYTE*)realloc(s->buffer, new_capacity); + if (!new_buf) + return FALSE; + } + + s->buffer = new_buf; + s->capacity = new_capacity; + s->length = new_capacity; + ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity); + + Stream_SetPosition(s, position); return TRUE; } @@ -132,3 +127,68 @@ void Stream_Free(wStream* s, BOOL bFreeBuffer) free(s); } } + +BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, UINT64 len, + const char* fmt, ...) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthExVa(tag, level, s, len, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, UINT64 len, + const char* fmt, va_list args) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, len, fmt, args); + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, UINT64 len, + const char* fmt, ...) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + { + va_list args; + + va_start(args, fmt); + Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, len, fmt, args); + va_end(args); + + return FALSE; + } + return TRUE; +} + +BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, UINT64 len, + const char* fmt, va_list args) +{ + const size_t actual = Stream_GetRemainingLength(s); + + if (actual < len) + { + char prefix[1024] = { 0 }; + + vsnprintf(prefix, sizeof(prefix), fmt, args); + + WLog_Print(log, level, "[%s] invalid length, got %" PRIuz ", require at least %" PRIu64, + prefix, actual, len); + winpr_log_backtrace_ex(log, level, 20); + return FALSE; + } + return TRUE; +} diff --git a/winpr/libwinpr/utils/unwind/debug.c b/winpr/libwinpr/utils/unwind/debug.c new file mode 100644 index 000000000000..4e86f7b1fc49 --- /dev/null +++ b/winpr/libwinpr/utils/unwind/debug.c @@ -0,0 +1,128 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include + +#include +#include "debug.h" + +#include + +typedef struct +{ + uintptr_t pc; + void* langSpecificData; +} unwind_info_t; + +typedef struct +{ + size_t pos; + size_t size; + unwind_info_t* info; +} unwind_context_t; + +static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg) +{ + unwind_context_t* ctx = arg; + + assert(ctx); + + if (ctx->pos < ctx->size) + { + unwind_info_t* info = &ctx->info[ctx->pos++]; + info->pc = _Unwind_GetIP(context); + info->langSpecificData = _Unwind_GetLanguageSpecificData(context); + } + + return _URC_NO_REASON; +} + +void* winpr_unwind_backtrace(DWORD size) +{ + _Unwind_Reason_Code rc; + unwind_context_t* ctx = calloc(1, sizeof(unwind_context_t)); + if (!ctx) + goto fail; + ctx->size = size; + ctx->info = calloc(size, sizeof(unwind_info_t)); + if (!ctx->info) + goto fail; + + rc = _Unwind_Backtrace(unwind_backtrace_callback, ctx); + if (rc != _URC_END_OF_STACK) + goto fail; + + return ctx; +fail: + winpr_unwind_backtrace_free(ctx); + return NULL; +} + +void winpr_unwind_backtrace_free(void* buffer) +{ + unwind_context_t* ctx = buffer; + if (!ctx) + return; + free(ctx->info); + free(ctx); +} + +#define UNWIND_MAX_LINE_SIZE 1024 + +char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used) +{ + size_t x; + char** str = NULL; + unwind_context_t* ctx = buffer; + + if (!ctx) + return NULL; + + str = calloc(ctx->pos * (sizeof(char*) + UNWIND_MAX_LINE_SIZE), sizeof(char*)); + if (!str) + return NULL; + + if (used) + *used = ctx->pos; + + for (x = 0; x < ctx->pos; x++) + { + char* msg = str + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE; + const unwind_info_t* info = &ctx->info[x]; + Dl_info dlinfo = { 0 }; + int rc = dladdr(info->pc, &dlinfo); + + str[x] = msg; + + if (rc == 0) + _snprintf(msg, UNWIND_MAX_LINE_SIZE, "unresolvable, address=%p", info->pc); + else + _snprintf(msg, UNWIND_MAX_LINE_SIZE, "dli_fname=%s [%p], dli_sname=%s [%p]", + dlinfo.dli_fname, dlinfo.dli_fbase, dlinfo.dli_sname, dlinfo.dli_saddr); + } + + return str; +} diff --git a/winpr/libwinpr/utils/unwind/debug.h b/winpr/libwinpr/utils/unwind/debug.h new file mode 100644 index 000000000000..5cd1290a8ee7 --- /dev/null +++ b/winpr/libwinpr/utils/unwind/debug.h @@ -0,0 +1,41 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_UNWIND_H +#define WINPR_DEBUG_UNWIND_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include + + void* winpr_unwind_backtrace(DWORD size); + void winpr_unwind_backtrace_free(void* buffer); + char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_UNWIND_H */ diff --git a/winpr/libwinpr/utils/windows/debug.c b/winpr/libwinpr/utils/windows/debug.c new file mode 100644 index 000000000000..41545c75b177 --- /dev/null +++ b/winpr/libwinpr/utils/windows/debug.c @@ -0,0 +1,169 @@ +/** + * WinPR: Windows Portable Runtime + * Debugging Utils + * + * Copyright 2014 Armin Novak + * Copyright 2014 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include + +#include "debug.h" + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif + +typedef struct +{ + PVOID* stack; + ULONG used; + ULONG max; +} t_win_stack; + +void winpr_win_backtrace_free(void* buffer) +{ + t_win_stack* data = (t_win_stack*)buffer; + if (!data) + return; + + free(data->stack); + free(data); +} + +void* winpr_win_backtrace(DWORD size) +{ + HANDLE process = GetCurrentProcess(); + t_win_stack* data = calloc(1, sizeof(t_win_stack)); + + if (!data) + return NULL; + + data->max = size; + data->stack = calloc(data->max, sizeof(PVOID)); + + if (!data->stack) + { + free(data); + return NULL; + } + + SymInitialize(process, NULL, TRUE); + data->used = RtlCaptureStackBackTrace(2, size, data->stack, NULL); + return data; +} + +char** winpr_win_backtrace_symbols(void* buffer, size_t* used) +{ + if (used) + *used = 0; + + if (!buffer) + return NULL; + + { + size_t i; + size_t line_len = 1024; + HANDLE process = GetCurrentProcess(); + t_win_stack* data = (t_win_stack*)buffer; + size_t array_size = data->used * sizeof(char*); + size_t lines_size = data->used * line_len; + char** vlines = calloc(1, array_size + lines_size); + SYMBOL_INFO* symbol = calloc(1, sizeof(SYMBOL_INFO) + line_len * sizeof(char)); + IMAGEHLP_LINE64* line = (IMAGEHLP_LINE64*)calloc(1, sizeof(IMAGEHLP_LINE64)); + + if (!vlines || !symbol || !line) + { + free(vlines); + free(symbol); + free(line); + return NULL; + } + + line->SizeOfStruct = sizeof(IMAGEHLP_LINE64); + symbol->MaxNameLen = (ULONG)line_len; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + /* Set the pointers in the allocated buffer's initial array section */ + for (i = 0; i < data->used; i++) + vlines[i] = (char*)vlines + array_size + i * line_len; + + for (i = 0; i < data->used; i++) + { + DWORD64 address = (DWORD64)(data->stack[i]); + DWORD displacement; + SymFromAddr(process, address, 0, symbol); + + if (SymGetLineFromAddr64(process, address, &displacement, line)) + { + sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s in %s:%" PRIu32, symbol->Address, + symbol->Name, line->FileName, line->LineNumber); + } + else + sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s", symbol->Address, symbol->Name); + } + + if (used) + *used = data->used; + + free(symbol); + free(line); + return vlines; + } +} + +char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size) +{ + DWORD rc; + DWORD nSize = 0; + DWORD dwFlags = 0; + LPTSTR msg = NULL; + BOOL alloc = FALSE; + dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; +#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER + alloc = TRUE; + dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; +#else + nSize = (DWORD)(size * 2); + msg = (LPTSTR)calloc(nSize, sizeof(TCHAR)); +#endif + rc = FormatMessage(dwFlags, NULL, dw, 0, alloc ? (LPTSTR)&msg : msg, nSize, NULL); + + if (rc) + { +#if defined(UNICODE) + WideCharToMultiByte(CP_ACP, 0, msg, rc, dmsg, (int)MIN(size - 1, INT_MAX), NULL, NULL); +#else /* defined(UNICODE) */ + memcpy(dmsg, msg, MIN(rc, size - 1)); +#endif /* defined(UNICODE) */ + dmsg[MIN(rc, size - 1)] = 0; +#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER + LocalFree(msg); +#else + free(msg); +#endif + } + else + { + _snprintf(dmsg, size, "FAILURE: 0x%08" PRIX32 "", GetLastError()); + } + + return dmsg; +} diff --git a/winpr/libwinpr/utils/windows/debug.h b/winpr/libwinpr/utils/windows/debug.h new file mode 100644 index 000000000000..c36b87a58412 --- /dev/null +++ b/winpr/libwinpr/utils/windows/debug.h @@ -0,0 +1,40 @@ +/** + * WinPR: Windows Portable Runtime + * WinPR Debugging helpers + * + * Copyright 2022 Armin Novak + * Copyright 2022 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WINPR_DEBUG_WIN_H +#define WINPR_DEBUG_WIN_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + void* winpr_win_backtrace(DWORD size); + void winpr_win_backtrace_free(void* buffer); + char** winpr_win_backtrace_symbols(void* buffer, size_t* used); + char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* WINPR_DEBUG_WIN_H */