diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 8ea04d7f..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: Bug report -about: Please fill in this template as much as you can, to help us, help you. -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** - -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -Please do not link to image hosting sites, as these can be ephemeral. Instead, attach them to the issue. - -**Desktop (please complete the following information):** - - - OS: [e.g. Windows] -- Barrier version [e.g 2.3.3] - -**Additional context** - -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..208e7058 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,79 @@ +name: Bug Report +description: File a bug report (for questions, ideas & support, use the Discussions tab, or IRC for quick answers, but make sure to stay on the channel!) +labels: [bug, triage] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + - type: textarea + id: what-happened + attributes: + label: What happened? + description: Also tell us, what did you expect to happen? + placeholder: Tell us what you see! Screenshots are also helpful - please attach to the issue (when created), rather than linking to image hosting sites. + validations: + required: true + - type: dropdown + id: version + attributes: + label: Version + description: What version of Barrier are you running? + options: + - v2.0.0-RC1 + - v2.0.0-RC2 + - v2.0.0 + - v2.1.1 + - v2.1.2 + - v2.3.0 + - v2.3.1 + - v2.3.2-alpha + - v2.3.2 + - v2.3.3 + - From Git HEAD or commit (specify below) + validations: + required: true + - type: input + id: git-commit-if + attributes: + label: Git commit hash (if applicable) + description: "When building Barrier from Git, what commit hash did you checkout from?" + placeholder: b0c0b42b + validations: + required: false + - type: textarea + id: pkg-mgr-origin + attributes: + label: If applicable, where did you install Barrier from? + description: This includes Snaps, Flatpaks, and self-built executables. + validations: + required: false + - type: dropdown + id: os + attributes: + label: What OSes are you seeing the problem on? (Check all that apply) + multiple: true + options: + - Linux + - Windows + - macOS + validations: + required: true + - type: textarea + id: os-version + attributes: + label: What OS versions are you using? + description: This applies to both client(s) and the server. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + - type: textarea + id: misc-info + attributes: + label: Any other information + description: Please enter any other information we should know, if applicable. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3ba13e0c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..df9d99b9 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +## Contributor Checklist: + +* [ ] I have created a file in the `doc/newsfragments` directory (and read the `README.md` in that directory) diff --git a/.gitmodules b/.gitmodules index ad9a3025..9a58a7e9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "ext/gmock"] path = ext/gmock url = https://github.com/google/googlemock.git +[submodule "ext/gulrak-filesystem"] + path = ext/gulrak-filesystem + url = https://github.com/gulrak/filesystem diff --git a/Build.properties b/Build.properties index d1c07fee..07192e05 100644 --- a/Build.properties +++ b/Build.properties @@ -2,6 +2,6 @@ # Barrier build parameters # BARRIER_VERSION_MAJOR = 2 -BARRIER_VERSION_MINOR = 3 -BARRIER_VERSION_PATCH = 3 +BARRIER_VERSION_MINOR = 4 +BARRIER_VERSION_PATCH = 0 BARRIER_VERSION_STAGE = release diff --git a/CMakeLists.txt b/CMakeLists.txt index c56b30be..86deb4b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,6 @@ if (UNIX) check_function_exists (poll HAVE_POLL) check_function_exists (sigwait HAVE_POSIX_SIGWAIT) check_function_exists (strftime HAVE_STRFTIME) - check_function_exists (vsnprintf HAVE_VSNPRINTF) check_function_exists (inet_aton HAVE_INET_ATON) # For some reason, the check_function_exists macro doesn't detect @@ -292,6 +291,9 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") list (APPEND libs Wtsapi32 Userenv Wininet comsuppw Shlwapi) add_definitions ( + /DSYSAPI_WIN32=1 + /DWINAPI_MSWINDOWS=1 + /D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1 # tr1 is used from gtest and gmock /DWIN32 /D_WINDOWS /D_CRT_SECURE_NO_WARNINGS @@ -300,6 +302,8 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") ) endif() +include_directories("${CMAKE_SOURCE_DIR}/ext/gulrak-filesystem/include") + # # OpenSSL # @@ -317,10 +321,21 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") ${OPENSSL_ROOT}/lib/ssleay32.lib ) elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + find_program(APT_PROGRAM "apt") find_program(BREW_PROGRAM "brew") find_program(PORT_PROGRAM "port") - if (IS_DIRECTORY /opt/local AND PORT_PROGRAM) + if (IS_DIRECTORY /opt/procursus AND APT_PROGRAM) + # procursus/apt + set (OPENSSL_ROOT /opt/procursus) + + include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include) + + set (OPENSSL_LIBS + ${OPENSSL_ROOT}/lib/libssl.a + ${OPENSSL_ROOT}/lib/libcrypto.a + ) + elseif (IS_DIRECTORY /opt/local AND PORT_PROGRAM) # macports set (OPENSSL_ROOT /opt/local) @@ -335,6 +350,16 @@ elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include) + set (OPENSSL_LIBS + ${OPENSSL_ROOT}/lib/libssl.a + ${OPENSSL_ROOT}/lib/libcrypto.a + ) + elseif (IS_DIRECTORY /opt/homebrew/opt/openssl AND BREW_PROGRAM) + # brew + set (OPENSSL_ROOT /opt/homebrew/opt/openssl) + + include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include) + set (OPENSSL_LIBS ${OPENSSL_ROOT}/lib/libssl.a ${OPENSSL_ROOT}/lib/libcrypto.a diff --git a/LICENSE b/LICENSE index acfef1bb..42498a45 100644 --- a/LICENSE +++ b/LICENSE @@ -3,7 +3,7 @@ Copyright (C) 2012-2016 Symless Ltd. Copyright (C) 2008-2014 Nick Bolton Copyright (C) 2002-2014 Chris Schoeneman -This program is released under the GPL with the additional exemption +This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. GNU GENERAL PUBLIC LICENSE @@ -286,3 +286,62 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md index dfdd5322..5804341f 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ __Q: Is it possible to use Barrier on Mac OS X / OS X versions prior to 10.12?__ **Q: After loading my configuration on the client the field 'Server IP' is still empty!** -> A: Edit your configuration to include the servers ip adress manually with +> A: Edit your configuration to include the server's ip address manually with > >``` >(...) diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000..741f54ae --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,59 @@ +Creating a release +================== + +This document is documentation intednded for maintainers of Barrier. +It documents the release process of Barrier. + +Step 1: Setup environment variables +----------------------------------- + +Setup the following environment variable that will be used throughout the rest of the steps. + + export VERSION=X.Y.Z + +Step 2: Release notes PR +------------------------ + +Open a new branch (e.g. `release`) and run the following: + + towncrier --version ${VERSION} --date `date -u +%F` + +This collects the release notes using the `towncrier` tool. Please commit the collected release +notes afterwards. + +Certain file names are not properly supported by the `towncrier` tool and it ignores them. +Check `newsfragments` directory for any forgotten release notes + +Step 3: Merge the release notes PR +---------------------------------- + +Step 4: Push git tag +-------------------- + +Pull the merge commit created on the `master` branch during the step 2. + +Create a tag: + + git tag -s v${VERSION} -m v${VERSION} + +Push the tag: + + git push origin master --tags + + +Step 5: Draft a new release on Github +------------------------------------- + +Go to https://github.com/buildbot/buildbot/releases and draft a new release. + +Use git tag as the title of the release: `vX.Y.Z`. + +Use the release notes generated by the `towncrier` tool as the description of the releases. + +Upload the artifacts created by Azure pipelines as the binaries of the release. The following +artifacts should be uploaded to Github: + + - the Barrier-X.Y.Z-release.dmg created by the oldest Mac OS task (artifact name is + "Mac Release Disk Image and App XYZ"). + + - the BarrierSetup-X.Y.Z-release.exe (artifact name is Windows Release Installer). diff --git a/_config.yml b/_config.yml index c7418817..f980e760 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-slate \ No newline at end of file +theme: jekyll-theme-slate diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1ef9a7e0..e34a3bc5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -65,8 +65,6 @@ jobs: - job: LinuxBuild strategy: matrix: - ubuntu-16.04: - imageName: 'ubuntu-16.04' ubuntu-18.04: imageName: 'ubuntu-18.04' ubuntu-20.04: @@ -83,13 +81,22 @@ jobs: - job: MacBuild displayName: Mac Build - pool: - vmImage: 'macOS-10.14' strategy: matrix: - Release: - B_BUILD_TYPE: Release - BARRIER_VERSION_STAGE: Release + big-sur-Release: + imageName: "macOS-11" + B_BUILD_TYPE: Release + BARRIER_VERSION_STAGE: Release + catalina-Release: + imageName: "macOS-10.15" + B_BUILD_TYPE: Release + BARRIER_VERSION_STAGE: Release + mojave-Release: + imageName: "macOS-10.14" + B_BUILD_TYPE: Release + BARRIER_VERSION_STAGE: Release + pool: + vmImage: $(imageName) variables: VERBOSE: 1 TERM: xterm-256color @@ -107,4 +114,4 @@ jobs: condition: eq(variables['B_BUILD_TYPE'], 'Release') inputs: pathtoPublish: build/bundle - artifactName: Mac Release Disk Image and App + artifactName: Mac Release Disk Image and App $(imageName) diff --git a/azure-pipelines/download_install_bonjour_sdk_like.ps1 b/azure-pipelines/download_install_bonjour_sdk_like.ps1 index 97bfa832..7f5fcb96 100644 --- a/azure-pipelines/download_install_bonjour_sdk_like.ps1 +++ b/azure-pipelines/download_install_bonjour_sdk_like.ps1 @@ -1,8 +1,7 @@ $ErrorActionPreference = "Stop" New-Item -Force -ItemType Directory -Path ".\deps\" -$Wc = New-Object System.Net.WebClient -$Wc.DownloadFile('https://github.com/nelsonjchen/mDNSResponder/releases/download/v2019.05.08.1/x64_RelWithDebInfo.zip', 'deps\BonjourSDKLike.zip') ; +Invoke-WebRequest 'https://github.com/nelsonjchen/mDNSResponder/releases/download/v2019.05.08.1/x64_RelWithDebInfo.zip' -OutFile 'deps\BonjourSDKLike.zip' ; Write-Output 'Downloaded BonjourSDKLike Zip' Write-Output 'Unzipping BonjourSDKLike Zip' Remove-Item -Recurse -Force -ErrorAction Ignore .\deps\BonjourSDKLike diff --git a/azure-pipelines/download_install_qt.ps1 b/azure-pipelines/download_install_qt.ps1 index e31b9b7a..a4b51d2c 100644 --- a/azure-pipelines/download_install_qt.ps1 +++ b/azure-pipelines/download_install_qt.ps1 @@ -6,8 +6,7 @@ $qt_version = '5.13.0' New-Item -Force -ItemType Directory -Path ".\deps\" Write-Output 'Downloading QLI Installer' -$Wc = New-Object System.Net.WebClient -$Wc.DownloadFile("https://github.com/nelsonjchen/qli-installer/archive/v$qli_install_version.zip", '.\deps\qli-installer.zip') ; +Invoke-WebRequest "https://github.com/nelsonjchen/qli-installer/archive/v$qli_install_version.zip" -OutFile '.\deps\qli-installer.zip' ; Write-Output 'Downloaded QLI Installer' Write-Output 'Extracting QLI Installer' diff --git a/clean_build.bat b/clean_build.bat index 4416a7e9..6f042326 100644 --- a/clean_build.bat +++ b/clean_build.bat @@ -41,8 +41,7 @@ if ERRORLEVEL 1 goto failed cd build cmake -G "%cmake_gen%" -A x64 -D CMAKE_BUILD_TYPE=%B_BUILD_TYPE% -D CMAKE_PREFIX_PATH="%B_QT_FULLPATH%" -D DNSSD_LIB="%B_BONJOUR%\Lib\x64\dnssd.lib" -D QT_VERSION=%B_QT_VER% .. if ERRORLEVEL 1 goto failed -echo @msbuild barrier.sln /p:Platform="x64" /p:Configuration=%B_BUILD_TYPE% /m %B_BUILD_OPTIONS% > make.bat -call make.bat +cmake --build . --config %B_BUILD_TYPE% if ERRORLEVEL 1 goto failed if exist bin\Debug ( copy %B_QT_FULLPATH%\bin\Qt5Cored.dll bin\Debug\ > NUL @@ -65,7 +64,7 @@ if exist bin\Debug ( mkdir bin\Release\platforms copy %B_QT_FULLPATH%\plugins\platforms\qwindows.dll bin\Release\platforms\ > NUL ) else ( - echo Remember to copy supporting binaries and confiuration files! + echo Remember to copy supporting binaries and configuration files! ) echo Build completed successfully diff --git a/clean_build.sh b/clean_build.sh index 3a92d8b9..585ca88b 100755 --- a/clean_build.sh +++ b/clean_build.sh @@ -1,10 +1,10 @@ #!/bin/sh -cd "$(dirname $0)" || exit 1 +cd "$(dirname "$0")" || exit 1 # some environments have cmake v2 as 'cmake' and v3 as 'cmake3' # check for cmake3 first then fallback to just cmake B_CMAKE=`type cmake3 2>/dev/null` if [ $? -eq 0 ]; then - B_CMAKE=`echo $B_CMAKE | cut -d' ' -f3` + B_CMAKE=`echo "$B_CMAKE" | cut -d' ' -f3` else B_CMAKE=cmake fi @@ -26,7 +26,7 @@ B_CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=$B_BUILD_TYPE $B_CMAKE_FLAGS" rm -rf build mkdir build || exit 1 cd build || exit 1 -echo Starting Barrier $B_BUILD_TYPE build... +echo "Starting Barrier $B_BUILD_TYPE build..." $B_CMAKE $B_CMAKE_FLAGS .. || exit 1 make || exit 1 echo "Build completed successfully" diff --git a/cmake/Version.cmake b/cmake/Version.cmake index 58d8943e..73524bf1 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -1,8 +1,8 @@ cmake_minimum_required (VERSION 3.4) set (BARRIER_VERSION_MAJOR 2) -set (BARRIER_VERSION_MINOR 3) -set (BARRIER_VERSION_PATCH 3) +set (BARRIER_VERSION_MINOR 4) +set (BARRIER_VERSION_PATCH 0) set (BARRIER_VERSION_STAGE "release") # diff --git a/dist/macos/bundle/build_dist.sh.in b/dist/macos/bundle/build_dist.sh.in index 3d5c333f..26a93201 100755 --- a/dist/macos/bundle/build_dist.sh.in +++ b/dist/macos/bundle/build_dist.sh.in @@ -1,7 +1,7 @@ #!/bin/sh # Use the same verbose variable as CMake -[ "$VERBOSE" == "1" ] && set -x +[ "$VERBOSE" = "1" ] && set -x # Exit on unset variables or pipe errors set -uo pipefail @@ -14,10 +14,10 @@ B_BARRIERC="Barrier.app/Contents/MacOS/barrierc" B_BARRIERS="Barrier.app/Contents/MacOS/barriers" # Colorized output -function info() { tput bold; echo "$@" ; tput sgr0 ;} -function error() { tput bold; tput setaf 1; echo "$@"; tput sgr0 ; } -function success() { tput bold; tput setaf 2; echo "$@"; tput sgr0 ; } -function warn() { tput bold; tput setaf 3; echo "$@"; tput sgr0 ; } +info() { tput bold; echo "$@"; tput sgr0 ; } +error() { tput bold; tput setaf 1; echo "$@"; tput sgr0 ; } +success() { tput bold; tput setaf 2; echo "$@"; tput sgr0 ; } +warn() { tput bold; tput setaf 3; echo "$@"; tput sgr0 ; } info "Checking for bundle contents" if [ ! -d "Barrier.app/Contents" ]; then @@ -40,7 +40,7 @@ if which -s port ; then info "MacPorts found, searching for macdeployqt" DEPLOYQT="$(port contents qt5-qttools | grep --only --max-count 1 '/.*macdeployqt')" if [ ! -x "$DEPLOYQT" ]; then - error Please install package qt5-qttools + error "Please install package qt5-qttools" exit 1 fi fi @@ -50,13 +50,13 @@ if which -s brew ; then info "Homebrew found, searching for macdeployqt" DEPLOYQT="$(brew list qt@5 | grep --only '/.*macdeployqt' | head -1)" if [ ! -x "$DEPLOYQT" ]; then - error Please install package qt + error "Please install package qt" exit 1 fi fi # Use macdeployqt to include libraries and create dmg -if [ "$B_BUILDTYPE" == "Release" ]; then +if [ "$B_BUILDTYPE" = "Release" ]; then info "Building Release disk image (dmg)" "$DEPLOYQT" Barrier.app -dmg \ -executable="$B_BARRIERC" \ diff --git a/dist/macos/bundle/build_installer.sh.in b/dist/macos/bundle/build_installer.sh.in index 0d2ccfc7..c1fd3fc2 100755 --- a/dist/macos/bundle/build_installer.sh.in +++ b/dist/macos/bundle/build_installer.sh.in @@ -1,30 +1,30 @@ #!/bin/sh # add warning for users running manually -function warn() { tput bold; tput setaf 3; echo "$@"; tput sgr0 ; } +warn() { tput bold; tput setaf 3; echo "$@"; tput sgr0 ; } warn "The scripts build_installer.sh and reref_dylibs.sh have been deprecated." warn "Please use build_dist.sh instead to deploy using macdeployqt" # change this to rename the installer package B_DMG="Barrier-@BARRIER_VERSION@.dmg" -cd "$( dirname '$0' )" +cd "$( dirname "$0" )" OWNDIR="$( pwd )" B_REREF_SCRIPT="$OWNDIR/reref_dylibs.sh" -if [ ! -x $B_REREF_SCRIPT ]; then - echo Missing script: $B_REREF_SCRIPT +if [ ! -x "$B_REREF_SCRIPT" ]; then + echo "Missing script: $B_REREF_SCRIPT" exit 1 fi # remove any old copies so there's no confusion about whether this # process completes successfully or not -rm -rf temp.dmg $B_DMG +rm -rf temp.dmg "$B_DMG" cd Barrier.app/Contents 2>/dev/null if [ $? -ne 0 ]; then - echo Please make sure that the build completed successfully - echo before trying to create the installer. + echo "Please make sure that the build completed successfully" + echo "before trying to create the installer." exit 1 fi @@ -77,7 +77,7 @@ hdiutil create -size 64m -fs HFS+ -volname "Barrier" temp.dmg || exit 1 hdiutil attach temp.dmg -mountpoint mnt || exit 1 cp -r Barrier.app mnt/ || exit 1 hdiutil detach mnt || exit 1 -hdiutil convert temp.dmg -format UDZO -o $B_DMG || exit 1 +hdiutil convert temp.dmg -format UDZO -o "$B_DMG" || exit 1 rm temp.dmg -echo "dmg $B_DMB created successfully" +echo "dmg $B_DMG created successfully" diff --git a/dist/macos/bundle/reref_dylibs.sh b/dist/macos/bundle/reref_dylibs.sh index 029ca4ae..71a9ba03 100755 --- a/dist/macos/bundle/reref_dylibs.sh +++ b/dist/macos/bundle/reref_dylibs.sh @@ -5,28 +5,28 @@ B_TARGET=$1 if [ "x$B_TARGET" = "x" ]; then # add warning for users running manually - function warn() { tput bold; tput setaf 3; echo "$@"; tput sgr0 ; } + warn() { tput bold; tput setaf 3; echo "$@"; tput sgr0 ; } warn "The scripts build_installer.sh and reref_dylibs.sh have been deprecated." warn "Please use build_dist.sh instead to deploy using macdeployqt" - echo Which binary needs to be re-referenced? + echo "Which binary needs to be re-referenced?" exit 1 fi -cd $(dirname $B_TARGET) || exit 1 +cd "$(dirname "$B_TARGET")" || exit 1 # where to find non-system libraries relative to target's directory. # the vast majority of the time this should be empty B_REL_PATH=$2 # we're in target's directory now. trim off the path -B_TARGET=$(basename $B_TARGET) +B_TARGET=$(basename "$B_TARGET") # get a list of non-system libraries and make local copies -B_LIBS=$(otool -XL $B_TARGET | awk '{ print $1 }' | grep -Ev '^(/usr/lib|/System)') +B_LIBS=$(otool -XL "$B_TARGET" | awk '{ print $1 }' | grep -Ev '^(/usr/lib|/System)') [ $? -ne 0 ] && exit 1 for B_LIB in $B_LIBS; do - B_LIB_NAME=$(basename $B_LIB) + B_LIB_NAME=$(basename "$B_LIB") # otool reports barrier as "barrier:" which fails self-reference test below B_LIB_NAME=${B_LIB_NAME//:} @@ -34,16 +34,16 @@ for B_LIB in $B_LIBS; do [ "$B_TARGET" = "$B_LIB_NAME" ] && continue B_DST=${B_REL_PATH}${B_LIB_NAME} - if [ ! -e $B_DST ]; then - cp $B_LIB $B_DST || exit 1 - chmod u+rw $B_DST || exit 1 + if [ ! -e "$B_DST" ]; then + cp "$B_LIB" "$B_DST" || exit 1 + chmod u+rw "$B_DST" || exit 1 # recursively call this script on libraries purposefully not passing # $B_REL_PATH so that it is only used explicitly - $0 $B_DST + $0 "$B_DST" fi # adjust the target's metadata to point to the local copy # rather than the system-wide copy which would only exist on # a development machine - install_name_tool -change $B_LIB @loader_path/$B_DST $B_TARGET || exit 1 + install_name_tool -change "$B_LIB" "@loader_path/$B_DST" "$B_TARGET" || exit 1 done diff --git a/doc/barrier.conf.example-advanced b/doc/barrier.conf.example-advanced index b3d6ad69..e1b23924 100644 --- a/doc/barrier.conf.example-advanced +++ b/doc/barrier.conf.example-advanced @@ -47,7 +47,7 @@ section: links end # The aliases section is to map the full names of the computers to their logical names used in the screens section -# One way to find the actual name of a comptuer is to run hostname from a command window +# One way to find the actual name of a computer is to run hostname from a command window section: aliases # Laptop is actually known as John-Smiths-MacBook-3.local John-Smiths-MacBook-3.local: diff --git a/doc/barrierc.1 b/doc/barrierc.1 index 2fd882ac..d4704470 100644 --- a/doc/barrierc.1 +++ b/doc/barrierc.1 @@ -1,5 +1,5 @@ .\" See UpdateManpages.txt about modification of this file. Most of it was generated by help2man 1.47.8. -.TH BARRIERC "1" "November 2019" "barrierc 2.3.3-release" "User Commands" +.TH BARRIERC "1" "November 2019" "barrierc 2.4.0-release" "User Commands" .SH NAME barrierc \- Barrier Keyboard/Mouse Client .SH SYNOPSIS diff --git a/doc/barriers.1 b/doc/barriers.1 index 098ea846..f4a460e6 100644 --- a/doc/barriers.1 +++ b/doc/barriers.1 @@ -1,5 +1,5 @@ .\" See UpdateManpages.txt about modification of this file. Most of it was generated by help2man 1.47.8. -.TH BARRIERS "1" "November 2019" "barriers 2.3.3-release" "User Commands" +.TH BARRIERS "1" "November 2019" "barriers 2.4.0-release" "User Commands" .SH NAME barriers \- Barrier Keyboard/Mouse Server .SH SYNOPSIS diff --git a/doc/newsfragments/README.md b/doc/newsfragments/README.md index a0ce3f33..0338cf80 100644 --- a/doc/newsfragments/README.md +++ b/doc/newsfragments/README.md @@ -1,12 +1,13 @@ -This is the directory for news snippets used by towncrier: https://github.com/twisted/towncrier +This is the directory for release note fragments processed by +[towncrier](https://github.com/hawkowl/towncrier). -When changing code in a way that's visible to an end user please make a new file in this directory. -It will be removed and integrated into release notes document upon a release of a new version of -Barrier. +When making a user-visible change create a file in this directory and it will be automatically be +included into the release note document when the next release is published. -towncrier has a few standard types of news fragments, signified by the file extension. These are: +The file extension specifies the type of a change. The following are currently supported: -.feature: Signifying a new feature. -.bugfix: Signifying a bug fix. -.doc: Signifying a documentation improvement. -.removal: Signifying a deprecation or removal of public API. + - .feature: a new feature. + - .bugfix: a bug fix. + - .security: a fix for security issue. + - .doc: a documentation improvement. + - .removal: a deprecation or removal of functionality. diff --git a/doc/newsfragments/build-failure-mipsel-riscv.bugfix b/doc/newsfragments/build-failure-mipsel-riscv.bugfix deleted file mode 100644 index 4e0506ad..00000000 --- a/doc/newsfragments/build-failure-mipsel-riscv.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix build failure on mips*el and riscv64 architecture. diff --git a/doc/newsfragments/configuration-reading-non-ascii-windows.bugfix b/doc/newsfragments/configuration-reading-non-ascii-windows.bugfix deleted file mode 100644 index 05234a50..00000000 --- a/doc/newsfragments/configuration-reading-non-ascii-windows.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed reading of configuration on Windows when the paths contain non-ASCII characters (https://github.com/debauchee/barrier/issues/976, https://github.com/debauchee/barrier/issues/974, https://github.com/debauchee/barrier/issues/444). diff --git a/doc/newsfragments/drop-target--option.feature b/doc/newsfragments/drop-target--option.feature deleted file mode 100644 index a49763e3..00000000 --- a/doc/newsfragments/drop-target--option.feature +++ /dev/null @@ -1 +0,0 @@ -Added `--drop-target` option that improves drag and drop support on Windows when Barrier is being run as a portable app. diff --git a/doc/newsfragments/fix-wrong-encoding-for-text-copied-between-linux-and-windows.bugfix b/doc/newsfragments/fix-wrong-encoding-for-text-copied-between-linux-and-windows.bugfix new file mode 100644 index 00000000..3b76d060 --- /dev/null +++ b/doc/newsfragments/fix-wrong-encoding-for-text-copied-between-linux-and-windows.bugfix @@ -0,0 +1,2 @@ +Fix wrong encoding for text copied between Linux and Windows +(https://github.com/debauchee/barrier/issues/1037, https://github.com/debauchee/barrier/issues/1137). diff --git a/doc/newsfragments/fix-x11-paste.bugfix b/doc/newsfragments/fix-x11-paste.bugfix deleted file mode 100644 index af4da5b1..00000000 --- a/doc/newsfragments/fix-x11-paste.bugfix +++ /dev/null @@ -1 +0,0 @@ - Map more X11 clipboard MIME types to corresponding converters (https://github.com/debauchee/barrier/issues/344). diff --git a/doc/newsfragments/gcc-11-build.bugfix b/doc/newsfragments/gcc-11-build.bugfix new file mode 100644 index 00000000..255f5365 --- /dev/null +++ b/doc/newsfragments/gcc-11-build.bugfix @@ -0,0 +1 @@ +Fixed build on GCC 11.2 (https://github.com/debauchee/barrier/issues/1366). diff --git a/doc/newsfragments/gui-autostart.feature b/doc/newsfragments/gui-autostart.feature deleted file mode 100644 index d75ca8d8..00000000 --- a/doc/newsfragments/gui-autostart.feature +++ /dev/null @@ -1 +0,0 @@ -Implemented a configuration option for Server GUI auto-start. diff --git a/doc/newsfragments/gui-hotkey-multiple-actions.bugfix b/doc/newsfragments/gui-hotkey-multiple-actions.bugfix deleted file mode 100644 index c78712b8..00000000 --- a/doc/newsfragments/gui-hotkey-multiple-actions.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed setup of multiple actions associated with a hotkey. diff --git a/doc/newsfragments/gui-hotkey-special-keys-handling.bugfix b/doc/newsfragments/gui-hotkey-special-keys-handling.bugfix deleted file mode 100644 index 5dd12a7e..00000000 --- a/doc/newsfragments/gui-hotkey-special-keys-handling.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed setup of hotkeys with special characters such as comma and semicolon (https://github.com/debauchee/barrier/issues/778). diff --git a/doc/newsfragments/gui-keyboard-enabled-screen-layout.feature b/doc/newsfragments/gui-keyboard-enabled-screen-layout.feature deleted file mode 100644 index 5ca3019e..00000000 --- a/doc/newsfragments/gui-keyboard-enabled-screen-layout.feature +++ /dev/null @@ -1 +0,0 @@ -Made it possible to use keyboard instead of mouse to modify screen layout. \ No newline at end of file diff --git a/doc/newsfragments/keyboard-backlight-media-keys.feature b/doc/newsfragments/keyboard-backlight-media-keys.feature deleted file mode 100644 index 9566304b..00000000 --- a/doc/newsfragments/keyboard-backlight-media-keys.feature +++ /dev/null @@ -1 +0,0 @@ -Added support for keyboard backlight media keys diff --git a/doc/newsfragments/muhenkan-eisu-toggle-keys.feature b/doc/newsfragments/muhenkan-eisu-toggle-keys.feature deleted file mode 100644 index 2c5e7163..00000000 --- a/doc/newsfragments/muhenkan-eisu-toggle-keys.feature +++ /dev/null @@ -1 +0,0 @@ -Added support for Eisu_toggle and Muhenkan keys diff --git a/doc/newsfragments/non-ascii-character-transfer.bugfix b/doc/newsfragments/non-ascii-character-transfer.bugfix deleted file mode 100644 index 31051ce5..00000000 --- a/doc/newsfragments/non-ascii-character-transfer.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed transfer of non-ASCII characters coming from a Windows server in certain cases (https://github.com/debauchee/barrier/issues/527). diff --git a/doc/newsfragments/profile-dir-option.feature b/doc/newsfragments/profile-dir-option.feature deleted file mode 100644 index f4836c39..00000000 --- a/doc/newsfragments/profile-dir-option.feature +++ /dev/null @@ -1 +0,0 @@ -Added `--profile-dir` option that allows to select custom profile directory. diff --git a/doc/newsfragments/regenerate-broken-server-cert.bugfix b/doc/newsfragments/regenerate-broken-server-cert.bugfix deleted file mode 100644 index 8ea1df3c..00000000 --- a/doc/newsfragments/regenerate-broken-server-cert.bugfix +++ /dev/null @@ -1 +0,0 @@ -Barrier will now regenerate server certificate if it's invalid instead of failing to launch (https://github.com/debauchee/barrier/issues/802) diff --git a/doc/newsfragments/sun-keyboard-keys.bugfix b/doc/newsfragments/sun-keyboard-keys.bugfix deleted file mode 100644 index 83bc952f..00000000 --- a/doc/newsfragments/sun-keyboard-keys.bugfix +++ /dev/null @@ -1 +0,0 @@ -Added support for additional keys on Sun Microsystems USB keyboards (https://github.com/debauchee/barrier/issues/784). diff --git a/doc/newsfragments/translation_chinese.bugfix b/doc/newsfragments/translation_chinese.bugfix deleted file mode 100644 index cab219e0..00000000 --- a/doc/newsfragments/translation_chinese.bugfix +++ /dev/null @@ -1 +0,0 @@ -Updated Chinese translation. diff --git a/doc/newsfragments/translation_slovak.bugfix b/doc/newsfragments/translation_slovak.bugfix deleted file mode 100644 index ff46ce94..00000000 --- a/doc/newsfragments/translation_slovak.bugfix +++ /dev/null @@ -1 +0,0 @@ -Updated Slovak translation. diff --git a/doc/newsfragments/use-theme-icons.bugfix b/doc/newsfragments/use-theme-icons.bugfix deleted file mode 100644 index 9dae72ca..00000000 --- a/doc/newsfragments/use-theme-icons.bugfix +++ /dev/null @@ -1 +0,0 @@ -Theme icons are now preferred to icons distributed together with Barrier (https://github.com/debauchee/barrier/issues/471). diff --git a/doc/newsfragments/windows-service-path.bugfix b/doc/newsfragments/windows-service-path.bugfix deleted file mode 100644 index c418b0aa..00000000 --- a/doc/newsfragments/windows-service-path.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed incorrect setup of Barrier service path on Windows. diff --git a/doc/org.barrier-foss.org.barrierc.plist b/doc/org.barrier-foss.org.barrierc.plist index 31e10ba5..90345c88 100644 --- a/doc/org.barrier-foss.org.barrierc.plist +++ b/doc/org.barrier-foss.org.barrierc.plist @@ -4,17 +4,17 @@ - Label - org.debauchee.com.barrierc.plist - OnDemand - - ProgramArguments - + Label + org.debauchee.com.barrierc.plist + OnDemand + + ProgramArguments + /usr/bin/barrierc 192.168.0.2 - - RunAtLoad - + + RunAtLoad + diff --git a/doc/org.barrier-foss.org.barriers.plist b/doc/org.barrier-foss.org.barriers.plist index f1ab5bf9..fed7b47a 100644 --- a/doc/org.barrier-foss.org.barriers.plist +++ b/doc/org.barrier-foss.org.barriers.plist @@ -4,12 +4,12 @@ - Label - org.debauchee.com.barriers.plist - OnDemand - - ProgramArguments - + Label + org.debauchee.com.barriers.plist + OnDemand + + ProgramArguments + /usr/bin/barriers --no-daemon --config diff --git a/doc/release_notes/index.md b/doc/release_notes/index.md new file mode 100644 index 00000000..0f2b7ca1 --- /dev/null +++ b/doc/release_notes/index.md @@ -0,0 +1,98 @@ +Release notes +============= + +[comment]: <> (towncrier release notes start) + +Barrier `2.4.0` ( `2021-11-01` ) +================================ + +Security fixes +-------------- + +- Barrier now supports client identity verification (fixes CVE-2021-42072, CVE-2021-42073). + + Previously a malicious client could connect to Barrier server without any authentication and + send application-level messages. This made the attack surface of Barrier significantly larger. + Additionally, in case the malicious client got possession of a valid screen name by brute forcing + or other means it could modify the clipboard contents of the server. + + To support seamless upgrades from older versions of Barrier this is currently disabled by default. + The feature can be enabled in the settings dialog. If enabled, older clients of Barrier will be + rejected. + +- Barrier now uses SHA256 fingerprints for establishing security of encrypted SSL connections. + After upgrading client to new version the existing server fingerprint will need to be approved + again. Client and server will show both SHA1 and SHA256 server fingerprints to allow + interoperability with older versions of Barrier. + +All of the above security issues have been reported by Matthias Gerstner who was really helpful +resolving them. + +Bug fixes +--------- + +- Fixed build failure on mips*el and riscv64 architecture. +- Fixed reading of configuration on Windows when the paths contain non-ASCII characters +(https://github.com/debauchee/barrier/issues/976, https://github.com/debauchee/barrier/issues/974, + https://github.com/debauchee/barrier/issues/444). +- Barrier no longer uses openssl CLI tool for any operations and hooks into the openssl library directly. +- More X11 clipboard MIME types have been mapped to corresponding converters (https://github.com/debauchee/barrier/issues/344). +- Fixed setup of multiple actions associated with a hotkey. +- Fixed setup of hotkeys with special characters such as comma and semicolon + (https://github.com/debauchee/barrier/issues/778). +- Fixed transfer of non-ASCII characters coming from a Windows server in certain cases + (https://github.com/debauchee/barrier/issues/527). +- Barrier will now regenerate server certificate if it's invalid instead of failing to launch + (https://github.com/debauchee/barrier/issues/802) +- Added support for additional keys on Sun Microsystems USB keyboards + (https://github.com/debauchee/barrier/issues/784). +- Updated Chinese translation. +- Updated Slovak translation. +- Theme icons are now preferred to icons distributed together with Barrier + (https://github.com/debauchee/barrier/issues/471). +- Fixed incorrect setup of Barrier service path on Windows. + +Features +-------- + +- Added `--drop-target` option that improves drag and drop support on Windows when Barrier is + being run as a portable app. +- The `--enable-crypto` command line option has been made the default to reduce chances of + accidental security mishaps when configuring Barrier from command line. + A new `--disable-crypto` command line option has been added to explicitly disable encryption. +- Added support for randomart images for easier comparison of SSL certificate fingerprints. + The algorithm is identical to what OpenSSH uses. +- Implemented a configuration option for Server GUI auto-start. +- Made it possible to use keyboard instead of mouse to modify screen layout. +- Added support for keyboard backlight media keys +- Added support for Eisu_toggle and Muhenkan keys +- Added `--profile-dir` option that allows to select custom profile directory. + +Barrier `2.3.4` ( `2021-11-01` ) +================================ + +Security fixes +-------------- + +- Barrier will now correctly close connections when the app-level handshake fails (fixes CVE-2021-42075). + + Previously repeated failing connections would leak file descriptors leading to Barrier being unable + to receive new connections from clients. + +- Barrier will now enforce a maximum length of input messages (fixes CVE-2021-42076). + + Previously it was possible for a malicious client or server to send excessive length messages + leading to denial of service by resource exhaustion. + +- Fixed a bug which caused Barrier to crash when disconnecting a TCP session just after sending + Hello message (fixes CVE-2021-42074). + This bug allowed an unauthenticated attacker to crash Barrier with only network access. + +All of the above security issues have been reported by Matthias Gerstner who was really helpful +resolving them. + +Bug fixes +--------- + +- Fixed a bug in SSL implementation that caused invalid data occasionally being sent to clients + under heavy load. diff --git a/doc/release_notes/index.template.jinja b/doc/release_notes/index.template.jinja new file mode 100644 index 00000000..418a1d32 --- /dev/null +++ b/doc/release_notes/index.template.jinja @@ -0,0 +1,37 @@ +{% for section, _ in sections|dictsort(by='key') %} +{% set underline = "-" %} +{% if section %} +{{section}} +{{ underline * section|length }}{% set underline = "-" %} + +{% endif %} +{% if sections[section] %} +{% for category, val in definitions|dictsort if category in sections[section]%} + +{{ definitions[category]['name'] }} +{{ underline * definitions[category]['name']|length }} + +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category]|dictsort(by='value') %} +- {{ text }} +{% endfor %} +{% else %} +- {{ sections[section][category]['']|sort|join(', ') }} + + +{% endif %} +{% if sections[section][category]|length == 0 %} + +No significant changes. + + +{% else %} +{% endif %} +{% endfor %} +{% else %} + +No significant changes. + + +{% endif %} +{% endfor %} diff --git a/ext/gulrak-filesystem b/ext/gulrak-filesystem new file mode 160000 index 00000000..614bbe87 --- /dev/null +++ b/ext/gulrak-filesystem @@ -0,0 +1 @@ +Subproject commit 614bbe87b80435d87ab8791564370e0c1d13627d diff --git a/osx_environment.sh b/osx_environment.sh index 0c725fb1..2ee886da 100644 --- a/osx_environment.sh +++ b/osx_environment.sh @@ -1,7 +1,7 @@ #!/bin/bash # Checks if directory exists, otherwise asks to install package. -function check_dir_exists() { +check_dir_exists() { local path=$1 local package=$2 @@ -11,7 +11,7 @@ function check_dir_exists() { fi } -if [ ! $BARRIER_BUILD_ENV ]; then +if [ -z "$BARRIER_BUILD_ENV" ]; then check_dir_exists '/Applications/Xcode.app' 'Xcode' printf "Modifying environment for Barrier build...\n" @@ -30,17 +30,14 @@ if [ ! $BARRIER_BUILD_ENV ]; then elif command -v brew; then printf "Detected Homebrew\n" QT_PATH=$(brew --prefix qt@5) - OPENSSL_PATH=$(brew --prefix openssl) - check_dir_exists "$QT_PATH" 'qt' - check_dir_exists "$OPENSSL_PATH" 'openssl' + check_dir_exists "$QT_PATH" 'qt5' export BARRIER_BUILD_BREW=1 - export CMAKE_PREFIX_PATH="$QT_PATH:$CMAKE_PREFIX_PATH" - export LD_LIBRARY_PATH="$OPENSSL_PATH/lib:$LD_LIBRARY_PATH" - export CPATH="$OPENSSL_PATH/include:$CPATH" - export PKG_CONFIG_PATH="$OPENSSL_PATH/lib/pkgconfig:$PKG_CONFIG_PATH" - + export CMAKE_PREFIX_PATH="/opt/procursus:$QT_PATH:$CMAKE_PREFIX_PATH" + export LD_LIBRARY_PATH="/opt/procursus/lib:$LD_LIBRARY_PATH" + export CPATH="/opt/procursus/include:$CPATH" + export PKG_CONFIG_PATH="/opt/procursus/lib/pkgconfig:$PKG_CONFIG_PATH" else printf "Neither Homebrew nor Macports is installed. Can't get dependency paths\n" exit 1 diff --git a/res/Readme.txt b/res/Readme.txt index 0b2802bd..9cfcf3c3 100644 --- a/res/Readme.txt +++ b/res/Readme.txt @@ -1,4 +1,4 @@ -Thank you for chosing Barrier! +Thank you for choosing Barrier! https://github.com/debauchee/barrier/ Barrier allows you to share your keyboard and mouse between computers over a network. diff --git a/res/barrier.desktop b/res/barrier.desktop index 6bb60e13..a47fd8ef 100644 --- a/res/barrier.desktop +++ b/res/barrier.desktop @@ -5,6 +5,5 @@ Comment=Keyboard and mouse sharing solution Exec=barrier Icon=barrier Terminal=false -Categories=Utility;DesktopUtility; +Categories=Utility;RemoteAccess; Keywords=keyboard;mouse;sharing;network;share; - diff --git a/res/config.h.in b/res/config.h.in index a2216875..53d3a3ec 100644 --- a/res/config.h.in +++ b/res/config.h.in @@ -94,9 +94,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H} -/* Define to 1 if you have the `vsnprintf` function. */ -#cmakedefine HAVE_VSNPRINTF ${HAVE_VSNPRINTF} - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WCHAR_H ${HAVE_WCHAR_H} diff --git a/res/makeicon.sh b/res/makeicon.sh index 2883755c..cfebe308 100755 --- a/res/makeicon.sh +++ b/res/makeicon.sh @@ -1,10 +1,10 @@ #!/bin/sh ICNS_BASE=../dist/macos/bundle/Barrier.app/Contents/Resources if ! which magick >/dev/null 2>&1; then - echo "Need ImageMagic for this" + echo "Need ImageMagick for this" exit 10 fi -cd $(dirname $0) || exit $? +cd "$(dirname "$0")" || exit $? if [ ! -r barrier.png ]; then echo "Use inkscape (or another vector graphics editor) to create barrier.png from barrier.svg first" exit 10 @@ -12,11 +12,11 @@ fi rm -rf work || exit $? mkdir -p work || exit $? for s in 16 24 32 48 64 128 256 512 1024; do - magick convert barrier.png -resize ${s}x${s} -depth 8 work/${s}.png || exit $? + magick convert barrier.png -resize "${s}x${s}" -depth 8 "work/${s}.png" || exit $? done # windows icon magick convert work/{16,24,32,48,64,128}.png barrier.png barrier.ico || exit $? # macos icon -png2icns $ICNS_BASE/Barrier.icns work/{16,32,256,512,1024}.png || exit $? +png2icns "$ICNS_BASE/Barrier.icns" work/{16,32,256,512,1024}.png || exit $? rm -rf work echo Done diff --git a/res/openssl/barrier.conf b/res/openssl/barrier.conf deleted file mode 100644 index a29abfd5..00000000 --- a/res/openssl/barrier.conf +++ /dev/null @@ -1,65 +0,0 @@ -# -# Barrier OpenSSL configuration file. -# Used for generation of certificate requests. -# - -dir = . - -[ca] -default_ca = CA_default - -[CA_default] -serial = $dir/serial -database = $dir/certindex.txt -new_certs_dir = $dir/certs -certificate = $dir/cacert.pem -private_key = $dir/private/cakey.pem -default_days = 365 -default_md = md5 -preserve = no -email_in_dn = no -nameopt = default_ca -certopt = default_ca -policy = policy_match - -[policy_match] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -[req] -default_bits = 2048 # Size of keys -default_keyfile = key.pem # name of generated keys -default_md = md5 # message digest algorithm -string_mask = nombstr # permitted characters -distinguished_name = req_distinguished_name -req_extensions = v3_req - -[req_distinguished_name] -0.organizationName = Organization Name (company) -organizationalUnitName = Organizational Unit Name (department, division) -emailAddress = Email Address -emailAddress_max = 40 -localityName = Locality Name (city, district) -stateOrProvinceName = State or Province Name (full name) -countryName = Country Name (2 letter code) -countryName_min = 2 -countryName_max = 2 -commonName = Common Name (hostname, IP, or your name) -commonName_max = 64 -0.organizationName_default = My Company -localityName_default = My Town -stateOrProvinceName_default = State or Providence -countryName_default = US - -[v3_ca] -basicConstraints = CA:TRUE -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer:always - -[v3_req] -basicConstraints = CA:FALSE -subjectKeyIdentifier = hash diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt index 0534d0fb..946b19a2 100644 --- a/src/cmd/CMakeLists.txt +++ b/src/cmd/CMakeLists.txt @@ -20,4 +20,3 @@ add_subdirectory(barriers) if (WIN32) add_subdirectory(barrierd) endif() - diff --git a/src/cmd/barrierc/CMakeLists.txt b/src/cmd/barrierc/CMakeLists.txt index c78ac6a4..45e9ab0e 100644 --- a/src/cmd/barrierc/CMakeLists.txt +++ b/src/cmd/barrierc/CMakeLists.txt @@ -46,4 +46,3 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") install (TARGETS barrierc DESTINATION bin) endif() - diff --git a/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp b/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp index 1a2bf56e..e273f998 100644 --- a/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp +++ b/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp @@ -66,4 +66,3 @@ createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events { return new OSXClientTaskBarReceiver(logBuffer, events); } - diff --git a/src/cmd/barrierd/barrierd.rc b/src/cmd/barrierd/barrierd.rc index df31e5f6..c5ad5022 100644 --- a/src/cmd/barrierd/barrierd.rc +++ b/src/cmd/barrierd/barrierd.rc @@ -73,7 +73,7 @@ BEGIN VALUE "ProductName", "Barrier" VALUE "ProductVersion", BARRIER_VERSION VALUE "OriginalFilename", "barrierd.exe" - VALUE "FileDescription", "Open source KVM software deamon" + VALUE "FileDescription", "Open source KVM software daemon" VALUE "InternalName", "barrierd" END END diff --git a/src/cmd/barriers/CMakeLists.txt b/src/cmd/barriers/CMakeLists.txt index e92334a6..c9fa7508 100644 --- a/src/cmd/barriers/CMakeLists.txt +++ b/src/cmd/barriers/CMakeLists.txt @@ -46,5 +46,3 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") install (TARGETS barriers DESTINATION bin) endif() - - diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 2875adf4..570e8424 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -29,7 +29,7 @@ set(GUI_SOURCE_FILES src/CommandProcess.cpp src/DataDownloader.cpp src/DisplayIsValid.cpp - src/Fingerprint.cpp + src/FingerprintAcceptDialog.cpp src/HotkeyDialog.cpp src/IpcClient.cpp src/Ipc.cpp @@ -70,7 +70,6 @@ set(GUI_HEADER_FILES src/DataDownloader.h src/DisplayIsValid.h src/ElevateMode.h - src/Fingerprint.h src/HotkeyDialog.h src/IpcClient.h src/Ipc.h @@ -106,6 +105,7 @@ set(GUI_UI_FILES src/AboutDialogBase.ui src/ActionDialogBase.ui src/AddClientDialogBase.ui + src/FingerprintAcceptDialog.ui src/HotkeyDialogBase.ui src/LogWindowBase.ui src/MainWindowBase.ui @@ -131,7 +131,7 @@ add_executable (barrier WIN32 include_directories (./src) -target_link_libraries (barrier Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS}) +target_link_libraries(barrier net base io Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS}) target_compile_definitions (barrier PRIVATE -DBARRIER_VERSION_STAGE="${BARRIER_VERSION_STAGE}") target_compile_definitions (barrier PRIVATE -DBARRIER_REVISION="${BARRIER_REVISION}") diff --git a/src/gui/res/lang/Languages.xml b/src/gui/res/lang/Languages.xml index 5948f9c6..723d667c 100644 --- a/src/gui/res/lang/Languages.xml +++ b/src/gui/res/lang/Languages.xml @@ -1,46 +1,46 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/res/lang/gui_ca-AD.ts b/src/gui/res/lang/gui_ca-AD.ts index 7d7d8738..3cd78957 100644 --- a/src/gui/res/lang/gui_ca-AD.ts +++ b/src/gui/res/lang/gui_ca-AD.ts @@ -905,8 +905,8 @@ To automatically trust this fingerprint for future connections, click Yes. To re Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - Arrossega noves pantalles a la graella o mou les actuals al voltant. -Arrossega una pantalla a la paperera per eliminar-la. + Arrossega noves pantalles a la graella o mou les actuals al voltant. +Arrossega una pantalla a la paperera per eliminar-la. Fes doble clic a una pantalla per editar la seva configuració. @@ -1335,7 +1335,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Error inici de sessió, hi ha hagut un error. + Error inici de sessió, hi ha hagut un error. Resposta del servidor: %1 diff --git a/src/gui/res/lang/gui_da.ts b/src/gui/res/lang/gui_da.ts index 77c4c2b2..7bec5184 100644 --- a/src/gui/res/lang/gui_da.ts +++ b/src/gui/res/lang/gui_da.ts @@ -1334,7 +1334,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Fejl i login, en fejl opstod. + Fejl i login, en fejl opstod. Server svar: %1 diff --git a/src/gui/res/lang/gui_nl-NL.ts b/src/gui/res/lang/gui_nl-NL.ts index d666189c..4ea0ee8a 100644 --- a/src/gui/res/lang/gui_nl-NL.ts +++ b/src/gui/res/lang/gui_nl-NL.ts @@ -1334,7 +1334,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Inloggen mislukt, er is een fout opgetreden. + Inloggen mislukt, er is een fout opgetreden. Foutmelding: %1 diff --git a/src/gui/res/lang/gui_pl-PL.ts b/src/gui/res/lang/gui_pl-PL.ts index 6d357fdc..7262a2ae 100644 --- a/src/gui/res/lang/gui_pl-PL.ts +++ b/src/gui/res/lang/gui_pl-PL.ts @@ -1325,7 +1325,7 @@ p, li { white-space: pre-wrap; } Login failed, an error occurred. %1 - Logowanie nie powiodło się, wystąpił błąd. + Logowanie nie powiodło się, wystąpił błąd. %1 @@ -1335,7 +1335,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Logowanie nie powiodło się, wystąpił błąd. + Logowanie nie powiodło się, wystąpił błąd. Odpowiedź serwera: %1 diff --git a/src/gui/res/lang/gui_ru.ts b/src/gui/res/lang/gui_ru.ts index 07a94254..f9842400 100644 --- a/src/gui/res/lang/gui_ru.ts +++ b/src/gui/res/lang/gui_ru.ts @@ -1325,7 +1325,7 @@ p, li { white-space: pre-wrap; } Login failed, an error occurred. %1 - Войти не удалось, произошла ошибка. + Войти не удалось, произошла ошибка. %1 @@ -1336,7 +1336,7 @@ p, li { white-space: pre-wrap; } Server response: %1 - Войти не удалось, произошла ошибка. + Войти не удалось, произошла ошибка. Ответ сервера: diff --git a/src/gui/res/lang/gui_tr-TR.ts b/src/gui/res/lang/gui_tr-TR.ts index 95946555..924662f9 100644 --- a/src/gui/res/lang/gui_tr-TR.ts +++ b/src/gui/res/lang/gui_tr-TR.ts @@ -905,8 +905,8 @@ To automatically trust this fingerprint for future connections, click Yes. To re Drag new screens to the grid or move existing ones around. Drag a screen to the trashcan to delete it. Double click on a screen to edit its settings. - Izgaraya yeni ekranları sürükleyin veya çevresinde mevcut olanları taşıyın. -Silmek için çöp tenekesine ekranı sürükleyin. + Izgaraya yeni ekranları sürükleyin veya çevresinde mevcut olanları taşıyın. +Silmek için çöp tenekesine ekranı sürükleyin. Kendi ayarlarınızı düzenlemek için bir ekran üzerine çift tıklayın. diff --git a/src/gui/src/AboutDialog.h b/src/gui/src/AboutDialog.h index bd85c9d4..6fdb0a24 100644 --- a/src/gui/src/AboutDialog.h +++ b/src/gui/src/AboutDialog.h @@ -40,4 +40,3 @@ class AboutDialog : public QDialog, public Ui::AboutDialogBase }; #endif - diff --git a/src/gui/src/Action.cpp b/src/gui/src/Action.cpp index 2882afb1..f34d1e8f 100644 --- a/src/gui/src/Action.cpp +++ b/src/gui/src/Action.cpp @@ -164,4 +164,3 @@ QTextStream& operator<<(QTextStream& outStream, const Action& action) return outStream; } - diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index c7878afe..894ce49a 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -158,6 +158,8 @@ void AppConfig::loadSettings() m_ElevateMode = static_cast(elevateMode.toInt()); m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); m_CryptoEnabled = settings().value("cryptoEnabled", true).toBool(); + // TODO: set default value of requireClientCertificate to true on Barrier 2.5.0 + m_RequireClientCertificate = settings().value("requireClientCertificate", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); m_AutoStart = settings().value("autoStart", false).toBool(); m_MinimizeToTray = settings().value("minimizeToTray", false).toBool(); @@ -181,6 +183,7 @@ void AppConfig::saveSettings() settings().setValue("elevateModeEnum", static_cast(m_ElevateMode)); settings().setValue("autoConfigPrompted", m_AutoConfigPrompted); settings().setValue("cryptoEnabled", m_CryptoEnabled); + settings().setValue("requireClientCertificate", m_RequireClientCertificate); settings().setValue("autoHide", m_AutoHide); settings().setValue("autoStart", m_AutoStart); settings().setValue("minimizeToTray", m_MinimizeToTray); @@ -211,7 +214,7 @@ void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; } void AppConfig::setAutoConfig(bool autoConfig) { m_AutoConfig = autoConfig; } -bool AppConfig::autoConfigPrompted() { return m_AutoConfigPrompted; } +bool AppConfig::autoConfigPrompted() { return m_AutoConfigPrompted; } void AppConfig::setAutoConfigPrompted(bool prompted) { m_AutoConfigPrompted = prompted; } @@ -225,6 +228,10 @@ void AppConfig::setCryptoEnabled(bool e) { m_CryptoEnabled = e; } bool AppConfig::getCryptoEnabled() const { return m_CryptoEnabled; } +void AppConfig::setRequireClientCertificate(bool e) { m_RequireClientCertificate = e; } + +bool AppConfig::getRequireClientCertificate() const { return m_RequireClientCertificate; } + void AppConfig::setAutoHide(bool b) { m_AutoHide = b; } bool AppConfig::getAutoHide() { return m_AutoHide; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 124ee85f..0dabb182 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -91,6 +91,9 @@ class AppConfig: public QObject void setCryptoEnabled(bool e); bool getCryptoEnabled() const; + void setRequireClientCertificate(bool e); + bool getRequireClientCertificate() const; + void setAutoHide(bool b); bool getAutoHide(); @@ -132,6 +135,7 @@ protected: ElevateMode m_ElevateMode; bool m_AutoConfigPrompted; bool m_CryptoEnabled; + bool m_RequireClientCertificate = false; bool m_AutoHide; bool m_AutoStart; bool m_MinimizeToTray; diff --git a/src/gui/src/BaseConfig.cpp b/src/gui/src/BaseConfig.cpp index c61b4c5b..0f31493d 100644 --- a/src/gui/src/BaseConfig.cpp +++ b/src/gui/src/BaseConfig.cpp @@ -44,4 +44,3 @@ const char* BaseConfig::m_SwitchCornerNames[] = "bottom-left", "bottom-right" }; - diff --git a/src/gui/src/CommandProcess.cpp b/src/gui/src/CommandProcess.cpp index 57397ae7..c85e847b 100644 --- a/src/gui/src/CommandProcess.cpp +++ b/src/gui/src/CommandProcess.cpp @@ -30,35 +30,34 @@ CommandProcess::CommandProcess(QString cmd, QStringList arguments, QString input QString CommandProcess::run() { QProcess process; - QString standardOutput, standardError; process.setReadChannel(QProcess::StandardOutput); process.start(m_Command, m_Arguments); bool success = process.waitForStarted(); + QString output, error; if (success) { if (!m_Input.isEmpty()) { - process.write(m_Input.toLocal8Bit()); + process.write(m_Input.toStdString().c_str()); } if (process.waitForFinished()) { - standardOutput = QString::fromLocal8Bit(process.readAllStandardOutput().trimmed()); - standardError = QString::fromLocal8Bit(process.readAllStandardError().trimmed()); + output = process.readAllStandardOutput().trimmed(); + error = process.readAllStandardError().trimmed(); } } int code = process.exitCode(); - if (!standardError.isEmpty() || !success || code != 0) + if (!error.isEmpty() || !success || code != 0) { throw std::runtime_error( - std::string( - QString("Code: %1\nError: %2") - .arg(process.exitCode()) - .arg(standardError.isEmpty() ? "Unknown" : standardError) - .toLocal8Bit().constData())); + QString("Code: %1\nError: %2") + .arg(process.exitCode()) + .arg(error.isEmpty() ? "Unknown" : error) + .toStdString()); } emit finished(); - return standardOutput; + return output; } diff --git a/src/gui/src/Fingerprint.cpp b/src/gui/src/Fingerprint.cpp deleted file mode 100644 index 0e4588f8..00000000 --- a/src/gui/src/Fingerprint.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "Fingerprint.h" -#include "QUtility.h" - -#include -#include - -static const char kDirName[] = "SSL/Fingerprints"; -static const char kLocalFilename[] = "Local.txt"; -static const char kTrustedServersFilename[] = "TrustedServers.txt"; -static const char kTrustedClientsFilename[] = "TrustedClients.txt"; - -Fingerprint::Fingerprint(const QString& filename) -{ - m_Filename = filename; -} - -void Fingerprint::trust(const QString& fingerprintText, bool append) -{ - Fingerprint::persistDirectory(); - - QIODevice::OpenMode openMode; - if (append) { - openMode = QIODevice::Append; - } - else { - openMode = QIODevice::WriteOnly; - } - - QFile file(filePath()); - if (file.open(openMode)) - { - QTextStream out(&file); - out << fingerprintText << "\n"; - file.close(); - } -} - -bool Fingerprint::fileExists() const -{ - QString dirName = Fingerprint::directoryPath(); - if (!QDir(dirName).exists()) { - return false; - } - - QFile file(filePath()); - return file.exists(); -} - -bool Fingerprint::isTrusted(const QString& fingerprintText) -{ - QStringList list = readList(); - for (QString trusted : list) { - if (trusted == fingerprintText) { - return true; - } - } - return false; -} - -QStringList Fingerprint::readList(const int readTo) -{ - QStringList list; - - QString dirName = Fingerprint::directoryPath(); - if (!QDir(dirName).exists()) { - return list; - } - - QFile file(filePath()); - - if (file.open(QIODevice::ReadOnly)) - { - QTextStream in(&file); - while (!in.atEnd()) - { - list.append(in.readLine()); - if (list.size() == readTo) { - break; - } - } - file.close(); - } - - return list; -} - -QString Fingerprint::readFirst() -{ - QStringList list = readList(1); - return list.at(0); -} - -QString Fingerprint::filePath() const -{ - QString dir = Fingerprint::directoryPath(); - return QString("%1/%2").arg(dir).arg(m_Filename); -} - -void Fingerprint::persistDirectory() -{ - QDir dir(Fingerprint::directoryPath()); - if (!dir.exists()) { - dir.mkpath("."); - } -} - -QString Fingerprint::directoryPath() -{ - return QString("%1/%2") - .arg(profilePath()) - .arg(kDirName); -} - -Fingerprint Fingerprint::local() -{ - return Fingerprint(kLocalFilename); -} - -Fingerprint Fingerprint::trustedServers() -{ - return Fingerprint(kTrustedServersFilename); -} - -Fingerprint Fingerprint::trustedClients() -{ - return Fingerprint(kTrustedClientsFilename); -} diff --git a/src/gui/src/Fingerprint.h b/src/gui/src/Fingerprint.h deleted file mode 100644 index 5a38d201..00000000 --- a/src/gui/src/Fingerprint.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -class Fingerprint -{ -public: - void trust(const QString& fingerprintText, bool append = true); - bool isTrusted(const QString& fingerprintText); - QStringList readList(const int readTo = -1); - QString readFirst(); - QString filePath() const; - bool fileExists() const; - - static Fingerprint local(); - static Fingerprint trustedServers(); - static Fingerprint trustedClients(); - static QString directoryPath(); - static void persistDirectory(); - -private: - Fingerprint(const QString& filename); - - QString m_Filename; -}; diff --git a/src/gui/src/FingerprintAcceptDialog.cpp b/src/gui/src/FingerprintAcceptDialog.cpp new file mode 100644 index 00000000..e0dc7e60 --- /dev/null +++ b/src/gui/src/FingerprintAcceptDialog.cpp @@ -0,0 +1,65 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "FingerprintAcceptDialog.h" +#include "ui_FingerprintAcceptDialog.h" +#include "net/SecureUtils.h" + +FingerprintAcceptDialog::FingerprintAcceptDialog(QWidget *parent, + BarrierType type, + const barrier::FingerprintData& fingerprint_sha1, + const barrier::FingerprintData& fingerprint_sha256) : + QDialog(parent), + ui_{std::make_unique()} +{ + ui_->setupUi(this); + + if (type == BarrierType::Server) { + ui_->label_sha1->hide(); + ui_->label_sha1_fingerprint_full->hide(); + } else { + ui_->label_sha1_fingerprint_full->setText( + QString::fromStdString(barrier::format_ssl_fingerprint(fingerprint_sha1.data))); + } + + ui_->label_sha256_fingerprint_full->setText( + QString::fromStdString(barrier::format_ssl_fingerprint_columns(fingerprint_sha256.data))); + ui_->label_sha256_fingerprint_randomart->setText( + QString::fromStdString(barrier::create_fingerprint_randomart(fingerprint_sha256.data))); + + QString explanation; + if (type == BarrierType::Server) { + explanation = tr("This is a client fingerprint. You should compare this " + "fingerprint to the one on your client's screen. If the " + "two don't match exactly, then it's probably not the client " + "you're expecting (it could be a malicious user).\n\n" + "To automatically trust this fingerprint for future " + "connections, click Yes. To reject this fingerprint and " + "disconnect the client, click No."); + } else { + explanation = tr("This is a server fingerprint. You should compare this " + "fingerprint to the one on your server's screen. If the " + "two don't match exactly, then it's probably not the server " + "you're expecting (it could be a malicious user).\n\n" + "To automatically trust this fingerprint for future " + "connections, click Yes. To reject this fingerprint and " + "disconnect from the server, click No."); + } + ui_->label_explanation->setText(explanation); +} + +FingerprintAcceptDialog::~FingerprintAcceptDialog() = default; diff --git a/src/gui/src/FingerprintAcceptDialog.h b/src/gui/src/FingerprintAcceptDialog.h new file mode 100644 index 00000000..da8884c9 --- /dev/null +++ b/src/gui/src/FingerprintAcceptDialog.h @@ -0,0 +1,45 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_GUI_FINGERPRINT_ACCEPT_DIALOG_H +#define BARRIER_GUI_FINGERPRINT_ACCEPT_DIALOG_H + +#include "net/FingerprintData.h" +#include "barrier/BarrierType.h" +#include +#include + +namespace Ui { +class FingerprintAcceptDialog; +} + +class FingerprintAcceptDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FingerprintAcceptDialog(QWidget* parent, + BarrierType type, + const barrier::FingerprintData& fingerprint_sha1, + const barrier::FingerprintData& fingerprint_sha256); + ~FingerprintAcceptDialog() override; + +private: + std::unique_ptr ui_; +}; + +#endif // BARRIER_GUI_FINGERPRINT_ACCEPT_DIALOG_H diff --git a/src/gui/src/FingerprintAcceptDialog.ui b/src/gui/src/FingerprintAcceptDialog.ui new file mode 100644 index 00000000..9c181ec3 --- /dev/null +++ b/src/gui/src/FingerprintAcceptDialog.ui @@ -0,0 +1,174 @@ + + + FingerprintAcceptDialog + + + + 0 + 0 + 600 + 400 + + + + + 0 + 0 + + + + Security question + + + + QLayout::SetFixedSize + + + + + Qt::Horizontal + + + QDialogButtonBox::No|QDialogButtonBox::Yes + + + + + + + + 0 + 0 + + + + SHA1 (deprecated, compare to old servers only) + + + + + + + + 0 + 0 + + + + + + + true + + + 10 + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + Do you trust this fingerprint? + + + + + + + + 0 + 0 + + + + + Courier + 75 + true + + + + + + + Qt::AlignCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + Qt::AlignCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA256: + + + + + + + + + buttonBox + accepted() + FingerprintAcceptDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FingerprintAcceptDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/gui/src/IpcClient.cpp b/src/gui/src/IpcClient.cpp index b8b1bcb1..1b0e147a 100644 --- a/src/gui/src/IpcClient.cpp +++ b/src/gui/src/IpcClient.cpp @@ -104,12 +104,14 @@ void IpcClient::sendCommand(const QString& command, ElevateMode const elevate) stream.writeRawData(kIpcMsgCommand, 4); - QByteArray utf8Command = command.toUtf8(); + std::string stdStringCommand = command.toStdString(); + const char* charCommand = stdStringCommand.c_str(); + int length = (int)strlen(charCommand); char lenBuf[4]; - intToBytes(utf8Command.size(), lenBuf, 4); + intToBytes(length, lenBuf, 4); stream.writeRawData(lenBuf, 4); - stream.writeRawData(utf8Command.constData(), utf8Command.size()); + stream.writeRawData(charCommand, length); char elevateBuf[1]; // Refer to enum ElevateMode documentation for why this flag is mapped this way diff --git a/src/gui/src/IpcClient.h b/src/gui/src/IpcClient.h index cd398b3d..6040029c 100644 --- a/src/gui/src/IpcClient.h +++ b/src/gui/src/IpcClient.h @@ -28,7 +28,7 @@ class IpcReader; class IpcClient : public QObject { - Q_OBJECT + Q_OBJECT public: IpcClient(); diff --git a/src/gui/src/KeySequence.h b/src/gui/src/KeySequence.h index 9c73029a..03310918 100644 --- a/src/gui/src/KeySequence.h +++ b/src/gui/src/KeySequence.h @@ -54,4 +54,3 @@ class KeySequence }; #endif - diff --git a/src/gui/src/KeySequenceWidget.h b/src/gui/src/KeySequenceWidget.h index 42dc746d..636375b3 100644 --- a/src/gui/src/KeySequenceWidget.h +++ b/src/gui/src/KeySequenceWidget.h @@ -78,4 +78,3 @@ class KeySequenceWidget : public QPushButton }; #endif - diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index a3962aa8..d17548a4 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -20,17 +20,21 @@ #include "MainWindow.h" -#include "Fingerprint.h" #include "AboutDialog.h" #include "ServerConfigDialog.h" #include "SettingsDialog.h" #include "ZeroconfService.h" #include "DataDownloader.h" #include "CommandProcess.h" +#include "FingerprintAcceptDialog.h" #include "QUtility.h" #include "ProcessorArch.h" #include "SslCertificate.h" #include "ShutdownCh.h" +#include "base/String.h" +#include "common/DataDirectories.h" +#include "net/FingerprintDatabase.h" +#include "net/SecureUtils.h" #include #include @@ -155,9 +159,22 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pComboServerList->hide(); m_pLabelPadlock->hide(); + frame_fingerprint_details->hide(); updateSSLFingerprint(); + connect(toolbutton_show_fingerprint, &QToolButton::clicked, [this](bool checked) + { + m_fingerprint_expanded = !m_fingerprint_expanded; + if (m_fingerprint_expanded) { + frame_fingerprint_details->show(); + toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::UpArrow); + } else { + frame_fingerprint_details->hide(); + toolbutton_show_fingerprint->setArrowType(Qt::ArrowType::DownArrow); + } + }); + // resize window to smallest reasonable size resize(0, 0); } @@ -411,41 +428,57 @@ void MainWindow::checkConnected(const QString& line) void MainWindow::checkFingerprint(const QString& line) { - QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); + QRegExp fingerprintRegex(".*peer fingerprint \\(SHA1\\): ([A-F0-9:]+) \\(SHA256\\): ([A-F0-9:]+)"); if (!fingerprintRegex.exactMatch(line)) { return; } - QString fingerprint = fingerprintRegex.cap(1); - if (Fingerprint::trustedServers().isTrusted(fingerprint)) { + barrier::FingerprintData fingerprint_sha1 = { + barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA1), + barrier::string::from_hex(fingerprintRegex.cap(1).toStdString()) + }; + + barrier::FingerprintData fingerprint_sha256 = { + barrier::fingerprint_type_to_string(barrier::FingerprintType::SHA256), + barrier::string::from_hex(fingerprintRegex.cap(2).toStdString()) + }; + + bool is_client = barrier_type() == BarrierType::Client; + + auto db_path = is_client + ? barrier::DataDirectories::trusted_servers_ssl_fingerprints_path() + : barrier::DataDirectories::trusted_clients_ssl_fingerprints_path(); + + auto db_dir = db_path.parent_path(); + if (!barrier::fs::exists(db_dir)) { + barrier::fs::create_directories(db_dir); + } + + // We compare only SHA256 fingerprints, but show both SHA1 and SHA256 so that the users can + // still verify fingerprints on old Barrier servers. This way the only time when we are exposed + // to SHA1 vulnerabilities is when the user is reconnecting again. + barrier::FingerprintDatabase db; + db.read(db_path); + if (db.is_trusted(fingerprint_sha256)) { return; } static bool messageBoxAlreadyShown = false; if (!messageBoxAlreadyShown) { - stopBarrier(); + if (is_client) { + stopBarrier(); + } messageBoxAlreadyShown = true; - QMessageBox::StandardButton fingerprintReply = - QMessageBox::information( - this, tr("Security question"), - tr("Do you trust this fingerprint?\n\n" - "%1\n\n" - "This is a server fingerprint. You should compare this " - "fingerprint to the one on your server's screen. If the " - "two don't match exactly, then it's probably not the server " - "you're expecting (it could be a malicious user).\n\n" - "To automatically trust this fingerprint for future " - "connections, click Yes. To reject this fingerprint and " - "disconnect from the server, click No.") - .arg(fingerprint), - QMessageBox::Yes | QMessageBox::No); - - if (fingerprintReply == QMessageBox::Yes) { + FingerprintAcceptDialog dialog{this, barrier_type(), fingerprint_sha1, fingerprint_sha256}; + if (dialog.exec() == QDialog::Accepted) { // restart core process after trusting fingerprint. - Fingerprint::trustedServers().trust(fingerprint); - startBarrier(); + db.add_trusted(fingerprint_sha256); + db.write(db_path); + if (is_client) { + startBarrier(); + } } messageBoxAlreadyShown = false; @@ -515,8 +548,8 @@ void MainWindow::startBarrier() #endif - if (m_AppConfig->getCryptoEnabled()) { - args << "--enable-crypto"; + if (!m_AppConfig->getCryptoEnabled()) { + args << "--disable-crypto"; } #if defined(Q_OS_WIN) @@ -524,11 +557,11 @@ void MainWindow::startBarrier() // launched the process (e.g. when launched with elevation). setting the // profile dir on launch ensures it uses the same profile dir is used // no matter how its relaunched. - args << "--profile-dir" << QString("\"%1\"").arg(profilePath()); + args << "--profile-dir" << QString::fromStdString("\"" + barrier::DataDirectories::profile().u8string() + "\""); #endif - if ((barrierType() == barrierClient && !clientArgs(args, app)) - || (barrierType() == barrierServer && !serverArgs(args, app))) + if ((barrier_type() == BarrierType::Client && !clientArgs(args, app)) + || (barrier_type() == BarrierType::Server && !serverArgs(args, app))) { stopBarrier(); return; @@ -543,7 +576,7 @@ void MainWindow::startBarrier() m_pLogWindow->startNewInstance(); - appendLogInfo("starting " + QString(barrierType() == barrierServer ? "server" : "client")); + appendLogInfo("starting " + QString(barrier_type() == BarrierType::Server ? "server" : "client")); qDebug() << args; @@ -624,7 +657,7 @@ QString MainWindow::configFilename() if (m_pRadioInternalConfig->isChecked()) { // TODO: no need to use a temporary file, since we need it to - // be permenant (since it'll be used for Windows services, etc). + // be permanent (since it'll be used for Windows services, etc). m_pTempConfigFile = new QTemporaryFile(); if (!m_pTempConfigFile->open()) { @@ -653,6 +686,11 @@ QString MainWindow::configFilename() return filename; } +BarrierType MainWindow::barrier_type() const +{ + return m_pGroupClient->isChecked() ? BarrierType::Client : BarrierType::Server; +} + QString MainWindow::address() { QString address = appConfig().networkInterface(); @@ -689,6 +727,10 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) args << "--log" << appConfig().logFilenameCmd(); } + if (!appConfig().getRequireClientCertificate()) { + args << "--disable-client-cert-checking"; + } + QString configFilename = this->configFilename(); #if defined(Q_OS_WIN) // wrap in quotes in case username contains spaces. @@ -729,7 +771,7 @@ void MainWindow::stopBarrier() void MainWindow::stopService() { - // send empty command to stop service from laucning anything. + // send empty command to stop service from launching anything. m_IpcClient.sendCommand("", appConfig().elevateMode()); } @@ -924,6 +966,14 @@ void MainWindow::changeEvent(QEvent* event) QMainWindow::changeEvent(event); } +bool MainWindow::event(QEvent* event) +{ + if (event->type() == QEvent::LayoutRequest) { + setFixedSize(sizeHint()); + } + return QMainWindow::event(event); +} + void MainWindow::updateZeroconfService() { QMutexLocker locker(&m_UpdateZeroconfMutex); @@ -935,7 +985,7 @@ void MainWindow::updateZeroconfService() m_pZeroconfService = NULL; } - if (m_AppConfig->autoConfig() || barrierType() == barrierServer) { + if (m_AppConfig->autoConfig() || barrier_type() == BarrierType::Server) { m_pZeroconfService = new ZeroconfService(this); } } @@ -964,12 +1014,47 @@ void MainWindow::updateSSLFingerprint() }); m_pSslCertificate->generateCertificate(); } - if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) { - m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst()); - m_pLabelLocalFingerprint->setTextInteractionFlags(Qt::TextSelectableByMouse); - } else { - m_pLabelLocalFingerprint->setText("Disabled"); + + toolbutton_show_fingerprint->setEnabled(false); + m_pLabelLocalFingerprint->setText("Disabled"); + + if (!m_AppConfig->getCryptoEnabled()) { + return; } + + auto local_path = barrier::DataDirectories::local_ssl_fingerprints_path(); + if (!barrier::fs::exists(local_path)) { + return; + } + + barrier::FingerprintDatabase db; + db.read(local_path); + if (db.fingerprints().size() != 2) { + return; + } + + for (const auto& fingerprint : db.fingerprints()) { + if (fingerprint.algorithm == "sha1") { + auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data); + label_sha1_fingerprint_full->setText(QString::fromStdString(fingerprint_str)); + continue; + } + + if (fingerprint.algorithm == "sha256") { + auto fingerprint_str = barrier::format_ssl_fingerprint(fingerprint.data); + fingerprint_str.resize(40); + fingerprint_str += " ..."; + + auto fingerprint_str_cols = barrier::format_ssl_fingerprint_columns(fingerprint.data); + auto fingerprint_randomart = barrier::create_fingerprint_randomart(fingerprint.data); + + m_pLabelLocalFingerprint->setText(QString::fromStdString(fingerprint_str)); + label_sha256_fingerprint_full->setText(QString::fromStdString(fingerprint_str_cols)); + label_sha256_randomart->setText(QString::fromStdString(fingerprint_randomart)); + } + } + + toolbutton_show_fingerprint->setEnabled(true); } void MainWindow::on_m_pGroupClient_toggled(bool on) @@ -1001,7 +1086,7 @@ bool MainWindow::on_m_pButtonBrowseConfigFile_clicked() return false; } -bool MainWindow::on_m_pActionSave_triggered() +bool MainWindow::on_m_pActionSave_triggered() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as..."), QString(), barrierConfigSaveFilter); diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index c115b912..0c582c9f 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -20,6 +20,8 @@ #define MAINWINDOW__H +#include "barrier/BarrierType.h" + #include #include #include @@ -76,12 +78,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase barrierTransfering }; - enum qBarrierType - { - barrierClient, - barrierServer - }; - enum qLevel { Error, Info @@ -98,7 +94,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase public: void setVisible(bool visible); - int barrierType() const { return m_pGroupClient->isChecked() ? barrierClient : barrierServer; } + BarrierType barrier_type() const; int barrierState() const { return m_BarrierState; } QString hostname() const { return m_pLineEditHostname->text(); } QString configFilename(); @@ -157,6 +153,7 @@ public slots: void stopService(); void stopDesktop(); void changeEvent(QEvent* event); + bool event(QEvent* event); void retranslateMenuBar(); #if defined(Q_OS_WIN) bool isServiceRunning(QString name); @@ -202,6 +199,8 @@ public slots: QStringList m_PendingClientNames; LogWindow *m_pLogWindow; + bool m_fingerprint_expanded = false; + private slots: void on_m_pCheckBoxAutoConfig_toggled(bool checked); void on_m_pComboServerList_currentIndexChanged(QString ); @@ -211,4 +210,3 @@ private slots: }; #endif - diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui index 117405ca..88994cf9 100644 --- a/src/gui/src/MainWindowBase.ui +++ b/src/gui/src/MainWindowBase.ui @@ -2,31 +2,20 @@ MainWindowBase - - - 0 - 0 - 600 - 550 - - 0 0 - - - 600 - 0 - - Barrier + + QLayout::SetFixedSize + @@ -66,30 +55,6 @@ - - - - - - - 0 - 0 - - - - SSL Fingerprint: - - - - - - - - - - - - @@ -239,6 +204,107 @@ + + + + + + + 0 + 0 + + + + SSL Fingerprint: + + + + + + + + + + Qt::PlainText + + + + + + + ... + + + Qt::DownArrow + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + QLayout::SetMinimumSize + + + + + + Courier + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA1 (deprecated, compare to old clients and servers only): + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + SHA256: + + + + + + + + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + @@ -253,7 +319,7 @@ - :/res/icons/16x16/padlock.png + :/res/icons/16x16/padlock.png @@ -388,7 +454,7 @@ - Show &Log + Show &Log Show Log @@ -399,7 +465,7 @@ - + diff --git a/src/gui/src/NewScreenWidget.cpp b/src/gui/src/NewScreenWidget.cpp index 7543bd3d..28e99fa4 100644 --- a/src/gui/src/NewScreenWidget.cpp +++ b/src/gui/src/NewScreenWidget.cpp @@ -1,4 +1,4 @@ -/* +/* * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) @@ -45,4 +45,3 @@ void NewScreenWidget::mousePressEvent(QMouseEvent* event) pDrag->exec(Qt::CopyAction, Qt::CopyAction); } - diff --git a/src/gui/src/NewScreenWidget.h b/src/gui/src/NewScreenWidget.h index 03b41abd..6e527952 100644 --- a/src/gui/src/NewScreenWidget.h +++ b/src/gui/src/NewScreenWidget.h @@ -37,4 +37,3 @@ class NewScreenWidget : public QLabel }; #endif - diff --git a/src/gui/src/QBarrierApplication.h b/src/gui/src/QBarrierApplication.h index 1b06ba08..bd348b3a 100644 --- a/src/gui/src/QBarrierApplication.h +++ b/src/gui/src/QBarrierApplication.h @@ -44,4 +44,3 @@ class QBarrierApplication : public QApplication }; #endif - diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 932bf146..2d05fc36 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -19,7 +19,6 @@ #include "ProcessorArch.h" #include "CommandProcess.h" -#include "common/DataDirectories.h" #if defined(Q_OS_LINUX) #include @@ -113,11 +112,3 @@ QString getOSInformation() return result; } - -QString profilePath() -{ - // Get path to current profile directory, properly converted - // from an OS locale std::string to Unicode QString. - auto localePath = DataDirectories::profile(); - return QString::fromLocal8Bit(localePath.c_str(), localePath.size()); -} diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index 7c05fd00..62a60825 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -29,4 +29,3 @@ QString hash(const QString& string); QString getFirstMacAddress(); qProcessorArch getProcessorArch(); QString getOSInformation(); -QString profilePath(); diff --git a/src/gui/src/Screen.h b/src/gui/src/Screen.h index e06056f7..64962214 100644 --- a/src/gui/src/Screen.h +++ b/src/gui/src/Screen.h @@ -105,4 +105,3 @@ QDataStream& operator<<(QDataStream& outStream, const Screen& screen); QDataStream& operator>>(QDataStream& inStream, Screen& screen); #endif - diff --git a/src/gui/src/ScreenSettingsDialog.cpp b/src/gui/src/ScreenSettingsDialog.cpp index 899881ed..9055e356 100644 --- a/src/gui/src/ScreenSettingsDialog.cpp +++ b/src/gui/src/ScreenSettingsDialog.cpp @@ -152,4 +152,3 @@ void ScreenSettingsDialog::on_m_pListAliases_itemSelectionChanged() { m_pButtonRemoveAlias->setEnabled(!m_pListAliases->selectedItems().isEmpty()); } - diff --git a/src/gui/src/ScreenSettingsDialog.h b/src/gui/src/ScreenSettingsDialog.h index ae1181b8..953d1f89 100644 --- a/src/gui/src/ScreenSettingsDialog.h +++ b/src/gui/src/ScreenSettingsDialog.h @@ -50,4 +50,3 @@ class ScreenSettingsDialog : public QDialog, public Ui::ScreenSettingsDialogBase }; #endif - diff --git a/src/gui/src/ScreenSetupModel.cpp b/src/gui/src/ScreenSetupModel.cpp index 405ba227..5c933f81 100644 --- a/src/gui/src/ScreenSetupModel.cpp +++ b/src/gui/src/ScreenSetupModel.cpp @@ -110,7 +110,7 @@ bool ScreenSetupModel::dropMimeData(const QMimeData* data, Qt::DropAction action return false; if (!parent.isValid() || row != -1 || column != -1) - return false; + return false; QByteArray encodedData = data->data(m_MimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); @@ -141,4 +141,3 @@ bool ScreenSetupModel::dropMimeData(const QMimeData* data, Qt::DropAction action return true; } - diff --git a/src/gui/src/ScreenSetupModel.h b/src/gui/src/ScreenSetupModel.h index 8a1a9820..0f558b42 100644 --- a/src/gui/src/ScreenSetupModel.h +++ b/src/gui/src/ScreenSetupModel.h @@ -68,4 +68,3 @@ class ScreenSetupModel : public QAbstractTableModel }; #endif - diff --git a/src/gui/src/ScreenSetupView.cpp b/src/gui/src/ScreenSetupView.cpp index 15795092..5459f23a 100644 --- a/src/gui/src/ScreenSetupView.cpp +++ b/src/gui/src/ScreenSetupView.cpp @@ -225,4 +225,3 @@ QStyleOptionViewItem ScreenSetupView::viewOptions() const option.textElideMode = Qt::ElideMiddle; return option; } - diff --git a/src/gui/src/ScreenSetupView.h b/src/gui/src/ScreenSetupView.h index 55981c78..64968a0b 100644 --- a/src/gui/src/ScreenSetupView.h +++ b/src/gui/src/ScreenSetupView.h @@ -58,4 +58,3 @@ class ScreenSetupView : public QTableView }; #endif - diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index 161b00d7..9f3e2db1 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -138,4 +138,3 @@ enum { }; #endif - diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index a727f331..84c20470 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -76,7 +76,7 @@ void ServerConfigDialog::showEvent(QShowEvent* event) if (!m_Message.isEmpty()) { - // TODO: ideally this massage box should pop up after the dialog is shown + // TODO: ideally this message box should pop up after the dialog is shown QMessageBox::information(this, tr("Configure server"), m_Message); } } diff --git a/src/gui/src/ServerConfigDialog.h b/src/gui/src/ServerConfigDialog.h index de2472ea..91da51ae 100644 --- a/src/gui/src/ServerConfigDialog.h +++ b/src/gui/src/ServerConfigDialog.h @@ -63,4 +63,3 @@ class ServerConfigDialog : public QDialog, public Ui::ServerConfigDialogBase }; #endif - diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 1caeae5d..8d01c777 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -51,6 +51,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : m_pCheckBoxAutoStart->setChecked(appConfig().getAutoStart()); m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray()); m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); + checkbox_require_client_certificate->setChecked(m_appConfig.getRequireClientCertificate()); #if defined(Q_OS_WIN) m_pComboElevate->setCurrentIndex(static_cast(appConfig().elevateMode())); @@ -67,6 +68,7 @@ void SettingsDialog::accept() m_appConfig.setPort(m_pSpinBoxPort->value()); m_appConfig.setNetworkInterface(m_pLineEditInterface->text()); m_appConfig.setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked()); + m_appConfig.setRequireClientCertificate(checkbox_require_client_certificate->isChecked()); m_appConfig.setLogLevel(m_pComboLogLevel->currentIndex()); m_appConfig.setLogToFile(m_pCheckBoxLogToFile->isChecked()); m_appConfig.setLogFilename(m_pLineEditLogFilename->text()); diff --git a/src/gui/src/SettingsDialogBase.ui b/src/gui/src/SettingsDialogBase.ui index 719a84bc..44ed98cb 100644 --- a/src/gui/src/SettingsDialogBase.ui +++ b/src/gui/src/SettingsDialogBase.ui @@ -142,6 +142,16 @@ Networking + + + + &Address: + + + m_pLineEditInterface + + + @@ -171,16 +181,6 @@ - - - - &Address: - - - m_pLineEditInterface - - - @@ -188,13 +188,20 @@ - + Enable &SSL + + + + Require client certificate + + + @@ -216,19 +223,20 @@ false - - - - - 75 - 0 - + + + + Log to file: + + + + + + + false - &Logging level: - - - m_pComboLogLevel + Browse... @@ -271,10 +279,19 @@ - - + + + + + 75 + 0 + + - Log to file: + &Logging level: + + + m_pComboLogLevel @@ -285,16 +302,6 @@ - - - - false - - - Browse... - - - diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index cd13c7c4..262d63c8 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -37,7 +37,7 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : #elif defined(Q_OS_WIN) - // when areo is disabled on windows, the next/back buttons + // when aero is disabled on windows, the next/back buttons // are hidden (must be a qt bug) -- resizing the window // to +1 of the original height seems to fix this. // NOTE: calling setMinimumSize after this will break diff --git a/src/gui/src/ShutdownCh.h b/src/gui/src/ShutdownCh.h index 2462cae7..54ec84d4 100644 --- a/src/gui/src/ShutdownCh.h +++ b/src/gui/src/ShutdownCh.h @@ -19,4 +19,3 @@ // included in both the GUI and the child apps (server & client) const char ShutdownCh = 'S'; - diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index de3eea67..65ac08a6 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -16,12 +16,11 @@ */ #include "SslCertificate.h" -#include "Fingerprint.h" -#include "QUtility.h" - -#include -#include -#include +#include "common/DataDirectories.h" +#include "base/finally.h" +#include "io/filesystem.h" +#include "net/FingerprintDatabase.h" +#include "net/SecureUtils.h" #include #include @@ -29,198 +28,90 @@ #include #include -static const char kCertificateLifetime[] = "365"; -static const char kCertificateSubjectInfo[] = "/CN=Barrier"; -static const char kCertificateFilename[] = "Barrier.pem"; -static const char kSslDir[] = "SSL"; -static const char kUnixOpenSslCommand[] = "openssl"; - -#if defined(Q_OS_WIN) -static const char kWinOpenSslBinary[] = "openssl.exe"; -static const char kConfigFile[] = "barrier.conf"; -#endif - SslCertificate::SslCertificate(QObject *parent) : QObject(parent) { - m_ProfileDir = profilePath(); - if (m_ProfileDir.isEmpty()) { + if (barrier::DataDirectories::profile().empty()) { emit error(tr("Failed to get profile directory.")); } } -std::pair SslCertificate::runTool(const QStringList& args) -{ - QString program; -#if defined(Q_OS_WIN) - program = QCoreApplication::applicationDirPath(); - program.append("\\").append(kWinOpenSslBinary); -#else - program = kUnixOpenSslCommand; -#endif - - - QStringList environment; -#if defined(Q_OS_WIN) - environment << QString("OPENSSL_CONF=%1\\%2") - .arg(QCoreApplication::applicationDirPath()) - .arg(kConfigFile); -#endif - - QProcess process; - QString standardOutput, standardError; - process.setEnvironment(environment); - process.start(program, args); - bool success = process.waitForStarted(); - - if (success && process.waitForFinished()) - { - standardOutput = QString::fromLocal8Bit(process.readAllStandardOutput().trimmed()); - standardError = QString::fromLocal8Bit(process.readAllStandardError().trimmed()); - } - - int code = process.exitCode(); - if (!success || code != 0) - { - emit error( - QString("SSL tool failed: %1\n\nCode: %2\nError: %3") - .arg(program) - .arg(process.exitCode()) - .arg(standardError.isEmpty() ? "Unknown" : standardError)); - return {false, standardOutput}; - } - - return {true, standardOutput}; -} - void SslCertificate::generateCertificate() { - auto filename = getCertificatePath(); + auto cert_path = barrier::DataDirectories::ssl_certificate_path(); - QFile file(filename); - if (!file.exists() || !isCertificateValid(filename)) { - QStringList arguments; + if (!barrier::fs::exists(cert_path) || !is_certificate_valid(cert_path)) { + try { + auto cert_dir = cert_path.parent_path(); + if (!barrier::fs::exists(cert_dir)) { + barrier::fs::create_directories(cert_dir); + } - // self signed certificate - arguments.append("req"); - arguments.append("-x509"); - arguments.append("-nodes"); - - // valide duration - arguments.append("-days"); - arguments.append(kCertificateLifetime); - - // subject information - arguments.append("-subj"); - - QString subInfo(kCertificateSubjectInfo); - arguments.append(subInfo); - - // private key - arguments.append("-newkey"); - arguments.append("rsa:2048"); - - QDir sslDir(getCertificateDirectory()); - if (!sslDir.exists()) { - sslDir.mkpath("."); - } - - // key output filename - arguments.append("-keyout"); - arguments.append(filename); - - // certificate output filename - arguments.append("-out"); - arguments.append(filename); - - if (!runTool(arguments).first) { + barrier::generate_pem_self_signed_cert(cert_path.u8string()); + } catch (const std::exception& e) { + emit error(QString("SSL tool failed: %1").arg(e.what())); return; } emit info(tr("SSL certificate generated.")); } - generateFingerprint(filename); + generate_fingerprint(cert_path); emit generateFinished(); } -void SslCertificate::generateFingerprint(const QString& certificateFilename) +void SslCertificate::generate_fingerprint(const barrier::fs::path& cert_path) { - QStringList arguments; - arguments.append("x509"); - arguments.append("-fingerprint"); - arguments.append("-sha1"); - arguments.append("-noout"); - arguments.append("-in"); - arguments.append(certificateFilename); + try { + auto local_path = barrier::DataDirectories::local_ssl_fingerprints_path(); + auto local_dir = local_path.parent_path(); + if (!barrier::fs::exists(local_dir)) { + barrier::fs::create_directories(local_dir); + } - auto ret = runTool(arguments); - bool success = ret.first; - if (!success) { - return; - } + barrier::FingerprintDatabase db; + db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path.u8string(), + barrier::FingerprintType::SHA1)); + db.add_trusted(barrier::get_pem_file_cert_fingerprint(cert_path.u8string(), + barrier::FingerprintType::SHA256)); + db.write(local_path); - // find the fingerprint from the tool output - QString fingerprint = ret.second; - auto i = fingerprint.indexOf('='); - if (i != -1) { - fingerprint.remove(0, i+1); - - Fingerprint::local().trust(fingerprint, false); emit info(tr("SSL fingerprint generated.")); - } - else { - emit error(tr("Failed to find SSL fingerprint.")); + } catch (const std::exception& e) { + emit error(tr("Failed to find SSL fingerprint.") + e.what()); } } -QString SslCertificate::getCertificatePath() -{ - return getCertificateDirectory() + QDir::separator() + kCertificateFilename; -} - -QString SslCertificate::getCertificateDirectory() -{ - return m_ProfileDir + QDir::separator() + kSslDir; -} - -bool SslCertificate::isCertificateValid(const QString& path) +bool SslCertificate::is_certificate_valid(const barrier::fs::path& path) { OpenSSL_add_all_algorithms(); - ERR_load_BIO_strings(); ERR_load_crypto_strings(); - BIO* bio = BIO_new(BIO_s_file()); - - auto ret = BIO_read_filename(bio, path.toLocal8Bit().constData()); - if (!ret) { + auto fp = barrier::fopen_utf8_path(path, "r"); + if (!fp) { emit info(tr("Could not read from default certificate file.")); - BIO_free_all(bio); return false; } + auto file_close = barrier::finally([fp]() { std::fclose(fp); }); - X509* cert = PEM_read_bio_X509(bio, NULL, 0, NULL); + auto* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr); if (!cert) { emit info(tr("Error loading default certificate file to memory.")); - BIO_free_all(bio); return false; } + auto cert_free = barrier::finally([cert]() { X509_free(cert); }); - EVP_PKEY* pubkey = X509_get_pubkey(cert); + auto* pubkey = X509_get_pubkey(cert); if (!pubkey) { emit info(tr("Default certificate key file does not contain valid public key")); - X509_free(cert); - BIO_free_all(bio); return false; } + auto pubkey_free = barrier::finally([pubkey]() { EVP_PKEY_free(pubkey); }); auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey)); if (type != EVP_PKEY_RSA && type != EVP_PKEY_DSA) { emit info(tr("Public key in default certificate key file is not RSA or DSA")); - EVP_PKEY_free(pubkey); - X509_free(cert); - BIO_free_all(bio); return false; } @@ -228,14 +119,8 @@ bool SslCertificate::isCertificateValid(const QString& path) if (bits < 2048) { // We could have small keys in old barrier installations emit info(tr("Public key in default certificate key file is too small.")); - EVP_PKEY_free(pubkey); - X509_free(cert); - BIO_free_all(bio); return false; } - EVP_PKEY_free(pubkey); - X509_free(cert); - BIO_free_all(bio); return true; } diff --git a/src/gui/src/SslCertificate.h b/src/gui/src/SslCertificate.h index 3ff3ff5c..eae7fd6c 100644 --- a/src/gui/src/SslCertificate.h +++ b/src/gui/src/SslCertificate.h @@ -19,10 +19,11 @@ #include #include +#include "io/filesystem.h" class SslCertificate : public QObject { -Q_OBJECT + Q_OBJECT public: explicit SslCertificate(QObject *parent = 0); @@ -36,13 +37,7 @@ signals: void generateFinished(); private: - std::pair runTool(const QStringList& args); - void generateFingerprint(const QString& certificateFilename); + void generate_fingerprint(const barrier::fs::path& cert_path); - QString getCertificatePath(); - QString getCertificateDirectory(); - - bool isCertificateValid(const QString& path); -private: - QString m_ProfileDir; + bool is_certificate_valid(const barrier::fs::path& path); }; diff --git a/src/gui/src/TrashScreenWidget.cpp b/src/gui/src/TrashScreenWidget.cpp index 4e81d4da..6f2abad7 100644 --- a/src/gui/src/TrashScreenWidget.cpp +++ b/src/gui/src/TrashScreenWidget.cpp @@ -40,4 +40,3 @@ void TrashScreenWidget::dropEvent(QDropEvent* event) else event->ignore(); } - diff --git a/src/gui/src/TrashScreenWidget.h b/src/gui/src/TrashScreenWidget.h index 676b44fb..967f73d5 100644 --- a/src/gui/src/TrashScreenWidget.h +++ b/src/gui/src/TrashScreenWidget.h @@ -39,4 +39,3 @@ class TrashScreenWidget : public QLabel }; #endif - diff --git a/src/gui/src/ZeroconfService.cpp b/src/gui/src/ZeroconfService.cpp index f7b10007..fbb0ea10 100644 --- a/src/gui/src/ZeroconfService.cpp +++ b/src/gui/src/ZeroconfService.cpp @@ -66,7 +66,7 @@ ZeroconfService::ZeroconfService(MainWindow* mainWindow) : m_ServiceRegistered(false) { silence_avahi_warning(); - if (m_pMainWindow->barrierType() == MainWindow::barrierServer) { + if (m_pMainWindow->barrier_type() == BarrierType::Server) { if (registerService(true)) { m_pZeroconfBrowser = new ZeroconfBrowser(this); connect(m_pZeroconfBrowser, SIGNAL( diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 73251d52..776b44de 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -97,6 +97,12 @@ int main(int argc, char* argv[]) QApplication::setQuitOnLastWindowClosed(false); + if (QGuiApplication::platformName() == "wayland") { + QMessageBox::warning( + NULL, "Barrier", + "You are using wayland session, which is currently not fully supported by Barrier."); + } + QSettings settings; AppConfig appConfig (&settings); diff --git a/src/lib/arch/ArchConsoleStd.cpp b/src/lib/arch/ArchConsoleStd.cpp index adb2d010..2e188bb5 100644 --- a/src/lib/arch/ArchConsoleStd.cpp +++ b/src/lib/arch/ArchConsoleStd.cpp @@ -30,4 +30,4 @@ ArchConsoleStd::writeConsole(ELevel level, const char* str) std::cout << str << std::endl; std::cout.flush(); -} \ No newline at end of file +} diff --git a/src/lib/arch/IArchMultithread.h b/src/lib/arch/IArchMultithread.h index a9051799..6a57694d 100644 --- a/src/lib/arch/IArchMultithread.h +++ b/src/lib/arch/IArchMultithread.h @@ -19,6 +19,7 @@ #pragma once #include "common/IInterface.h" +#include /*! \class ArchCondImpl @@ -71,7 +72,7 @@ barrier. Each architecture must implement this interface. class IArchMultithread : public IInterface { public: //! Type of thread entry point - typedef void* (*ThreadFunc)(void*); + typedef void (*ThreadFunc)(void*); //! Type of thread identifier typedef unsigned int ThreadID; //! Types of signals @@ -160,7 +161,7 @@ public: Creates and starts a new thread, using \c func as the entry point and passing it \c userData. The thread is an opaque data type. */ - virtual ArchThread newThread(ThreadFunc func, void* userData) = 0; + virtual ArchThread newThread(const std::function& func) = 0; //! Get a reference to the calling thread /*! @@ -235,15 +236,6 @@ public: */ virtual bool isExitedThread(ArchThread thread) = 0; - //! Returns the exit code of a thread - /*! - Waits indefinitely for \c thread to exit (if it hasn't yet) then - returns the thread's exit code. - - (Cancellation point) - */ - virtual void* getResultOfThread(ArchThread thread) = 0; - //! Returns an ID for a thread /*! Returns some ID number for \c thread. This is for logging purposes. diff --git a/src/lib/arch/IArchString.h b/src/lib/arch/IArchString.h index ad16fbea..f1803d8d 100644 --- a/src/lib/arch/IArchString.h +++ b/src/lib/arch/IArchString.h @@ -46,16 +46,6 @@ public: //! @name manipulators //@{ - //! printf() to limited size buffer with va_list - /*! - This method is equivalent to vsprintf() except it will not write - more than \c n bytes to the buffer, returning -1 if the output - was truncated and the number of bytes written not including the - trailing NUL otherwise. - */ - virtual int vsnprintf(char* str, - int size, const char* fmt, va_list ap); - //! Convert multibyte string to wide character string virtual int convStringMBToWC(wchar_t*, const char*, UInt32 n, bool* errors); diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp index 425b443c..8400f9d8 100644 --- a/src/lib/arch/unix/ArchMultithreadPosix.cpp +++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp @@ -59,24 +59,19 @@ public: int m_refCount; IArchMultithread::ThreadID m_id; pthread_t m_thread; - IArchMultithread::ThreadFunc m_func; - void* m_userData; + std::function func_;; bool m_cancel; bool m_cancelling; bool m_exited; - void* m_result; void* m_networkData; }; ArchThreadImpl::ArchThreadImpl() : m_refCount(1), m_id(0), - m_func(NULL), - m_userData(NULL), m_cancel(false), m_cancelling(false), m_exited(false), - m_result(NULL), m_networkData(NULL) { // do nothing @@ -319,11 +314,8 @@ ArchMultithreadPosix::unlockMutex(ArchMutex mutex) } } -ArchThread -ArchMultithreadPosix::newThread(ThreadFunc func, void* data) +ArchThread ArchMultithreadPosix::newThread(const std::function& func) { - assert(func != NULL); - // initialize signal handler. we do this here instead of the // constructor so we can avoid daemonizing (using fork()) // when there are multiple threads. clients can safely @@ -341,8 +333,7 @@ ArchMultithreadPosix::newThread(ThreadFunc func, void* data) // create thread impl for new thread ArchThreadImpl* thread = new ArchThreadImpl; - thread->m_func = func; - thread->m_userData = data; + thread->func_ = func; // create the thread. pthread_create() on RedHat 7.2 smp fails // if passed a NULL attr so use a default attr. @@ -389,7 +380,7 @@ ArchMultithreadPosix::closeThread(ArchThread thread) // decrement ref count and clean up thread if no more references if (--thread->m_refCount == 0) { // detach from thread (unless it's the main thread) - if (thread->m_func != NULL) { + if (thread->func_) { pthread_detach(thread->m_thread); } @@ -526,13 +517,6 @@ ArchMultithreadPosix::isExitedThread(ArchThread thread) return thread->m_exited; } -void* -ArchMultithreadPosix::getResultOfThread(ArchThread thread) -{ - std::lock_guard lock(m_threadMutex); - return thread->m_result; -} - IArchMultithread::ThreadID ArchMultithreadPosix::getIDOfThread(ArchThread thread) { @@ -699,10 +683,8 @@ ArchMultithreadPosix::doThreadFunc(ArchThread thread) std::lock_guard lock(m_threadMutex); } - void* result = NULL; try { - // go - result = (*thread->m_func)(thread->m_userData); + thread->func_(); } catch (XThreadCancel&) { @@ -721,7 +703,6 @@ ArchMultithreadPosix::doThreadFunc(ArchThread thread) // thread has exited { std::lock_guard lock(m_threadMutex); - thread->m_result = result; thread->m_exited = true; } diff --git a/src/lib/arch/unix/ArchMultithreadPosix.h b/src/lib/arch/unix/ArchMultithreadPosix.h index 7f492415..798147a0 100644 --- a/src/lib/arch/unix/ArchMultithreadPosix.h +++ b/src/lib/arch/unix/ArchMultithreadPosix.h @@ -67,7 +67,7 @@ public: virtual void closeMutex(ArchMutex); virtual void lockMutex(ArchMutex); virtual void unlockMutex(ArchMutex); - virtual ArchThread newThread(ThreadFunc, void*); + virtual ArchThread newThread(const std::function& func); virtual ArchThread newCurrentThread(); virtual ArchThread copyThread(ArchThread); virtual void closeThread(ArchThread); @@ -77,7 +77,6 @@ public: virtual bool wait(ArchThread, double timeout); virtual bool isSameThread(ArchThread, ArchThread); virtual bool isExitedThread(ArchThread); - virtual void* getResultOfThread(ArchThread); virtual ThreadID getIDOfThread(ArchThread); virtual void setSignalHandler(ESignal, SignalFunc, void*); virtual void raiseSignal(ESignal); diff --git a/src/lib/arch/unix/ArchStringUnix.cpp b/src/lib/arch/unix/ArchStringUnix.cpp index cddb8bd8..dbb91c1e 100644 --- a/src/lib/arch/unix/ArchStringUnix.cpp +++ b/src/lib/arch/unix/ArchStringUnix.cpp @@ -25,7 +25,6 @@ // #include "arch/multibyte.h" -#include "arch/vsnprintf.h" ArchStringUnix::ArchStringUnix() { diff --git a/src/lib/arch/vsnprintf.h b/src/lib/arch/vsnprintf.h deleted file mode 100644 index 5422f270..00000000 --- a/src/lib/arch/vsnprintf.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "arch/IArchString.h" - -#if HAVE_VSNPRINTF - -#if !defined(ARCH_VSNPRINTF) -# define ARCH_VSNPRINTF vsnprintf -#endif - -int -IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap) -{ - int n = ::ARCH_VSNPRINTF(str, size, fmt, ap); - if (n > size) { - n = -1; - } - return n; -} - -#elif SYSAPI_UNIX // !HAVE_VSNPRINTF - -#include - -int -IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap) -{ - static FILE* bitbucket = fopen("/dev/null", "w"); - if (bitbucket == NULL) { - // uh oh - if (size > 0) { - str[0] = '\0'; - } - return 0; - } - else { - // count the characters using the bitbucket - int n = vfprintf(bitbucket, fmt, ap); - if (n + 1 <= size) { - // it'll fit so print it into str - vsprintf(str, fmt, ap); - } - return n; - } -} - -#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX - -#error vsnprintf not implemented - -#endif // !HAVE_VSNPRINTF diff --git a/src/lib/arch/win32/ArchInternetWindows.cpp b/src/lib/arch/win32/ArchInternetWindows.cpp index df08afa7..fb75e7df 100644 --- a/src/lib/arch/win32/ArchInternetWindows.cpp +++ b/src/lib/arch/win32/ArchInternetWindows.cpp @@ -72,7 +72,7 @@ std::string ArchInternetWindows::urlEncode(const std::string& url) std::string result(buffer); - // the win32 url encoding funcitons are pretty useless (to us) and only + // the win32 url encoding functions are pretty useless (to us) and only // escape "unsafe" chars, but not + or =, so we need to replace these // manually (and probably many other chars). barrier::string::findReplaceAll(result, "+", "%2B"); diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index 6ee01fc1..ab16daad 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -443,7 +443,7 @@ ArchMiscWindows::wasLaunchedAsService() bool ArchMiscWindows::getParentProcessName(std::string &name) { PROCESSENTRY32 parentEntry; - if (!getParentProcessEntry(parentEntry)){ + if (!getParentProcessEntry(parentEntry)) { LOG((CLOG_ERR "could not get entry for parent process")); return false; } diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp index 45fa31ef..43a7374a 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.cpp +++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp @@ -49,12 +49,10 @@ public: int m_refCount; HANDLE m_thread; DWORD m_id; - IArchMultithread::ThreadFunc m_func; - void* m_userData; + std::function func_; HANDLE m_cancel; bool m_cancelling; HANDLE m_exit; - void* m_result; void* m_networkData; }; @@ -62,10 +60,7 @@ ArchThreadImpl::ArchThreadImpl() : m_refCount(1), m_thread(NULL), m_id(0), - m_func(NULL), - m_userData(NULL), m_cancelling(false), - m_result(NULL), m_networkData(NULL) { m_exit = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -292,15 +287,13 @@ ArchMultithreadWindows::unlockMutex(ArchMutex mutex) LeaveCriticalSection(&mutex->m_mutex); } -ArchThread -ArchMultithreadWindows::newThread(ThreadFunc func, void* data) +ArchThread ArchMultithreadWindows::newThread(const std::function& func) { lockMutex(m_threadMutex); // create thread impl for new thread ArchThreadImpl* thread = new ArchThreadImpl; - thread->m_func = func; - thread->m_userData = data; + thread->func_ = func; // create thread unsigned int id = 0; @@ -523,15 +516,6 @@ ArchMultithreadWindows::isExitedThread(ArchThread thread) return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0); } -void* -ArchMultithreadWindows::getResultOfThread(ArchThread thread) -{ - lockMutex(m_threadMutex); - void* result = thread->m_result; - unlockMutex(m_threadMutex); - return result; -} - IArchMultithread::ThreadID ArchMultithreadWindows::getIDOfThread(ArchThread thread) { @@ -678,10 +662,8 @@ ArchMultithreadWindows::doThreadFunc(ArchThread thread) lockMutex(m_threadMutex); unlockMutex(m_threadMutex); - void* result = NULL; try { - // go - result = (*thread->m_func)(thread->m_userData); + thread->func_(); } catch (XThreadCancel&) { @@ -695,9 +677,6 @@ ArchMultithreadWindows::doThreadFunc(ArchThread thread) } // thread has exited - lockMutex(m_threadMutex); - thread->m_result = result; - unlockMutex(m_threadMutex); SetEvent(thread->m_exit); // done with thread diff --git a/src/lib/arch/win32/ArchMultithreadWindows.h b/src/lib/arch/win32/ArchMultithreadWindows.h index c413a648..31a2b304 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.h +++ b/src/lib/arch/win32/ArchMultithreadWindows.h @@ -73,7 +73,7 @@ public: virtual void closeMutex(ArchMutex); virtual void lockMutex(ArchMutex); virtual void unlockMutex(ArchMutex); - virtual ArchThread newThread(ThreadFunc, void*); + virtual ArchThread newThread(const std::function& func); virtual ArchThread newCurrentThread(); virtual ArchThread copyThread(ArchThread); virtual void closeThread(ArchThread); @@ -83,7 +83,6 @@ public: virtual bool wait(ArchThread, double timeout); virtual bool isSameThread(ArchThread, ArchThread); virtual bool isExitedThread(ArchThread); - virtual void* getResultOfThread(ArchThread); virtual ThreadID getIDOfThread(ArchThread); virtual void setSignalHandler(ESignal, SignalFunc, void*); virtual void raiseSignal(ESignal); diff --git a/src/lib/arch/win32/ArchStringWindows.cpp b/src/lib/arch/win32/ArchStringWindows.cpp index c570d1be..00336590 100644 --- a/src/lib/arch/win32/ArchStringWindows.cpp +++ b/src/lib/arch/win32/ArchStringWindows.cpp @@ -26,11 +26,6 @@ // ArchStringWindows // -#include "arch/multibyte.h" -#define HAVE_VSNPRINTF 1 -#define ARCH_VSNPRINTF _vsnprintf -#include "arch/vsnprintf.h" - ArchStringWindows::ArchStringWindows() { } diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp index c406124a..bf71b741 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.cpp +++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp @@ -88,7 +88,7 @@ ArchTaskBarWindows::init() // create a window on the current desktop with the current // thread then the current thread won't be able to switch // desktops if it needs to. - m_thread = ARCH->newThread(&ArchTaskBarWindows::threadEntry, this); + m_thread = ARCH->newThread([this]() { threadMainLoop(); }); // wait for child thread while (!m_ready) { @@ -501,14 +501,7 @@ ArchTaskBarWindows::threadMainLoop() UnregisterClass(className, instanceWin32()); } -void* -ArchTaskBarWindows::threadEntry(void* self) -{ - static_cast(self)->threadMainLoop(); - return NULL; -} - HINSTANCE ArchTaskBarWindows::instanceWin32() { return ArchMiscWindows::instanceWin32(); -} \ No newline at end of file +} diff --git a/src/lib/arch/win32/ArchTaskBarWindows.h b/src/lib/arch/win32/ArchTaskBarWindows.h index ee9e000e..2b8b7ad6 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.h +++ b/src/lib/arch/win32/ArchTaskBarWindows.h @@ -84,7 +84,6 @@ private: static LRESULT CALLBACK staticWndProc(HWND, UINT, WPARAM, LPARAM); void threadMainLoop(); - static void* threadEntry(void*); HINSTANCE instanceWin32(); diff --git a/src/lib/arch/win32/XArchWindows.cpp b/src/lib/arch/win32/XArchWindows.cpp index 9391a375..eb3c1513 100644 --- a/src/lib/arch/win32/XArchWindows.cpp +++ b/src/lib/arch/win32/XArchWindows.cpp @@ -57,58 +57,58 @@ XArchEvalWinsock::eval() const noexcept // may not look up network error messages correctly. we'll have // to do it ourself. static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = { - /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, - /* 10009 */{WSAEBADF, "Bad file handle"}, - /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, - /* 10014 */{WSAEFAULT, "WSAEFAULT"}, - /* 10022 */{WSAEINVAL, "WSAEINVAL"}, - /* 10024 */{WSAEMFILE, "No more file descriptors available"}, - /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, - /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, + /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"}, + /* 10009 */{WSAEBADF, "Bad file handle"}, + /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"}, + /* 10014 */{WSAEFAULT, "WSAEFAULT"}, + /* 10022 */{WSAEINVAL, "WSAEINVAL"}, + /* 10024 */{WSAEMFILE, "No more file descriptors available"}, + /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"}, + /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"}, /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"}, /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"}, /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"}, /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"}, - /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, - /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, - /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"}, - /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"}, - /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, + /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"}, + /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"}, + /* 10043 */{WSAEPROTONOSUPPORT, "The specified protocol is not supported"}, + /* 10044 */{WSAESOCKTNOSUPPORT, "The specified socket type is not supported by this address family"}, + /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"}, /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"}, /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"}, - /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, - /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, + /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"}, + /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"}, /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"}, - /* 10051 */{WSAENETUNREACH, "The network can't be reached from this host at this time"}, - /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, + /* 10051 */{WSAENETUNREACH, "The network can't be reached from this host at this time"}, + /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"}, /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"}, - /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, - /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"}, - /* 10056 */{WSAEISCONN, "The socket is already connected"}, + /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"}, + /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occurred. The socket cannot be created"}, + /* 10056 */{WSAEISCONN, "The socket is already connected"}, /* 10057 */{WSAENOTCONN, "The socket is not connected"}, - /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, + /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"}, /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"}, - /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, + /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"}, /* 10061 */{WSAECONNREFUSED, "Connection was refused"}, - /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, + /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"}, /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"}, - /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, + /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"}, /* 10065 */{WSAEHOSTUNREACH, "No route to host"}, - /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, + /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"}, /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"}, - /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, - /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, - /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, - /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, - /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"}, - /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, - /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, - /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, - /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, - /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, - /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, - /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, - /* end */{0, NULL} + /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"}, + /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"}, + /* 10070 */{WSAESTALE, "Undocumented WinSock error code"}, + /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"}, + /* 10091 */{WSASYSNOTREADY, "Underlying network subsystem is not ready for network communication"}, + /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"}, + /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"}, + /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"}, + /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"}, + /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"}, + /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"}, + /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"}, + /* end */{0, NULL} }; for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) { diff --git a/src/lib/barrier/App.cpp b/src/lib/barrier/App.cpp index f0aea6a5..2b3eccc3 100644 --- a/src/lib/barrier/App.cpp +++ b/src/lib/barrier/App.cpp @@ -34,7 +34,6 @@ #if SYSAPI_WIN32 #include "base/IEventQueue.h" -#include "base/TMethodJob.h" #endif #include @@ -164,7 +163,7 @@ App::initApp(int argc, const char** argv) // parse command line parseArgs(argc, argv); - DataDirectories::profile(argsBase().m_profileDirectory); + barrier::DataDirectories::profile(argsBase().m_profileDirectory); // set log filter if (!CLOG->setFilter(argsBase().m_logFilter)) { @@ -229,8 +228,7 @@ App::handleIpcMessage(const Event& e, void*) } } -void -App::runEventsLoop(void*) +void App::run_events_loop() { m_events->loop(); diff --git a/src/lib/barrier/App.h b/src/lib/barrier/App.h index ed034b62..8e17a714 100644 --- a/src/lib/barrier/App.h +++ b/src/lib/barrier/App.h @@ -90,7 +90,7 @@ public: ARCH_APP_UTIL& appUtil() { return m_appUtil; } - virtual IArchTaskBarReceiver* taskBarReceiver() const { return m_taskBarReceiver; } + virtual IArchTaskBarReceiver* taskBarReceiver() const { return m_taskBarReceiver; } virtual void setByeFunc(void(*bye)(int)) { m_bye = bye; } virtual void bye(int error) { m_bye(error); } @@ -108,7 +108,7 @@ private: protected: void initIpcClient(); void cleanupIpcClient(); - void runEventsLoop(void*); + void run_events_loop(); IArchTaskBarReceiver* m_taskBarReceiver; bool m_suspended; @@ -166,7 +166,8 @@ private: " -l --log write log messages to file.\n" \ " --no-tray disable the system tray icon.\n" \ " --enable-drag-drop enable file drag & drop.\n" \ - " --enable-crypto enable the crypto (ssl) plugin.\n" \ + " --enable-crypto enable the crypto (ssl) plugin (default, deprecated).\n" \ + " --disable-crypto disable the crypto (ssl) plugin.\n" \ " --profile-dir use named profile directory instead.\n" \ " --drop-dir use named drop target directory instead.\n" diff --git a/src/lib/barrier/ArgParser.cpp b/src/lib/barrier/ArgParser.cpp index 5bf08b6c..99cd803b 100644 --- a/src/lib/barrier/ArgParser.cpp +++ b/src/lib/barrier/ArgParser.cpp @@ -24,7 +24,7 @@ #include "barrier/ArgsBase.h" #include "base/Log.h" #include "base/String.h" -#include "common/PathUtilities.h" +#include "io/filesystem.h" #ifdef WINAPI_MSWINDOWS #include @@ -65,7 +65,9 @@ ArgParser::parseServerArgs(ServerArgs& args, int argc, const char* const* argv) // save screen change script path args.m_screenChangeScript = argv[++i]; } - else { + else if (isArg(i, argc, argv, nullptr, "--disable-client-cert-checking")) { + args.check_client_certificates = false; + } else { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_exename.c_str(), argv[i], args.m_exename.c_str())); return false; } @@ -133,10 +135,10 @@ ArgParser::parseClientArgs(ClientArgs& args, int argc, const char* const* argv) return true; } -bool -ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) -{ #if WINAPI_MSWINDOWS +bool +ArgParser::parseMSWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +{ if (isArg(i, argc, argv, NULL, "--service")) { LOG((CLOG_WARN "obsolete argument --service, use barrierd instead.")); argsBase.m_shouldExit = true; @@ -153,25 +155,46 @@ ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* con } return true; -#elif WINAPI_XWINDOWS +} +#endif + +#if WINAPI_CARBON +bool +ArgParser::parseCarbonArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +{ + // no options for carbon + return false; +} +#endif + +#if WINAPI_XWINDOWS +bool +ArgParser::parseXWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +{ if (isArg(i, argc, argv, "-display", "--display", 1)) { // use alternative display argsBase.m_display = argv[++i]; } - else if (isArg(i, argc, argv, NULL, "--no-xinitthreads")) { argsBase.m_disableXInitThreads = true; - } - - else { + } else { // option not supported here return false; } return true; +} +#endif + +bool +ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i) +{ +#if WINAPI_MSWINDOWS + return parseMSWindowsArg(argsBase, argc, argv, i); #elif WINAPI_CARBON - // no options for carbon - return false; + return parseCarbonArg(argsBase, argc, argv, i); +#elif WINAPI_XWINDOWS + return parseXWindowsArg(argsBase, argc, argv, i); #endif } @@ -261,13 +284,16 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) argsBase().m_dropTarget = argv[++i]; } else if (isArg(i, argc, argv, NULL, "--enable-crypto")) { - argsBase().m_enableCrypto = true; + LOG((CLOG_INFO "--enable-crypto is used by default. The option is deprecated.")); + } + else if (isArg(i, argc, argv, NULL, "--disable-crypto")) { + argsBase().m_enableCrypto = false; } else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) { - argsBase().m_profileDirectory = argv[++i]; + argsBase().m_profileDirectory = barrier::fs::u8path(argv[++i]); } else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) { - argsBase().m_pluginDirectory = argv[++i]; + argsBase().m_pluginDirectory = barrier::fs::u8path(argv[++i]); } else { // option not supported here @@ -352,7 +378,7 @@ ArgParser::splitCommandString(String& command, std::vector& argv) if (space > leftDoubleQuote && space < rightDoubleQuote) { ignoreThisSpace = true; } - else if (space > rightDoubleQuote){ + else if (space > rightDoubleQuote) { searchDoubleQuotes(command, leftDoubleQuote, rightDoubleQuote, rightDoubleQuote + 1); } @@ -463,7 +489,13 @@ void ArgParser::updateCommonArgs(const char* const* argv) { argsBase().m_name = ARCH->getHostName(); - argsBase().m_exename = PathUtilities::basename(argv[0]); + argsBase().m_exename = parse_exename(argv[0]); +} + +std::string ArgParser::parse_exename(const char* arg) +{ + // FIXME: we assume UTF-8 encoding, but on Windows this is not correct + return barrier::fs::u8path(arg).filename().u8string(); } bool diff --git a/src/lib/barrier/ArgParser.h b/src/lib/barrier/ArgParser.h index 239a20cd..472d93a1 100644 --- a/src/lib/barrier/ArgParser.h +++ b/src/lib/barrier/ArgParser.h @@ -48,12 +48,18 @@ public: static String assembleCommand(std::vector& argsArray, String ignoreArg = "", int parametersRequired = 0); + static std::string parse_exename(const char* arg); + private: void updateCommonArgs(const char* const* argv); bool checkUnexpectedArgs(); static ArgsBase& argsBase() { return *m_argsBase; } + bool parseMSWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i); + bool parseCarbonArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i); + bool parseXWindowsArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i); + private: App* m_app; diff --git a/src/lib/barrier/ArgsBase.cpp b/src/lib/barrier/ArgsBase.cpp index dd983c6f..e3e38031 100644 --- a/src/lib/barrier/ArgsBase.cpp +++ b/src/lib/barrier/ArgsBase.cpp @@ -42,8 +42,8 @@ m_enableDragDrop(false), m_dropTarget(""), m_shouldExit(false), m_barrierAddress(), -m_enableCrypto(false), -m_profileDirectory(""), + m_enableCrypto(true), +m_profileDirectory(), m_pluginDirectory("") { } diff --git a/src/lib/barrier/ArgsBase.h b/src/lib/barrier/ArgsBase.h index ad442dc9..cdb50928 100644 --- a/src/lib/barrier/ArgsBase.h +++ b/src/lib/barrier/ArgsBase.h @@ -19,6 +19,7 @@ #pragma once #include "base/String.h" +#include "io/filesystem.h" class ArgsBase { public: @@ -50,6 +51,6 @@ public: bool m_shouldExit; String m_barrierAddress; bool m_enableCrypto; - String m_profileDirectory; - String m_pluginDirectory; + barrier::fs::path m_profileDirectory; + barrier::fs::path m_pluginDirectory; }; diff --git a/src/lib/barrier/BarrierType.h b/src/lib/barrier/BarrierType.h new file mode 100644 index 00000000..0a184773 --- /dev/null +++ b/src/lib/barrier/BarrierType.h @@ -0,0 +1,26 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_BARRIER_BARRIER_TYPE_H +#define BARRIER_LIB_BARRIER_BARRIER_TYPE_H + +enum class BarrierType { + Server, + Client +}; + +#endif // BARRIER_LIB_BARRIER_BARRIER_TYPE_H diff --git a/src/lib/barrier/Chunk.h b/src/lib/barrier/Chunk.h index 07f0360d..26a343df 100644 --- a/src/lib/barrier/Chunk.h +++ b/src/lib/barrier/Chunk.h @@ -17,7 +17,7 @@ #pragma once -#include "common/basic_types.h" +#include class Chunk { public: diff --git a/src/lib/barrier/ClientApp.cpp b/src/lib/barrier/ClientApp.cpp index ec687e1e..4b0ef61b 100644 --- a/src/lib/barrier/ClientApp.cpp +++ b/src/lib/barrier/ClientApp.cpp @@ -37,10 +37,8 @@ #include "base/TMethodEventJob.h" #include "base/log_outputters.h" #include "base/EventQueue.h" -#include "base/TMethodJob.h" #include "base/Log.h" #include "common/Version.h" -#include "common/PathUtilities.h" #if WINAPI_MSWINDOWS #include "platform/MSWindowsScreen.h" @@ -466,10 +464,7 @@ ClientApp::mainLoop() #if defined(MAC_OS_X_VERSION_10_7) - Thread thread( - new TMethodJob( - this, &ClientApp::runEventsLoop, - NULL)); + Thread thread([this](){ run_events_loop(); }); // wait until carbon loop is ready OSXScreen* screen = dynamic_cast( @@ -522,7 +517,7 @@ ClientApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc { // general initialization m_serverAddress = new NetworkAddress; - args().m_exename = PathUtilities::basename(argv[0]); + argsBase().m_exename = ArgParser::parse_exename(argv[0]); // install caller's output filter if (outputter != NULL) { diff --git a/src/lib/barrier/ClientApp.h b/src/lib/barrier/ClientApp.h index 75ceb1ec..d1db8d05 100644 --- a/src/lib/barrier/ClientApp.h +++ b/src/lib/barrier/ClientApp.h @@ -24,7 +24,6 @@ namespace barrier { class Screen; } class Event; class Client; class NetworkAddress; -class Thread; class ClientArgs; class ClientApp : public App { diff --git a/src/lib/barrier/DropHelper.cpp b/src/lib/barrier/DropHelper.cpp index e3e15939..af22b523 100644 --- a/src/lib/barrier/DropHelper.cpp +++ b/src/lib/barrier/DropHelper.cpp @@ -18,6 +18,7 @@ #include "barrier/DropHelper.h" #include "base/Log.h" +#include "io/filesystem.h" #include @@ -35,7 +36,7 @@ DropHelper::writeToDir(const String& destination, DragFileList& fileList, String dropTarget.append("/"); #endif dropTarget.append(fileList.at(0).getFilename()); - file.open(dropTarget.c_str(), std::ios::out | std::ios::binary); + barrier::open_utf8_path(file, dropTarget, std::ios::out | std::ios::binary); if (!file.is_open()) { LOG((CLOG_ERR "drop file failed: can not open %s", dropTarget.c_str())); } diff --git a/src/lib/barrier/IScreenSaver.h b/src/lib/barrier/IScreenSaver.h index 475f9f83..2099f6d1 100644 --- a/src/lib/barrier/IScreenSaver.h +++ b/src/lib/barrier/IScreenSaver.h @@ -56,7 +56,7 @@ public: //! Deactivate screen saver /*! - Deactivate (i.e. hide) the screen saver, reseting the screen saver + Deactivate (i.e. hide) the screen saver, resetting the screen saver timer. */ virtual void deactivate() = 0; diff --git a/src/lib/barrier/PacketStreamFilter.cpp b/src/lib/barrier/PacketStreamFilter.cpp index 85a85f22..5955b6c2 100644 --- a/src/lib/barrier/PacketStreamFilter.cpp +++ b/src/lib/barrier/PacketStreamFilter.cpp @@ -17,6 +17,7 @@ */ #include "barrier/PacketStreamFilter.h" +#include "barrier/protocol_types.h" #include "base/IEventQueue.h" #include "mt/Lock.h" #include "base/TMethodEventJob.h" @@ -133,8 +134,7 @@ PacketStreamFilter::isReadyNoLock() const return (m_size != 0 && m_buffer.getSize() >= m_size); } -void -PacketStreamFilter::readPacketSize() +bool PacketStreamFilter::readPacketSize() { // note -- m_mutex must be locked on entry @@ -146,7 +146,13 @@ PacketStreamFilter::readPacketSize() ((UInt32)buffer[1] << 16) | ((UInt32)buffer[2] << 8) | (UInt32)buffer[3]; + + if (m_size > PROTOCOL_MAX_MESSAGE_LENGTH) { + m_events->addEvent(Event(m_events->forIStream().inputFormatError(), getEventTarget())); + return false; + } } + return true; } bool @@ -160,13 +166,17 @@ PacketStreamFilter::readMore() UInt32 n = getStream()->read(buffer, sizeof(buffer)); while (n > 0) { m_buffer.write(buffer, n); + + // if we don't yet have the next packet size then get it, if possible. + // Note that we can't wait for whole pending data to arrive because it may be huge in + // case of malicious or erroneous peer. + if (!readPacketSize()) { + break; + } + n = getStream()->read(buffer, sizeof(buffer)); } - // if we don't yet have the next packet size then get it, - // if possible. - readPacketSize(); - // note if we now have a whole packet bool isReady = isReadyNoLock(); diff --git a/src/lib/barrier/PacketStreamFilter.h b/src/lib/barrier/PacketStreamFilter.h index 3bccafe0..0c4bb04e 100644 --- a/src/lib/barrier/PacketStreamFilter.h +++ b/src/lib/barrier/PacketStreamFilter.h @@ -47,7 +47,9 @@ protected: private: bool isReadyNoLock() const; - void readPacketSize(); + + // returns false on erroneous packet size + bool readPacketSize(); bool readMore(); private: diff --git a/src/lib/barrier/ProtocolUtil.cpp b/src/lib/barrier/ProtocolUtil.cpp index 6e67b1b6..5a71010d 100644 --- a/src/lib/barrier/ProtocolUtil.cpp +++ b/src/lib/barrier/ProtocolUtil.cpp @@ -19,6 +19,8 @@ #include "barrier/ProtocolUtil.h" #include "io/IStream.h" #include "base/Log.h" +#include "barrier/protocol_types.h" +#include "barrier/XBarrier.h" #include "common/stdvector.h" #include "base/String.h" @@ -159,6 +161,10 @@ ProtocolUtil::vreadf(barrier::IStream* stream, const char* fmt, va_list args) (static_cast(buffer[2]) << 8) | static_cast(buffer[3]); + if (n > PROTOCOL_MAX_LIST_LENGTH) { + throw XBadClient("Too long message received"); + } + // convert it void* v = va_arg(args, void*); switch (len) { @@ -211,6 +217,10 @@ ProtocolUtil::vreadf(barrier::IStream* stream, const char* fmt, va_list args) (static_cast(buffer[2]) << 8) | static_cast(buffer[3]); + if (len > PROTOCOL_MAX_STRING_LENGTH) { + throw XBadClient("Too long message received"); + } + // use a fixed size buffer if its big enough const bool useFixed = (len <= sizeof(buffer)); diff --git a/src/lib/barrier/ServerApp.cpp b/src/lib/barrier/ServerApp.cpp index 599bde70..71158ce3 100644 --- a/src/lib/barrier/ServerApp.cpp +++ b/src/lib/barrier/ServerApp.cpp @@ -34,13 +34,11 @@ #include "base/EventQueue.h" #include "base/log_outputters.h" #include "base/FunctionEventJob.h" -#include "base/TMethodJob.h" #include "base/IEventQueue.h" #include "base/Log.h" #include "base/TMethodEventJob.h" #include "common/Version.h" #include "common/DataDirectories.h" -#include "common/PathUtilities.h" #if SYSAPI_WIN32 #include "arch/win32/ArchMiscWindows.h" @@ -129,11 +127,15 @@ ServerApp::help() #endif // refer to custom profile directory even if not saved yet - String profilePath = argsBase().m_profileDirectory; - if (profilePath.empty()) { - profilePath = DataDirectories::profile(); + barrier::fs::path profile_path = argsBase().m_profileDirectory; + if (profile_path.empty()) { + profile_path = barrier::DataDirectories::profile(); } + auto usr_config_path = (profile_path / barrier::fs::u8path(USR_CONFIG_NAME)).u8string(); + auto sys_config_path = (barrier::DataDirectories::systemconfig() / + barrier::fs::u8path(SYS_CONFIG_NAME)).u8string(); + std::ostringstream buffer; buffer << "Start the barrier server component.\n" << "\n" @@ -145,7 +147,10 @@ ServerApp::help() << "Options:\n" << " -a, --address
listen for clients on the given address.\n" << " -c, --config use the named configuration file instead.\n" - << HELP_COMMON_INFO_1 << WINAPI_INFO << HELP_SYS_INFO << HELP_COMMON_INFO_2 << "\n" + << HELP_COMMON_INFO_1 + << " --disable-client-cert-checking disable client SSL certificate \n" + " checking (deprecated)\n" + << WINAPI_INFO << HELP_SYS_INFO << HELP_COMMON_INFO_2 << "\n" << "Default options are marked with a *\n" << "\n" << "The argument for --address is of the form: [][:]. The\n" @@ -156,8 +161,8 @@ ServerApp::help() << "\n" << "If no configuration file pathname is provided then the first of the\n" << "following to load successfully sets the configuration:\n" - << " " << PathUtilities::concat(profilePath, USR_CONFIG_NAME) << "\n" - << " " << PathUtilities::concat(DataDirectories::systemconfig(), SYS_CONFIG_NAME) << "\n"; + << " " << usr_config_path << "\n" + << " " << sys_config_path << "\n"; LOG((CLOG_PRINT "%s", buffer.str().c_str())); } @@ -194,25 +199,25 @@ ServerApp::loadConfig() // load the default configuration if no explicit file given else { - String path = DataDirectories::profile(); + auto path = barrier::DataDirectories::profile(); if (!path.empty()) { // complete path - path = PathUtilities::concat(path, USR_CONFIG_NAME); + path /= barrier::fs::u8path(USR_CONFIG_NAME); // now try loading the user's configuration - if (loadConfig(path)) { + if (loadConfig(path.u8string())) { loaded = true; - args().m_configFile = path; + args().m_configFile = path.u8string(); } } if (!loaded) { // try the system-wide config file - path = DataDirectories::systemconfig(); + path = barrier::DataDirectories::systemconfig(); if (!path.empty()) { - path = PathUtilities::concat(path, SYS_CONFIG_NAME); - if (loadConfig(path)) { + path /= barrier::fs::u8path(SYS_CONFIG_NAME); + if (loadConfig(path.u8string())) { loaded = true; - args().m_configFile = path; + args().m_configFile = path.u8string(); } } } @@ -652,11 +657,18 @@ ServerApp::handleResume(const Event&, void*) ClientListener* ServerApp::openClientListener(const NetworkAddress& address) { + auto security_level = ConnectionSecurityLevel::PLAINTEXT; + if (args().m_enableCrypto) { + security_level = ConnectionSecurityLevel::ENCRYPTED; + if (args().check_client_certificates) { + security_level = ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED; + } + } + ClientListener* listen = new ClientListener( address, new TCPSocketFactory(m_events, getSocketMultiplexer()), - m_events, - args().m_enableCrypto); + m_events, security_level); m_events->adoptHandler( m_events->forClientListener().connected(), listen, @@ -784,10 +796,7 @@ ServerApp::mainLoop() #if defined(MAC_OS_X_VERSION_10_7) - Thread thread( - new TMethodJob( - this, &ServerApp::runEventsLoop, - NULL)); + Thread thread([this](){ run_events_loop(); }); // wait until carbon loop is ready OSXScreen* screen = dynamic_cast( @@ -832,7 +841,7 @@ ServerApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc // general initialization m_barrierAddress = new NetworkAddress; args().m_config = new Config(m_events); - args().m_exename = PathUtilities::basename(argv[0]); + args().m_exename = ArgParser::parse_exename(argv[0]); // install caller's output filter if (outputter != NULL) { diff --git a/src/lib/barrier/ServerArgs.h b/src/lib/barrier/ServerArgs.h index 6d912331..6323705d 100644 --- a/src/lib/barrier/ServerArgs.h +++ b/src/lib/barrier/ServerArgs.h @@ -30,4 +30,5 @@ public: String m_configFile; Config* m_config; String m_screenChangeScript; + bool check_client_certificates = true; }; diff --git a/src/lib/barrier/StreamChunker.cpp b/src/lib/barrier/StreamChunker.cpp index 45edd302..0f6c0a9d 100644 --- a/src/lib/barrier/StreamChunker.cpp +++ b/src/lib/barrier/StreamChunker.cpp @@ -42,14 +42,13 @@ bool StreamChunker::s_interruptFile = false; Mutex* StreamChunker::s_interruptMutex = NULL; void -StreamChunker::sendFile( - char* filename, +StreamChunker::sendFile(const char* filename, IEventQueue* events, void* eventTarget) { s_isChunkingFile = true; - std::fstream file(static_cast(filename), std::ios::in | std::ios::binary); + std::fstream file(filename, std::ios::in | std::ios::binary); if (!file.is_open()) { throw runtime_error("failed to open file"); diff --git a/src/lib/barrier/StreamChunker.h b/src/lib/barrier/StreamChunker.h index 736d1bde..ed5d83c6 100644 --- a/src/lib/barrier/StreamChunker.h +++ b/src/lib/barrier/StreamChunker.h @@ -25,10 +25,7 @@ class Mutex; class StreamChunker { public: - static void sendFile( - char* filename, - IEventQueue* events, - void* eventTarget); + static void sendFile(const char* filename, IEventQueue* events, void* eventTarget); static void sendClipboard( String& data, size_t size, diff --git a/src/lib/barrier/key_types.cpp b/src/lib/barrier/key_types.cpp index 43fd69b1..33ee3ebc 100644 --- a/src/lib/barrier/key_types.cpp +++ b/src/lib/barrier/key_types.cpp @@ -17,6 +17,7 @@ */ #include "barrier/key_types.h" +#include const KeyNameMapEntry kKeyNameMap[] = { { "AltGr", kKeyAltGr }, diff --git a/src/lib/barrier/protocol_types.h b/src/lib/barrier/protocol_types.h index 7ed0d070..f7306067 100644 --- a/src/lib/barrier/protocol_types.h +++ b/src/lib/barrier/protocol_types.h @@ -20,6 +20,8 @@ #include "base/EventTypes.h" +#include + // protocol version number // 1.0: initial protocol // 1.1: adds KeyCode to key press, release, and repeat @@ -51,6 +53,12 @@ static const double kKeepAlivesUntilDeath = 3.0; static const double kHeartRate = -1.0; static const double kHeartBeatsUntilDeath = 3.0; +// Messages of very large size indicate a likely protocol error. We don't parse such messages and +// drop connection instead. Note that e.g. the clipboard messages are already limited to 32kB. +static constexpr std::uint32_t PROTOCOL_MAX_MESSAGE_LENGTH = 4 * 1024 * 1024; +static constexpr std::uint32_t PROTOCOL_MAX_LIST_LENGTH = 1024 * 1024; +static constexpr std::uint32_t PROTOCOL_MAX_STRING_LENGTH = 1024 * 1024; + // direction constants enum EDirection { kNoDirection, @@ -267,8 +275,8 @@ extern const char* kMsgDSetOptions; // 2 means the file transfer is finished. extern const char* kMsgDFileTransfer; -// drag infomation: primary <-> secondary -// transfer drag infomation. The first 2 bytes are used for storing +// drag information: primary <-> secondary +// transfer drag information. The first 2 bytes are used for storing // the number of dragging objects. Then the following string consists // of each object's directory. extern const char* kMsgDDragInfo; diff --git a/src/lib/barrier/win32/DaemonApp.cpp b/src/lib/barrier/win32/DaemonApp.cpp index f8cf591a..81ec5ff3 100644 --- a/src/lib/barrier/win32/DaemonApp.cpp +++ b/src/lib/barrier/win32/DaemonApp.cpp @@ -28,13 +28,11 @@ #include "net/SocketMultiplexer.h" #include "arch/XArch.h" #include "base/Log.h" -#include "base/TMethodJob.h" #include "base/TMethodEventJob.h" #include "base/EventQueue.h" #include "base/log_outputters.h" #include "base/Log.h" #include "common/DataDirectories.h" -#include "base/Unicode.h" #include "arch/win32/ArchMiscWindows.h" #include "arch/win32/XArchWindows.h" @@ -246,7 +244,7 @@ DaemonApp::logFilename() { string logFilename = ARCH->setting("LogFilename"); if (logFilename.empty()) - logFilename = DataDirectories::global() + "\\" + LOG_FILENAME; + logFilename = (barrier::DataDirectories::global() / LOG_FILENAME).u8string(); MSWindowsUtil::createDirectory(logFilename, true); return logFilename; } @@ -258,7 +256,7 @@ DaemonApp::handleIpcMessage(const Event& e, void*) switch (m->type()) { case kIpcCommand: { IpcCommandMessage* cm = static_cast(m); - String command = Unicode::UTF8ToText(cm->command()); + String command = cm->command(); // if empty quotes, clear. if (command == "\"\"") { diff --git a/src/lib/base/Event.h b/src/lib/base/Event.h index 38a2cf11..cb00dccb 100644 --- a/src/lib/base/Event.h +++ b/src/lib/base/Event.h @@ -21,6 +21,8 @@ #include "common/basic_types.h" #include "common/stdmap.h" +#include + class EventData { public: EventData() { } diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index d54a2d50..173a0a97 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -56,6 +56,7 @@ REGISTER_EVENT(IStream, outputFlushed) REGISTER_EVENT(IStream, outputError) REGISTER_EVENT(IStream, inputShutdown) REGISTER_EVENT(IStream, outputShutdown) +REGISTER_EVENT(IStream, inputFormatError) // // IpcClient diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index 8b02bef4..995490e9 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -133,6 +133,11 @@ public: */ Event::Type outputShutdown(); + /** Get input format error event type + + This is sent when a stream receives an irrecoverable input format error. + */ + Event::Type inputFormatError(); //@} private: @@ -141,6 +146,7 @@ private: Event::Type m_outputError; Event::Type m_inputShutdown; Event::Type m_outputShutdown; + Event::Type m_inputFormatError; }; class IpcClientEvents : public EventTypes { diff --git a/src/lib/base/FunctionJob.cpp b/src/lib/base/FunctionJob.cpp deleted file mode 100644 index 2d7ef679..00000000 --- a/src/lib/base/FunctionJob.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "base/FunctionJob.h" - -// -// FunctionJob -// - -FunctionJob::FunctionJob(void (*func)(void*), void* arg) : - m_func(func), - m_arg(arg) -{ - // do nothing -} - -FunctionJob::~FunctionJob() -{ - // do nothing -} - -void -FunctionJob::run() -{ - if (m_func != NULL) { - m_func(m_arg); - } -} diff --git a/src/lib/base/FunctionJob.h b/src/lib/base/FunctionJob.h deleted file mode 100644 index 39c96449..00000000 --- a/src/lib/base/FunctionJob.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "base/IJob.h" - -//! Use a function as a job -/*! -A job class that invokes a function. -*/ -class FunctionJob : public IJob { -public: - //! run() invokes \c func(arg) - FunctionJob(void (*func)(void*), void* arg = NULL); - virtual ~FunctionJob(); - - // IJob overrides - virtual void run(); - -private: - void (*m_func)(void*); - void* m_arg; -}; diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp index 6a43521d..8f52a805 100644 --- a/src/lib/base/Log.cpp +++ b/src/lib/base/Log.cpp @@ -62,7 +62,7 @@ Log::Log() { assert(s_log == NULL); - // other initalization + // other initialization m_maxPriority = g_defaultMaxPriority; m_maxNewlineLength = 0; insert(new ConsoleLogOutputter); @@ -145,7 +145,7 @@ Log::print(const char* file, int line, const char* fmt, ...) // try printing into the buffer va_list args; va_start(args, fmt); - int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args); + int n = std::vsnprintf(buffer, len - sPad, fmt, args); va_end(args); // if the buffer wasn't big enough then make it bigger and try again diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 389ca8aa..adbb11db 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -35,6 +35,42 @@ namespace barrier { namespace string { +namespace { + +// returns negative in case of non-matching character +int hex_to_number(char ch) +{ + switch (ch) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + + case 'A': return 10; + case 'B': return 11; + case 'C': return 12; + case 'D': return 13; + case 'E': return 14; + case 'F': return 15; + } + return -1; +} + +} // namespace + std::string format(const char* fmt, ...) { @@ -135,7 +171,7 @@ sprintf(const char* fmt, ...) // try printing into the buffer va_list args; va_start(args, fmt); - int n = ARCH->vsnprintf(buffer, len, fmt, args); + int n = std::vsnprintf(buffer, len, fmt, args); va_end(args); // if the buffer wasn't big enough then make it bigger and try again @@ -185,16 +221,42 @@ removeFileExt(std::string filename) return filename.substr(0, dot); } -void -toHex(std::string& subject, int width, const char fill) +std::string to_hex(const std::vector& subject, int width, const char fill) { std::stringstream ss; ss << std::hex; - for (unsigned int i = 0; i < subject.length(); i++) { - ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i]; + for (unsigned int i = 0; i < subject.size(); i++) { + ss << std::setw(width) << std::setfill(fill) << static_cast(subject[i]); } - subject = ss.str(); + return ss.str(); +} + +std::vector from_hex(const std::string& data) +{ + std::vector result; + result.reserve(data.size() / 2); + + std::size_t i = 0; + while (i < data.size()) { + if (data[i] == ':') { + i++; + continue; + } + + if (i + 2 > data.size()) { + return {}; // uneven character count follows, it's unclear how to interpret it + } + + auto high = hex_to_number(data[i]); + auto low = hex_to_number(data[i + 1]); + if (high < 0 || low < 0) { + return {}; + } + result.push_back(high * 16 + low); + i += 2; + } + return result; } void diff --git a/src/lib/base/String.h b/src/lib/base/String.h index 047b6e16..9c5a53ba 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -75,7 +75,10 @@ std::string removeFileExt(std::string filename); /*! Convert each character in \c subject into hexdecimal form with \c width */ -void toHex(std::string& subject, int width, const char fill = '0'); +std::string to_hex(const std::vector& subject, int width, const char fill = '0'); + +/// Convert binary data from hexadecimal +std::vector from_hex(const std::string& data); //! Convert to all uppercase /*! diff --git a/src/lib/base/TMethodJob.h b/src/lib/base/TMethodJob.h deleted file mode 100644 index 84961d48..00000000 --- a/src/lib/base/TMethodJob.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "IJob.h" - -//! Use a function as a job -/*! -A job class that invokes a member function. -*/ -template -class TMethodJob : public IJob { -public: - //! run() invokes \c object->method(arg) - TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL); - virtual ~TMethodJob(); - - // IJob overrides - virtual void run(); - -private: - T* m_object; - void (T::*m_method)(void*); - void* m_arg; -}; - -template -inline -TMethodJob::TMethodJob(T* object, void (T::*method)(void*), void* arg) : - m_object(object), - m_method(method), - m_arg(arg) -{ - // do nothing -} - -template -inline -TMethodJob::~TMethodJob() -{ - // do nothing -} - -template -inline -void -TMethodJob::run() -{ - if (m_object != NULL) { - (m_object->*m_method)(m_arg); - } -} diff --git a/src/lib/base/finally.h b/src/lib/base/finally.h new file mode 100644 index 00000000..f3be617c --- /dev/null +++ b/src/lib/base/finally.h @@ -0,0 +1,61 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_BASE_FINALLY_H +#define BARRIER_LIB_BASE_FINALLY_H + +#include + +namespace barrier { + +// this implements a common pattern of executing an action at the end of function + +template +class final_action { +public: + final_action() noexcept {} + final_action(Callable callable) noexcept : callable_{callable} {} + + ~final_action() noexcept + { + if (!invoked_) { + callable_(); + } + } + + final_action(final_action&& other) noexcept : + callable_{std::move(other.callable_)} + { + std::swap(invoked_, other.invoked_); + } + + final_action(const final_action&) = delete; + final_action& operator=(const final_action&) = delete; +private: + bool invoked_ = false; + Callable callable_; +}; + +template +inline final_action finally(Callable&& callable) noexcept +{ + return final_action(std::forward(callable)); +} + +} // namespace barrier + +#endif // BARRIER_LIB_BASE_FINALLY_H diff --git a/src/lib/base/log_outputters.cpp b/src/lib/base/log_outputters.cpp index 1d43f605..176af2df 100644 --- a/src/lib/base/log_outputters.cpp +++ b/src/lib/base/log_outputters.cpp @@ -17,10 +17,9 @@ */ #include "base/log_outputters.h" -#include "base/TMethodJob.h" #include "arch/Arch.h" #include "base/String.h" - +#include "io/filesystem.h" #include enum EFileLogOutputter { @@ -260,7 +259,7 @@ FileLogOutputter::write(ELevel level, const char *message) bool moveFile = false; std::ofstream m_handle; - m_handle.open(m_fileName.c_str(), std::fstream::app); + barrier::open_utf8_path(m_handle, m_fileName, std::fstream::app); if (m_handle.is_open() && m_handle.fail() != true) { m_handle << message << std::endl; diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index b0dbbc37..a7b15cf5 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -37,7 +37,6 @@ #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include #include @@ -127,6 +126,12 @@ Client::connect() return; } + auto security_level = ConnectionSecurityLevel::PLAINTEXT; + if (m_useSecureNetwork) { + // client always authenticates server + security_level = ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED; + } + try { // resolve the server hostname. do this every time we connect // in case we couldn't resolve the address earlier or the address @@ -145,9 +150,8 @@ Client::connect() } // create the socket - IDataSocket* socket = m_socketFactory->create( - ARCH->getAddrFamily(m_serverAddress.getAddress()), - m_useSecureNetwork); + IDataSocket* socket = m_socketFactory->create(ARCH->getAddrFamily(m_serverAddress.getAddress()), + security_level); m_socket = dynamic_cast(socket); // filter socket messages, including a packetizing filter @@ -755,9 +759,7 @@ void Client::onFileRecieveCompleted() { if (isReceivedFileSizeValid()) { - m_writeToDropDirThread = new Thread( - new TMethodJob( - this, &Client::writeToDropDirThread)); + m_writeToDropDirThread = new Thread([this](){ write_to_drop_dir_thread(); }); } } @@ -767,8 +769,7 @@ Client::handleStopRetry(const Event&, void*) m_args.m_restartable = false; } -void -Client::writeToDropDirThread(void*) +void Client::write_to_drop_dir_thread() { LOG((CLOG_DEBUG "starting write to drop dir thread")); @@ -807,18 +808,13 @@ Client::sendFileToServer(const char* filename) StreamChunker::interruptFile(); } - m_sendFileThread = new Thread( - new TMethodJob( - this, &Client::sendFileThread, - static_cast(const_cast(filename)))); + m_sendFileThread = new Thread([this, filename]() { send_file_thread(filename); }); } -void -Client::sendFileThread(void* filename) +void Client::send_file_thread(const char* filename) { try { - char* name = static_cast(filename); - StreamChunker::sendFile(name, m_events, this); + StreamChunker::sendFile(filename, m_events, this); } catch (std::runtime_error& error) { LOG((CLOG_ERR "failed sending file chunks: %s", error.what())); diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 6979fc3a..c172af22 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -119,7 +119,7 @@ public: */ NetworkAddress getServerAddress() const; - //! Return true if recieved file size is valid + //! Return true if received file size is valid bool isReceivedFileSizeValid(); //! Return expected file size @@ -167,8 +167,8 @@ private: void sendEvent(Event::Type, void*); void sendConnectionFailedEvent(const char* msg); void sendFileChunk(const void* data); - void sendFileThread(void*); - void writeToDropDirThread(void*); + void send_file_thread(const char* filename); + void write_to_drop_dir_thread(); void setupConnecting(); void setupConnection(); void setupScreen(); diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index ce65c72d..c6e35766 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -26,6 +26,7 @@ #include "barrier/ProtocolUtil.h" #include "barrier/option_types.h" #include "barrier/protocol_types.h" +#include "barrier/XBarrier.h" #include "io/IStream.h" #include "base/Log.h" #include "base/IEventQueue.h" @@ -124,17 +125,27 @@ ServerProxy::handleData(const Event&, void*) // parse message LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3])); - switch ((this->*m_parser)(code)) { - case kOkay: - break; + try { + switch ((this->*m_parser)(code)) { + case kOkay: + break; - case kUnknown: - LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + case kUnknown: + LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3])); + m_client->disconnect("invalid message from server"); + return; + + case kDisconnect: + return; + } + } catch (const XBadClient& e) { + // TODO: disconnect handling is currently dispersed across both parseMessage() and + // handleData() functions, we should collect that to a single place + + LOG((CLOG_ERR "protocol error from server: %s", e.what())); + ProtocolUtil::writef(m_stream, kMsgEBad); m_client->disconnect("invalid message from server"); return; - - case kDisconnect: - return; } // next message diff --git a/src/lib/common/DataDirectories.h b/src/lib/common/DataDirectories.h index 6b990c20..45502116 100644 --- a/src/lib/common/DataDirectories.h +++ b/src/lib/common/DataDirectories.h @@ -15,27 +15,36 @@ * along with this program. If not, see . */ -#pragma once +#ifndef BARRIER_LIB_COMMON_DATA_DIRECTORIES_H +#define BARRIER_LIB_COMMON_DATA_DIRECTORIES_H -#include +#include "io/filesystem.h" + +namespace barrier { class DataDirectories { public: - static const std::string& profile(); - static const std::string& profile(const std::string& path); + static const fs::path& profile(); + static const fs::path& profile(const fs::path& path); - static const std::string& global(); - static const std::string& global(const std::string& path); + static const fs::path& global(); + static const fs::path& global(const fs::path& path); - static const std::string& systemconfig(); - static const std::string& systemconfig(const std::string& path); + static const fs::path& systemconfig(); + static const fs::path& systemconfig(const fs::path& path); + static fs::path ssl_fingerprints_path(); + static fs::path local_ssl_fingerprints_path(); + static fs::path trusted_servers_ssl_fingerprints_path(); + static fs::path trusted_clients_ssl_fingerprints_path(); + static fs::path ssl_certificate_path(); private: - // static class - DataDirectories() {} - - static std::string _profile; - static std::string _global; - static std::string _systemconfig; + static fs::path _profile; + static fs::path _global; + static fs::path _systemconfig; }; + +} // namespace barrier + +#endif diff --git a/src/lib/common/DataDirectories_static.cpp b/src/lib/common/DataDirectories_static.cpp index 48dccb68..47f88e70 100644 --- a/src/lib/common/DataDirectories_static.cpp +++ b/src/lib/common/DataDirectories_static.cpp @@ -17,7 +17,40 @@ #include "DataDirectories.h" -// static member -std::string DataDirectories::_profile; -std::string DataDirectories::_global; -std::string DataDirectories::_systemconfig; +namespace barrier { + +fs::path DataDirectories::_profile; +fs::path DataDirectories::_global; +fs::path DataDirectories::_systemconfig; + +static const char kFingerprintsDirName[] = "SSL/Fingerprints"; +static const char kFingerprintsLocalFilename[] = "Local.txt"; +static const char kFingerprintsTrustedServersFilename[] = "TrustedServers.txt"; +static const char kFingerprintsTrustedClientsFilename[] = "TrustedClients.txt"; + +fs::path DataDirectories::ssl_fingerprints_path() +{ + return profile() / kFingerprintsDirName; +} + +fs::path DataDirectories::local_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() / kFingerprintsLocalFilename; +} + +fs::path DataDirectories::trusted_servers_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() / kFingerprintsTrustedServersFilename; +} + +fs::path DataDirectories::trusted_clients_ssl_fingerprints_path() +{ + return ssl_fingerprints_path() / kFingerprintsTrustedClientsFilename; +} + +fs::path DataDirectories::ssl_certificate_path() +{ + return profile() / "SSL" / "Barrier.pem"; +} + +} // namespace barrier diff --git a/src/lib/common/MacOSXPrecomp.h b/src/lib/common/MacOSXPrecomp.h deleted file mode 100644 index 53ecc83c..00000000 --- a/src/lib/common/MacOSXPrecomp.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * barrier -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2002 Chris Schoeneman - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - // -// Prefix header for all source files of the 'deleteme' target in the 'deleteme' project. -// - -#include diff --git a/src/lib/common/PathUtilities.cpp b/src/lib/common/PathUtilities.cpp deleted file mode 100644 index a2ab38a2..00000000 --- a/src/lib/common/PathUtilities.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -* barrier -- mouse and keyboard sharing utility -* Copyright (C) 2018 Debauchee Open Source Group -* -* This package is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* found in the file LICENSE that should have accompanied this file. -* -* This package is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -/* - -These functions cover the vast majority of cases for different paths across -windows and unixes. They are not, however, fullproof and probably don't cover -fringe cases very well. The library below might be used as an alternative if -these implementations prove to be insufficient. As the library's readme states -it is simply a temporary band-aid until std::filesystem is integrated (C++17 -has it in std::experimental) and this class should also be treated as such. - -https://github.com/wjakob/filesystem/ - -*/ - -#include "PathUtilities.h" - -// keep the default platform delimiter as the first in the list -#ifdef _WIN32 -static const char *Delimiters = "\\/"; -#else -static const char *Delimiters = "/"; -#endif - -static const char DefaultDelimiter = Delimiters[0]; - -std::string PathUtilities::basename(const std::string& path) -{ - return path.substr(path.find_last_of(Delimiters) + 1); -} - -std::string PathUtilities::concat(const std::string& left, const std::string& right) -{ - // although npos is usually (-1) we can't count on that so handle it explicitly - auto leftEnd = left.find_last_not_of(Delimiters); - if (leftEnd == std::string::npos) - leftEnd = 0; - else - ++leftEnd; - auto rightStart = right.find_first_not_of(Delimiters, 0); - if (rightStart == std::string::npos) { - // both left/right are empty - if (left.size() == 0 && right.size() == 0) - return ""; - // right is full of delims, left is okay - if (leftEnd > 0) - return left.substr(0, leftEnd); - // both left/right useless but at least one has delims - return std::string(1, DefaultDelimiter); - } - if (leftEnd == 0) { - // right is okay and not prefixed with delims, left is empty - if (left.size() == 0 && rightStart == 0) - return right.substr(rightStart); - // (right is okay and prefixed with delims) OR left is full of delims - return DefaultDelimiter + right.substr(rightStart); - } - // concatenation using both left and right - return left.substr(0, leftEnd) + DefaultDelimiter + right.substr(rightStart); -} diff --git a/src/lib/common/PathUtilities.h b/src/lib/common/PathUtilities.h deleted file mode 100644 index 70b85b4c..00000000 --- a/src/lib/common/PathUtilities.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -* barrier -- mouse and keyboard sharing utility -* Copyright (C) 2018 Debauchee Open Source Group -* -* This package is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* found in the file LICENSE that should have accompanied this file. -* -* This package is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#pragma once - -#include - -class PathUtilities -{ -public: - static std::string basename(const std::string& path); - static std::string concat(const std::string& left, const std::string& right); - -private: - // static class - PathUtilities() {} -}; diff --git a/src/lib/common/basic_types.h b/src/lib/common/basic_types.h index 86300ca9..1882e57e 100644 --- a/src/lib/common/basic_types.h +++ b/src/lib/common/basic_types.h @@ -18,49 +18,7 @@ #pragma once -#include "common/common.h" - -// -// pick types of particular sizes -// - -#if !defined(TYPE_OF_SIZE_1) -# if SIZEOF_CHAR == 1 -# define TYPE_OF_SIZE_1 char -# endif -#endif - -#if !defined(TYPE_OF_SIZE_2) -# if SIZEOF_INT == 2 -# define TYPE_OF_SIZE_2 int -# else -# define TYPE_OF_SIZE_2 short -# endif -#endif - -#if !defined(TYPE_OF_SIZE_4) - // Carbon defines SInt32 and UInt32 in terms of long -# if SIZEOF_INT == 4 && !defined(__APPLE__) -# define TYPE_OF_SIZE_4 int -# else -# define TYPE_OF_SIZE_4 long -# endif -#endif - - // -// verify existence of required types -// - -#if !defined(TYPE_OF_SIZE_1) -# error No 1 byte integer type -#endif -#if !defined(TYPE_OF_SIZE_2) -# error No 2 byte integer type -#endif -#if !defined(TYPE_OF_SIZE_4) -# error No 4 byte integer type -#endif - +#include // // make typedefs @@ -75,18 +33,11 @@ #if defined(__APPLE__) #include #else -typedef signed TYPE_OF_SIZE_1 SInt8; -typedef signed TYPE_OF_SIZE_2 SInt16; -typedef signed TYPE_OF_SIZE_4 SInt32; -typedef unsigned TYPE_OF_SIZE_1 UInt8; -typedef unsigned TYPE_OF_SIZE_2 UInt16; -typedef unsigned TYPE_OF_SIZE_4 UInt32; +using SInt8 = std::int8_t; +using SInt16 = std::int16_t; +using SInt32 = std::int32_t; +using UInt8 = std::uint8_t; +using UInt16 = std::uint16_t; +using UInt32 = std::uint32_t; #endif #endif -// -// clean up -// - -#undef TYPE_OF_SIZE_1 -#undef TYPE_OF_SIZE_2 -#undef TYPE_OF_SIZE_4 diff --git a/src/lib/common/common.h b/src/lib/common/common.h index 5eac5684..39462568 100644 --- a/src/lib/common/common.h +++ b/src/lib/common/common.h @@ -18,26 +18,8 @@ #pragma once -#if defined(_WIN32) -# define SYSAPI_WIN32 1 -# define WINAPI_MSWINDOWS 1 -#elif HAVE_CONFIG_H +#if HAVE_CONFIG_H # include "config.h" -#else -# error "config.h missing" -#endif - -// VC++ has built-in sized types -#if defined(_MSC_VER) -# include -# define TYPE_OF_SIZE_1 __int8 -# define TYPE_OF_SIZE_2 __int16 -# define TYPE_OF_SIZE_4 __int32 -#else -# define SIZE_OF_CHAR 1 -# define SIZE_OF_SHORT 2 -# define SIZE_OF_INT 4 -# define SIZE_OF_LONG 4 #endif // define NULL diff --git a/src/lib/common/unix/DataDirectories.cpp b/src/lib/common/unix/DataDirectories.cpp index 608db05c..cf3ca243 100644 --- a/src/lib/common/unix/DataDirectories.cpp +++ b/src/lib/common/unix/DataDirectories.cpp @@ -22,7 +22,7 @@ #include // getpwuid(_r) #include // getpwuid(_r) -const std::string ProfileSubdir = "/barrier"; +namespace barrier { static std::string pw_dir(struct passwd* pwentp) { @@ -33,7 +33,7 @@ static std::string pw_dir(struct passwd* pwentp) #ifdef HAVE_GETPWUID_R -static std::string unix_home() +static fs::path unix_home() { long size = -1; #if defined(_SC_GETPW_R_SIZE_MAX) @@ -46,47 +46,47 @@ static std::string unix_home() struct passwd* pwentp; std::string buffer(size, 0); getpwuid_r(getuid(), &pwent, &buffer[0], size, &pwentp); - return pw_dir(pwentp); + return fs::u8path(pw_dir(pwentp)); } #else // not HAVE_GETPWUID_R -static std::string unix_home() +static fs::path unix_home() { - return pw_dir(getpwuid(getuid())); + return fs::u8path(pw_dir(getpwuid(getuid()))); } #endif // HAVE_GETPWUID_R -static std::string profile_basedir() +static fs::path profile_basedir() { #ifdef WINAPI_XWINDOWS // linux/bsd adheres to freedesktop standards // https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html const char* dir = std::getenv("XDG_DATA_HOME"); if (dir != NULL) - return dir; - return unix_home() + "/.local/share"; + return fs::u8path(dir); + return unix_home() / ".local/share"; #else // macos has its own standards // https://developer.apple.com/library/content/documentation/General/Conceptual/MOSXAppProgrammingGuide/AppRuntime/AppRuntime.html - return unix_home() + "/Library/Application Support"; + return unix_home() / "Library/Application Support"; #endif } -const std::string& DataDirectories::profile() +const fs::path& DataDirectories::profile() { if (_profile.empty()) - _profile = profile_basedir() + ProfileSubdir; + _profile = profile_basedir() / "barrier"; return _profile; } -const std::string& DataDirectories::profile(const std::string& path) +const fs::path& DataDirectories::profile(const fs::path& path) { _profile = path; return _profile; } -const std::string& DataDirectories::global() +const fs::path& DataDirectories::global() { if (_global.empty()) // TODO: where on a unix system should public/global shared data go? @@ -94,21 +94,23 @@ const std::string& DataDirectories::global() _global = "/tmp"; return _global; } -const std::string& DataDirectories::global(const std::string& path) +const fs::path& DataDirectories::global(const fs::path& path) { _global = path; return _global; } -const std::string& DataDirectories::systemconfig() +const fs::path& DataDirectories::systemconfig() { if (_systemconfig.empty()) _systemconfig = "/etc"; return _systemconfig; } -const std::string& DataDirectories::systemconfig(const std::string& path) +const fs::path& DataDirectories::systemconfig(const fs::path& path) { _systemconfig = path; return _systemconfig; } + +} // namespace barrier diff --git a/src/lib/common/win32/DataDirectories.cpp b/src/lib/common/win32/DataDirectories.cpp index 3e86c59b..31a428fd 100644 --- a/src/lib/common/win32/DataDirectories.cpp +++ b/src/lib/common/win32/DataDirectories.cpp @@ -16,44 +16,62 @@ */ #include "../DataDirectories.h" -#include "KnownFolderPaths.h" +#include "encoding_utilities.h" -const std::string& DataDirectories::profile() +#include + +namespace barrier { + +fs::path known_folder_path(const KNOWNFOLDERID& id) +{ + fs::path path; + WCHAR* buffer; + HRESULT result = SHGetKnownFolderPath(id, 0, NULL, &buffer); + if (result == S_OK) { + path = fs::path(std::wstring(buffer)); + CoTaskMemFree(buffer); + } + return path; +} + +const fs::path& DataDirectories::profile() { if (_profile.empty()) - _profile = localAppDataPath() + "\\Barrier"; + _profile = known_folder_path(FOLDERID_LocalAppData) / "Barrier"; return _profile; } -const std::string& DataDirectories::profile(const std::string& path) +const fs::path& DataDirectories::profile(const fs::path& path) { _profile = path; return _profile; } -const std::string& DataDirectories::global() +const fs::path& DataDirectories::global() { if (_global.empty()) - _global = programDataPath() + "\\Barrier"; + _global = known_folder_path(FOLDERID_ProgramData) / "Barrier"; return _global; } -const std::string& DataDirectories::global(const std::string& path) +const fs::path& DataDirectories::global(const fs::path& path) { _global = path; return _global; } -const std::string& DataDirectories::systemconfig() +const fs::path& DataDirectories::systemconfig() { // systemconfig() is a special case in that it will track the current value - // of global() unless and until it is explictly set otherwise + // of global() unless and until it is explicitly set otherwise // previously it would default to the windows folder which was horrible! if (_systemconfig.empty()) return global(); return _systemconfig; } -const std::string& DataDirectories::systemconfig(const std::string& path) +const fs::path& DataDirectories::systemconfig(const fs::path& path) { _systemconfig = path; return _systemconfig; } + +} // namespace barrier diff --git a/src/lib/common/win32/KnownFolderPaths.cpp b/src/lib/common/win32/KnownFolderPaths.cpp deleted file mode 100644 index 23ccc84a..00000000 --- a/src/lib/common/win32/KnownFolderPaths.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* -* barrier -- mouse and keyboard sharing utility -* Copyright (C) 2018 Debauchee Open Source Group -* -* This package is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* found in the file LICENSE that should have accompanied this file. -* -* This package is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include "KnownFolderPaths.h" - -#define WIN32_LEAN_AND_MEAN -#include -#include - -static std::string wide_to_mb(const wchar_t* source, int length) -{ - int ansiLength = WideCharToMultiByte(CP_ACP, 0, source, length, NULL, 0, NULL, NULL); - if (ansiLength > 0) { - std::string ansiString(ansiLength, 0); - ansiLength = WideCharToMultiByte(CP_ACP, 0, source, length, &ansiString[0], ansiLength, NULL, NULL); - if (ansiLength > 0) { - return ansiString; - } - } - return {}; -} - -static std::string known_folder_path(const KNOWNFOLDERID& id) -{ - std::string path; - WCHAR* buffer; - HRESULT result = SHGetKnownFolderPath(id, 0, NULL, &buffer); - if (result == S_OK) { - auto length = lstrlenW(buffer); - path = wide_to_mb(buffer, length); - CoTaskMemFree(buffer); - } - return path; -} - -std::string desktopPath() -{ - return known_folder_path(FOLDERID_Desktop); -} - -std::string localAppDataPath() -{ - return known_folder_path(FOLDERID_LocalAppData); -} - -std::string programDataPath() -{ - return known_folder_path(FOLDERID_ProgramData); -} diff --git a/src/lib/common/win32/KnownFolderPaths.h b/src/lib/common/win32/KnownFolderPaths.h deleted file mode 100644 index 81b650ec..00000000 --- a/src/lib/common/win32/KnownFolderPaths.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -* barrier -- mouse and keyboard sharing utility -* Copyright (C) 2018 Debauchee Open Source Group -* -* This package is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License -* found in the file LICENSE that should have accompanied this file. -* -* This package is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#pragma once - -#include - -std::string desktopPath(); -std::string localAppDataPath(); -std::string programDataPath(); diff --git a/src/lib/common/win32/encoding_utilities.cpp b/src/lib/common/win32/encoding_utilities.cpp new file mode 100644 index 00000000..11781d36 --- /dev/null +++ b/src/lib/common/win32/encoding_utilities.cpp @@ -0,0 +1,37 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "encoding_utilities.h" +#include + +std::string win_wchar_to_utf8(const WCHAR* utfStr) +{ + int utfLength = lstrlenW(utfStr); + int mbLength = WideCharToMultiByte(CP_UTF8, 0, utfStr, utfLength, NULL, 0, NULL, NULL); + std::string mbStr(mbLength, 0); + WideCharToMultiByte(CP_UTF8, 0, utfStr, utfLength, &mbStr[0], mbLength, NULL, NULL); + return mbStr; +} + +std::vector utf8_to_win_char(const std::string& str) +{ + int result_len = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), NULL, 0); + std::vector result; + result.resize(result_len + 1, 0); + MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), result.data(), result_len); + return result; +} diff --git a/src/lib/common/win32/encoding_utilities.h b/src/lib/common/win32/encoding_utilities.h new file mode 100644 index 00000000..747371e8 --- /dev/null +++ b/src/lib/common/win32/encoding_utilities.h @@ -0,0 +1,28 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_COMMON_WIN32_ENCODING_UTILITIES_H +#define BARRIER_LIB_COMMON_WIN32_ENCODING_UTILITIES_H + +#include +#include +#include + +std::string win_wchar_to_utf8(const WCHAR* utfStr); +std::vector utf8_to_win_char(const std::string& str); + +#endif diff --git a/src/lib/io/StreamBuffer.cpp b/src/lib/io/StreamBuffer.cpp index 34bb47cc..8abbbb82 100644 --- a/src/lib/io/StreamBuffer.cpp +++ b/src/lib/io/StreamBuffer.cpp @@ -22,6 +22,8 @@ // StreamBuffer // +#include + const UInt32 StreamBuffer::kChunkSize = 4096; StreamBuffer::StreamBuffer() : diff --git a/src/lib/io/filesystem.cpp b/src/lib/io/filesystem.cpp new file mode 100644 index 00000000..46ae06db --- /dev/null +++ b/src/lib/io/filesystem.cpp @@ -0,0 +1,71 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// this header must come first so that it picks up the filesystem implementation +#include + +#include "filesystem.h" +#if SYSAPI_WIN32 +#include "common/win32/encoding_utilities.h" +#endif +#include + +namespace barrier { + +namespace { + +template +void open_utf8_path_impl(Stream& stream, const fs::path& path, std::ios_base::openmode mode) +{ +#if SYSAPI_WIN32 + // on Windows we need to use a non-standard constructor from wchar_t* string + // which fs::path::native() returns + stream.open(path.native().c_str(), mode); +#else + stream.open(path.native().c_str(), mode); +#endif +} + +} // namespace + +void open_utf8_path(std::ifstream& stream, const fs::path& path, std::ios_base::openmode mode) +{ + open_utf8_path_impl(stream, path, mode); +} + +void open_utf8_path(std::ofstream& stream, const fs::path& path, std::ios_base::openmode mode) +{ + open_utf8_path_impl(stream, path, mode); +} + +void open_utf8_path(std::fstream& stream, const fs::path& path, std::ios_base::openmode mode) +{ + open_utf8_path_impl(stream, path, mode); +} + +std::FILE* fopen_utf8_path(const fs::path& path, const std::string& mode) +{ +#if SYSAPI_WIN32 + auto wchar_mode = utf8_to_win_char(mode); + return _wfopen(path.native().c_str(), + reinterpret_cast(wchar_mode.data())); +#else + return std::fopen(path.native().c_str(), mode.c_str()); +#endif +} + +} // namespace barrier diff --git a/src/lib/io/filesystem.h b/src/lib/io/filesystem.h new file mode 100644 index 00000000..78e34230 --- /dev/null +++ b/src/lib/io/filesystem.h @@ -0,0 +1,41 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_IO_FILESYSTEM_H +#define BARRIER_LIB_IO_FILESYSTEM_H + +#include +#include +#include +#include + +namespace barrier { + +namespace fs = ghc::filesystem; + +void open_utf8_path(std::ifstream& stream, const fs::path& path, + std::ios_base::openmode mode = std::ios_base::in); +void open_utf8_path(std::ofstream& stream, const fs::path& path, + std::ios_base::openmode mode = std::ios_base::out); +void open_utf8_path(std::fstream& stream, const fs::path& path, + std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); + +std::FILE* fopen_utf8_path(const fs::path& path, const std::string& mode); + +} // namespace barrier + +#endif // BARRIER_LIB_IO_FILESYSTEM_H diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index c895dc12..5118a0a6 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -28,8 +28,6 @@ #include "base/Event.h" #include "base/EventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" -#include "base/Unicode.h" enum EIpcLogOutputter { kBufferMaxSize = 1000, @@ -55,8 +53,7 @@ IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType m_clientType(clientType) { if (useThread) { - m_bufferThread = new Thread(new TMethodJob( - this, &IpcLogOutputter::bufferThread)); + m_bufferThread = new Thread([this](){ buffer_thread(); }); } } @@ -143,8 +140,7 @@ IpcLogOutputter::isRunning() return m_running; } -void -IpcLogOutputter::bufferThread(void*) +void IpcLogOutputter::buffer_thread() { m_bufferThreadId = m_bufferThread->getID(); m_running = true; @@ -197,7 +193,7 @@ IpcLogOutputter::sendBuffer() return; } - IpcLogLineMessage message(Unicode::textToUTF8(getChunk(kMaxSendLines))); + IpcLogLineMessage message(getChunk(kMaxSendLines)); m_sending = true; m_ipcServer.send(message, kIpcClientGui); m_sending = false; diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h index 11e4d137..3f3ab0a6 100644 --- a/src/lib/ipc/IpcLogOutputter.h +++ b/src/lib/ipc/IpcLogOutputter.h @@ -92,7 +92,7 @@ public: private: void init(); - void bufferThread(void*); + void buffer_thread(); std::string getChunk(size_t count); void appendBuffer(const std::string& text); bool isRunning(); diff --git a/src/lib/mt/Thread.cpp b/src/lib/mt/Thread.cpp index 858fdbab..755c0e6d 100644 --- a/src/lib/mt/Thread.cpp +++ b/src/lib/mt/Thread.cpp @@ -28,12 +28,10 @@ // Thread // -Thread::Thread(IJob* job) +Thread::Thread(const std::function& fun) { - m_thread = ARCH->newThread(&Thread::threadFunc, job); + m_thread = ARCH->newThread([=](){ threadFunc(fun); }); if (m_thread == NULL) { - // couldn't create thread - delete job; throw XMTThreadUnavailable(); } } @@ -69,7 +67,7 @@ Thread::operator=(const Thread& thread) void Thread::exit(void* result) { - throw XThreadExit(result); + throw XThreadExit(); } void @@ -108,15 +106,6 @@ Thread::wait(double timeout) const return ARCH->wait(m_thread, timeout); } -void* -Thread::getResult() const -{ - if (wait()) - return ARCH->getResultOfThread(m_thread); - else - return NULL; -} - IArchMultithread::ThreadID Thread::getID() const { @@ -135,8 +124,7 @@ Thread::operator!=(const Thread& thread) const return !ARCH->isSameThread(m_thread, thread.m_thread); } -void* -Thread::threadFunc(void* vjob) +void Thread::threadFunc(const std::function& func) { // get this thread's id for logging IArchMultithread::ThreadID id; @@ -146,42 +134,26 @@ Thread::threadFunc(void* vjob) ARCH->closeThread(thread); } - // get job - IJob* job = static_cast(vjob); - - // run job - void* result = NULL; try { // go LOG((CLOG_DEBUG1 "thread 0x%08x entry", id)); - job->run(); + func(); LOG((CLOG_DEBUG1 "thread 0x%08x exit", id)); } catch (XThreadCancel&) { // client called cancel() LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id)); - delete job; throw; } - catch (XThreadExit& e) { - // client called exit() - result = e.m_result; - LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); + catch (XThreadExit&) { + LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x", id)); } catch (XBase& e) { LOG((CLOG_ERR "exception on thread 0x%08x: %s", id, e.what())); - delete job; throw; } catch (...) { LOG((CLOG_ERR "exception on thread 0x%08x: ", id)); - delete job; throw; } - - // done with job - delete job; - - // return exit result - return result; } diff --git a/src/lib/mt/Thread.h b/src/lib/mt/Thread.h index dbde3afc..141d59f3 100644 --- a/src/lib/mt/Thread.h +++ b/src/lib/mt/Thread.h @@ -19,13 +19,12 @@ #pragma once #include "arch/IArchMultithread.h" - -class IJob; +#include //! Thread handle /*! Creating a Thread creates a new context of execution (i.e. thread) that -runs simulatenously with the calling thread. A Thread is only a handle +runs simultaneously with the calling thread. A Thread is only a handle to a thread; deleting a Thread does not cancel or destroy the thread it refers to and multiple Thread objects can refer to the same thread. @@ -44,10 +43,9 @@ class Thread { public: //! Run \c adoptedJob in a new thread /*! - Create and start a new thread executing the \c adoptedJob. The - new thread takes ownership of \c adoptedJob and will delete it. + Create and start a new thread executing the \c fun. */ - Thread(IJob* adoptedJob); + Thread(const std::function& fun); //! Duplicate a thread handle /*! @@ -79,8 +77,7 @@ public: /*! Terminate the calling thread. This function does not return but the stack is unwound and automatic objects are destroyed, as if - exit() threw an exception (which is, in fact, what it does). The - argument is saved as the result returned by getResult(). If you + exit() threw an exception (which is, in fact, what it does). If you have \c catch(...) blocks then you should add the following before each to avoid catching the exit: \code @@ -122,7 +119,7 @@ public: /*! Change the priority of the thread. Normal priority is 0, 1 is the next lower, etc. -1 is the next higher, etc. but boosting - the priority may not be permitted and will be silenty ignored. + the priority may not be permitted and will be silently ignored. */ void setPriority(int n); @@ -167,16 +164,6 @@ public: */ bool wait(double timeout = -1.0) const; - //! Get the exit result - /*! - Returns the exit result. This does an implicit wait(). It returns - NULL immediately if called by a thread on itself or on a thread that - was cancelled. - - (cancellation point) - */ - void* getResult() const; - //! Get the thread id /*! Returns an integer id for this thread. This id must not be used to @@ -203,7 +190,7 @@ public: private: Thread(ArchThread); - static void* threadFunc(void*); + static void threadFunc(const std::function& func); private: ArchThread m_thread; diff --git a/src/lib/mt/XThread.h b/src/lib/mt/XThread.h index 34177ce9..41172b33 100644 --- a/src/lib/mt/XThread.h +++ b/src/lib/mt/XThread.h @@ -26,12 +26,4 @@ Thrown by Thread::exit() to exit a thread. Clients of Thread must not throw this type but must rethrow it if caught (by XThreadExit, XThread, or ...). */ -class XThreadExit : public XThread { -public: - //! \c result is the result of the thread - XThreadExit(void* result) : m_result(result) { } - ~XThreadExit() { } - -public: - void* m_result; -}; +class XThreadExit : public XThread {}; diff --git a/src/lib/net/ConnectionSecurityLevel.h b/src/lib/net/ConnectionSecurityLevel.h new file mode 100644 index 00000000..913c2fd7 --- /dev/null +++ b/src/lib/net/ConnectionSecurityLevel.h @@ -0,0 +1,27 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H +#define BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H + +enum class ConnectionSecurityLevel { + PLAINTEXT, + ENCRYPTED, + ENCRYPTED_AUTHENTICATED +}; + +#endif // BARRIER_LIB_NET_CONNECTION_SECURITY_LEVEL_H diff --git a/src/lib/net/FingerprintData.cpp b/src/lib/net/FingerprintData.cpp new file mode 100644 index 00000000..460b39bd --- /dev/null +++ b/src/lib/net/FingerprintData.cpp @@ -0,0 +1,52 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/filesystem.h" +#include +#include + +namespace barrier { + +bool FingerprintData::operator==(const FingerprintData& other) const +{ + return algorithm == other.algorithm && data == other.data; +} + +const char* fingerprint_type_to_string(FingerprintType type) +{ + switch (type) { + case FingerprintType::INVALID: return "invalid"; + case FingerprintType::SHA1: return "sha1"; + case FingerprintType::SHA256: return "sha256"; + } + return "invalid"; +} + +FingerprintType fingerprint_type_from_string(const std::string& type) +{ + if (type == "sha1") { + return FingerprintType::SHA1; + } + if (type == "sha256") { + return FingerprintType::SHA256; + } + return FingerprintType::INVALID; +} + +} // namespace barrier diff --git a/src/lib/net/FingerprintData.h b/src/lib/net/FingerprintData.h new file mode 100644 index 00000000..938a6953 --- /dev/null +++ b/src/lib/net/FingerprintData.h @@ -0,0 +1,46 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATA_H +#define BARRIER_LIB_NET_FINGERPRINT_DATA_H + +#include +#include + +namespace barrier { + +enum FingerprintType { + INVALID, + SHA1, // deprecated + SHA256, +}; + +struct FingerprintData { + std::string algorithm; + std::vector data; + + bool valid() const { return !algorithm.empty(); } + + bool operator==(const FingerprintData& other) const; +}; + +const char* fingerprint_type_to_string(FingerprintType type); +FingerprintType fingerprint_type_from_string(const std::string& type); + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_TYPE_H diff --git a/src/lib/net/FingerprintDatabase.cpp b/src/lib/net/FingerprintDatabase.cpp new file mode 100644 index 00000000..def0de91 --- /dev/null +++ b/src/lib/net/FingerprintDatabase.cpp @@ -0,0 +1,135 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "base/String.h" +#include "FingerprintDatabase.h" +#include "io/filesystem.h" +#include +#include + +namespace barrier { + +void FingerprintDatabase::read(const fs::path& path) +{ + std::ifstream file; + open_utf8_path(file, path, std::ios_base::in); + read_stream(file); +} + +void FingerprintDatabase::write(const fs::path& path) +{ + std::ofstream file; + open_utf8_path(file, path, std::ios_base::out); + write_stream(file); +} + +void FingerprintDatabase::read_stream(std::istream& stream) +{ + if (!stream.good()) { + return; + } + + std::string line; + while (std::getline(stream, line)) { + if (line.empty()) { + continue; + } + + auto fingerprint = parse_db_line(line); + if (!fingerprint.valid()) { + continue; + } + + fingerprints_.push_back(fingerprint); + } +} + +void FingerprintDatabase::write_stream(std::ostream& stream) +{ + if (!stream.good()) { + return; + } + + for (const auto& fingerprint : fingerprints_) { + stream << to_db_line(fingerprint) << "\n"; + } +} + +void FingerprintDatabase::clear() +{ + fingerprints_.clear(); +} + +void FingerprintDatabase::add_trusted(const FingerprintData& fingerprint) +{ + if (is_trusted(fingerprint)) { + return; + } + fingerprints_.push_back(fingerprint); +} + +bool FingerprintDatabase::is_trusted(const FingerprintData& fingerprint) +{ + auto found_it = std::find(fingerprints_.begin(), fingerprints_.end(), fingerprint); + return found_it != fingerprints_.end(); +} + +FingerprintData FingerprintDatabase::parse_db_line(const std::string& line) +{ + FingerprintData result; + + // legacy v1 certificate handling + if (std::count(line.begin(), line.end(), ':') == 19 && line.size() == 40 + 19) { + auto data = string::from_hex(line); + if (data.empty()) { + return result; + } + result.algorithm = fingerprint_type_to_string(FingerprintType::SHA1); + result.data = data; + return result; + } + + auto version_end_pos = line.find(':'); + if (version_end_pos == std::string::npos) { + return result; + } + if (line.substr(0, version_end_pos) != "v2") { + return result; + } + auto algo_start_pos = version_end_pos + 1; + auto algo_end_pos = line.find(':', algo_start_pos); + if (algo_end_pos == std::string::npos) { + return result; + } + auto algorithm = line.substr(algo_start_pos, algo_end_pos - algo_start_pos); + auto data = string::from_hex(line.substr(algo_end_pos + 1)); + + if (data.empty()) { + return result; + } + + result.algorithm = algorithm; + result.data = data; + return result; +} + +std::string FingerprintDatabase::to_db_line(const FingerprintData& fingerprint) +{ + return "v2:" + fingerprint.algorithm + ":" + string::to_hex(fingerprint.data, 2); +} + +} // namespace barrier diff --git a/src/lib/net/FingerprintDatabase.h b/src/lib/net/FingerprintDatabase.h new file mode 100644 index 00000000..49272650 --- /dev/null +++ b/src/lib/net/FingerprintDatabase.h @@ -0,0 +1,53 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_FINGERPRINT_DATABASE_H +#define BARRIER_LIB_NET_FINGERPRINT_DATABASE_H + +#include "FingerprintData.h" +#include "io/filesystem.h" +#include +#include +#include + +namespace barrier { + +class FingerprintDatabase { +public: + void read(const fs::path& path); + void write(const fs::path& path); + + void read_stream(std::istream& stream); + void write_stream(std::ostream& stream); + + void clear(); + void add_trusted(const FingerprintData& fingerprint); + bool is_trusted(const FingerprintData& fingerprint); + + const std::vector& fingerprints() const { return fingerprints_; } + + static FingerprintData parse_db_line(const std::string& line); + static std::string to_db_line(const FingerprintData& fingerprint); + +private: + + std::vector fingerprints_; +}; + +} // namespace barrier + +#endif // BARRIER_LIB_NET_FINGERPRINT_DATABASE_H diff --git a/src/lib/net/ISocketFactory.h b/src/lib/net/ISocketFactory.h index a98ddd4f..edfc8c90 100644 --- a/src/lib/net/ISocketFactory.h +++ b/src/lib/net/ISocketFactory.h @@ -20,6 +20,7 @@ #include "common/IInterface.h" #include "arch/IArchNetwork.h" +#include "net/ConnectionSecurityLevel.h" class IDataSocket; class IListenSocket; @@ -35,14 +36,12 @@ public: //@{ //! Create data socket - virtual IDataSocket* create( - IArchNetwork::EAddressFamily family, - bool secure) const = 0; + virtual IDataSocket* create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const = 0; //! Create listen socket - virtual IListenSocket* createListen( - IArchNetwork::EAddressFamily family, - bool secure) const = 0; + virtual IListenSocket* createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const = 0; //@} }; diff --git a/src/lib/net/NetworkAddress.h b/src/lib/net/NetworkAddress.h index b572d757..3a006af5 100644 --- a/src/lib/net/NetworkAddress.h +++ b/src/lib/net/NetworkAddress.h @@ -43,7 +43,7 @@ public: If \c hostname can be parsed as a numerical address then that's how it's used, otherwise it's used as a host name. If \c hostname ends in ":[0-9]+" then that suffix is extracted and used as the port, - overridding the port parameter. The resulting port must be a valid + overriding the port parameter. The resulting port must be a valid port number (zero is not a valid port number) otherwise \c XSocketAddress is thrown with an error of \c XSocketAddress::kBadPort. The hostname is not resolved by the c'tor; use \c resolve to do that. diff --git a/src/lib/net/SecureListenSocket.cpp b/src/lib/net/SecureListenSocket.cpp index 2a9f84cb..11efc5cd 100644 --- a/src/lib/net/SecureListenSocket.cpp +++ b/src/lib/net/SecureListenSocket.cpp @@ -20,23 +20,16 @@ #include "SecureSocket.h" #include "net/NetworkAddress.h" #include "net/SocketMultiplexer.h" -#include "net/TSocketMultiplexerMethodJob.h" +#include "arch/Arch.h" #include "arch/XArch.h" #include "common/DataDirectories.h" #include "base/String.h" -static const char s_certificateDir[] = { "SSL" }; -static const char s_certificateFilename[] = { "Barrier.pem" }; - -// -// SecureListenSocket -// - -SecureListenSocket::SecureListenSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family) : - TCPListenSocket(events, socketMultiplexer, family) +SecureListenSocket::SecureListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) : + TCPListenSocket(events, socketMultiplexer, family), + security_level_{security_level} { } @@ -45,22 +38,15 @@ SecureListenSocket::accept() { SecureSocket* socket = NULL; try { - socket = new SecureSocket( - m_events, - m_socketMultiplexer, - ARCH->acceptSocket(m_socket, NULL)); + socket = new SecureSocket(m_events, m_socketMultiplexer, + ARCH->acceptSocket(m_socket, NULL), security_level_); socket->initSsl(true); if (socket != NULL) { setListeningJob(); } - std::string certificateFilename = barrier::string::sprintf("%s/%s/%s", - DataDirectories::profile().c_str(), - s_certificateDir, - s_certificateFilename); - - bool loaded = socket->loadCertificates(certificateFilename); + bool loaded = socket->load_certificates(barrier::DataDirectories::ssl_certificate_path()); if (!loaded) { delete socket; return NULL; diff --git a/src/lib/net/SecureListenSocket.h b/src/lib/net/SecureListenSocket.h index 11456879..a0e792ac 100644 --- a/src/lib/net/SecureListenSocket.h +++ b/src/lib/net/SecureListenSocket.h @@ -19,18 +19,21 @@ #include "net/TCPListenSocket.h" #include "common/stdset.h" +#include "ConnectionSecurityLevel.h" class IEventQueue; class SocketMultiplexer; class IDataSocket; -class SecureListenSocket : public TCPListenSocket{ +class SecureListenSocket : public TCPListenSocket { public: - SecureListenSocket(IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family); + SecureListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level); // IListenSocket overrides virtual IDataSocket* accept(); +private: + ConnectionSecurityLevel security_level_; }; diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index d84f3461..85e1d383 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -16,6 +16,7 @@ */ #include "SecureSocket.h" +#include "SecureUtils.h" #include "net/TSocketMultiplexerMethodJob.h" #include "base/TMethodEventJob.h" @@ -25,6 +26,8 @@ #include "base/Log.h" #include "base/String.h" #include "common/DataDirectories.h" +#include "io/filesystem.h" +#include "net/FingerprintDatabase.h" #include #include @@ -40,41 +43,36 @@ #define MAX_ERROR_SIZE 65535 +static const std::size_t MAX_INPUT_BUFFER_SIZE = 1024 * 1024; static const float s_retryDelay = 0.01f; enum { kMsgSize = 128 }; -static const char kFingerprintDirName[] = "SSL/Fingerprints"; -//static const char kFingerprintLocalFilename[] = "Local.txt"; -static const char kFingerprintTrustedServersFilename[] = "TrustedServers.txt"; -//static const char kFingerprintTrustedClientsFilename[] = "TrustedClients.txt"; - struct Ssl { SSL_CTX* m_context; SSL* m_ssl; }; -SecureSocket::SecureSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - IArchNetwork::EAddressFamily family) : +SecureSocket::SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) : TCPSocket(events, socketMultiplexer, family), m_ssl(nullptr), m_secureReady(false), - m_fatal(false) + m_fatal(false), + security_level_{security_level} { } -SecureSocket::SecureSocket( - IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - ArchSocket socket) : +SecureSocket::SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + ArchSocket socket, ConnectionSecurityLevel security_level) : TCPSocket(events, socketMultiplexer, socket), m_ssl(nullptr), m_secureReady(false), - m_fatal(false) + m_fatal(false), + security_level_{security_level} { } @@ -103,6 +101,8 @@ SecureSocket::close() void SecureSocket::freeSSLResources() { + std::lock_guard ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { SSL_shutdown(m_ssl->m_ssl); SSL_free(m_ssl->m_ssl); @@ -140,23 +140,23 @@ std::unique_ptr SecureSocket::newJob() void SecureSocket::secureConnect() { - setJob(std::make_unique>( - this, &SecureSocket::serviceConnect, - getSocket(), isReadable(), isWritable())); + setJob(std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceConnect(j, r, w, e); }, + getSocket(), isReadable(), isWritable())); } void SecureSocket::secureAccept() { - setJob(std::make_unique>( - this, &SecureSocket::serviceAccept, - getSocket(), isReadable(), isWritable())); + setJob(std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceAccept(j, r, w, e); }, + getSocket(), isReadable(), isWritable())); } TCPSocket::EJobResult SecureSocket::doRead() { - static UInt8 buffer[4096]; + UInt8 buffer[4096]; memset(buffer, 0, sizeof(buffer)); int bytesRead = 0; int status = 0; @@ -181,6 +181,10 @@ SecureSocket::doRead() do { m_inputBuffer.write(buffer, bytesRead); + if (m_inputBuffer.getSize() > MAX_INPUT_BUFFER_SIZE) { + break; + } + status = secureRead(buffer, sizeof(buffer), bytesRead); if (status < 0) { return kBreak; @@ -211,11 +215,6 @@ SecureSocket::doRead() TCPSocket::EJobResult SecureSocket::doWrite() { - static bool s_retry = false; - static int s_retrySize = 0; - static std::unique_ptr s_staticBuffer; - static std::size_t s_staticBufferSize = 0; - // write data int bufferSize = 0; int bytesWrote = 0; @@ -224,16 +223,16 @@ SecureSocket::doWrite() if (!isSecureReady()) return kRetry; - if (s_retry) { - bufferSize = s_retrySize; + if (do_write_retry_) { + bufferSize = do_write_retry_size_; } else { bufferSize = m_outputBuffer.getSize(); - if (bufferSize > s_staticBufferSize) { - s_staticBuffer.reset(new char[bufferSize]); - s_staticBufferSize = bufferSize; + if (bufferSize > do_write_retry_buffer_size_) { + do_write_retry_buffer_.reset(new char[bufferSize]); + do_write_retry_buffer_size_ = bufferSize; } if (bufferSize > 0) { - memcpy(s_staticBuffer.get(), m_outputBuffer.peek(bufferSize), bufferSize); + std::memcpy(do_write_retry_buffer_.get(), m_outputBuffer.peek(bufferSize), bufferSize); } } @@ -241,14 +240,14 @@ SecureSocket::doWrite() return kRetry; } - status = secureWrite(s_staticBuffer.get(), bufferSize, bytesWrote); + status = secureWrite(do_write_retry_buffer_.get(), bufferSize, bytesWrote); if (status > 0) { - s_retry = false; + do_write_retry_ = false; } else if (status < 0) { return kBreak; } else if (status == 0) { - s_retry = true; - s_retrySize = bufferSize; + do_write_retry_ = true; + do_write_retry_size_ = bufferSize; return kNew; } @@ -263,16 +262,16 @@ SecureSocket::doWrite() int SecureSocket::secureRead(void* buffer, int size, int& read) { + std::lock_guard ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { LOG((CLOG_DEBUG2 "reading secure socket")); read = SSL_read(m_ssl->m_ssl, buffer, size); - static int retry; - // Check result will cleanup the connection in the case of a fatal - checkResult(read, retry); + checkResult(read, secure_read_retry_); - if (retry) { + if (secure_read_retry_) { return 0; } @@ -289,17 +288,17 @@ SecureSocket::secureRead(void* buffer, int size, int& read) int SecureSocket::secureWrite(const void* buffer, int size, int& wrote) { + std::lock_guard ssl_lock{ssl_mutex_}; + if (m_ssl->m_ssl != NULL) { LOG((CLOG_DEBUG2 "writing secure socket:%p", this)); wrote = SSL_write(m_ssl->m_ssl, buffer, size); - static int retry; - // Check result will cleanup the connection in the case of a fatal - checkResult(wrote, retry); + checkResult(wrote, secure_write_retry_); - if (retry) { + if (secure_write_retry_) { return 0; } @@ -322,6 +321,8 @@ SecureSocket::isSecureReady() void SecureSocket::initSsl(bool server) { + std::lock_guard ssl_lock{ssl_mutex_}; + m_ssl = new Ssl(); m_ssl->m_context = NULL; m_ssl->m_ssl = NULL; @@ -329,48 +330,53 @@ SecureSocket::initSsl(bool server) initContext(server); } -bool SecureSocket::loadCertificates(const std::string& filename) +bool SecureSocket::load_certificates(const barrier::fs::path& path) { - if (filename.empty()) { + std::lock_guard ssl_lock{ssl_mutex_}; + + if (path.empty()) { showError("ssl certificate is not specified"); return false; } else { - std::ifstream file(filename.c_str()); - bool exist = file.good(); - file.close(); - - if (!exist) { - showError("ssl certificate doesn't exist: " + filename); + if (!barrier::fs::is_regular_file(path)) { + showError("ssl certificate doesn't exist: " + path.u8string()); return false; } } int r = 0; - r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); + r = SSL_CTX_use_certificate_file(m_ssl->m_context, path.u8string().c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use ssl certificate: " + filename); + showError("could not use ssl certificate: " + path.u8string()); return false; } - r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM); + r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, path.u8string().c_str(), SSL_FILETYPE_PEM); if (r <= 0) { - showError("could not use ssl private key: " + filename); + showError("could not use ssl private key: " + path.u8string()); return false; } r = SSL_CTX_check_private_key(m_ssl->m_context); if (!r) { - showError("could not verify ssl private key: " + filename); + showError("could not verify ssl private key: " + path.u8string()); return false; } return true; } +static int cert_verify_ignore_callback(X509_STORE_CTX*, void*) +{ + return 1; +} + void SecureSocket::initContext(bool server) { + // ssl_mutex_ is assumed to be acquired + SSL_library_init(); const SSL_METHOD* method; @@ -403,11 +409,21 @@ SecureSocket::initContext(bool server) if (m_ssl->m_context == NULL) { showError(""); } + + if (security_level_ == ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED) { + // We want to ask for peer certificate, but not verify it. If we don't ask for peer + // certificate, e.g. client won't send it. + SSL_CTX_set_verify(m_ssl->m_context, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + nullptr); + SSL_CTX_set_cert_verify_callback(m_ssl->m_context, cert_verify_ignore_callback, nullptr); + } } void SecureSocket::createSSL() { + // ssl_mutex_ is assumed to be acquired + // I assume just one instance is needed // get new SSL state with context if (m_ssl->m_ssl == NULL) { @@ -419,6 +435,8 @@ SecureSocket::createSSL() int SecureSocket::secureAccept(int socket) { + std::lock_guard ssl_lock{ssl_mutex_}; + createSSL(); // set connection socket to SSL state @@ -427,9 +445,7 @@ SecureSocket::secureAccept(int socket) LOG((CLOG_DEBUG2 "accepting secure socket")); int r = SSL_accept(m_ssl->m_ssl); - static int retry; - - checkResult(r, retry); + checkResult(r, secure_accept_retry_); if (isFatal()) { // tell user and sleep so the socket isn't hammered. @@ -437,12 +453,30 @@ SecureSocket::secureAccept(int socket) LOG((CLOG_INFO "client connection may not be secure")); m_secureReady = false; ARCH->sleep(1); - retry = 0; + secure_accept_retry_ = 0; return -1; // Failed, error out } // If not fatal and no retry, state is good - if (retry == 0) { + if (secure_accept_retry_ == 0) { + if (security_level_ == ConnectionSecurityLevel::ENCRYPTED_AUTHENTICATED) { + if (verify_cert_fingerprint( + barrier::DataDirectories::trusted_clients_ssl_fingerprints_path())) { + LOG((CLOG_INFO "accepted secure socket")); + if (!ensure_peer_certificate()) { + secure_accept_retry_ = 0; + disconnect(); + return -1;// Cert fail, error + } + } + else { + LOG((CLOG_ERR "failed to verify server certificate fingerprint")); + secure_accept_retry_ = 0; + disconnect(); + return -1; // Fingerprint failed, error + } + } + m_secureReady = true; LOG((CLOG_INFO "accepted secure socket")); if (CLOG->getFilter() >= kDEBUG1) { @@ -453,7 +487,7 @@ SecureSocket::secureAccept(int socket) } // If not fatal and retry is set, not ready, and return retry - if (retry > 0) { + if (secure_accept_retry_ > 0) { LOG((CLOG_DEBUG2 "retry accepting secure socket")); m_secureReady = false; ARCH->sleep(s_retryDelay); @@ -468,6 +502,15 @@ SecureSocket::secureAccept(int socket) int SecureSocket::secureConnect(int socket) { + // note that load_certificates acquires ssl_mutex_ + if (!load_certificates(barrier::DataDirectories::ssl_certificate_path())) { + LOG((CLOG_ERR "could not load client certificates")); + // FIXME: this is fatal error, but we current don't disconnect because whole logic in this + // function needs to be cleaned up + } + + std::lock_guard ssl_lock{ssl_mutex_}; + createSSL(); // attach the socket descriptor @@ -476,30 +519,28 @@ SecureSocket::secureConnect(int socket) LOG((CLOG_DEBUG2 "connecting secure socket")); int r = SSL_connect(m_ssl->m_ssl); - static int retry; - - checkResult(r, retry); + checkResult(r, secure_connect_retry_); if (isFatal()) { LOG((CLOG_ERR "failed to connect secure socket")); - retry = 0; + secure_connect_retry_ = 0; return -1; } // If we should retry, not ready and return 0 - if (retry > 0) { + if (secure_connect_retry_ > 0) { LOG((CLOG_DEBUG2 "retry connect secure socket")); m_secureReady = false; ARCH->sleep(s_retryDelay); return 0; } - retry = 0; + secure_connect_retry_ = 0; // No error, set ready, process and return ok m_secureReady = true; - if (verifyCertFingerprint()) { + if (verify_cert_fingerprint(barrier::DataDirectories::trusted_servers_ssl_fingerprints_path())) { LOG((CLOG_INFO "connected to secure socket")); - if (!showCertificate()) { + if (!ensure_peer_certificate()) { disconnect(); return -1;// Cert fail, error } @@ -518,8 +559,9 @@ SecureSocket::secureConnect(int socket) } bool -SecureSocket::showCertificate() +SecureSocket::ensure_peer_certificate() { + // ssl_mutex_ is assumed to be acquired X509* cert; char* line; @@ -527,12 +569,12 @@ SecureSocket::showCertificate() cert = SSL_get_peer_certificate(m_ssl->m_ssl); if (cert != NULL) { line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); - LOG((CLOG_INFO "server ssl certificate info: %s", line)); + LOG((CLOG_INFO "peer ssl certificate info: %s", line)); OPENSSL_free(line); X509_free(cert); } else { - showError("server has no ssl certificate"); + showError("peer has no ssl certificate"); return false; } @@ -542,6 +584,8 @@ SecureSocket::showCertificate() void SecureSocket::checkResult(int status, int& retry) { + // ssl_mutex_ is assumed to be acquired + // ssl errors are a little quirky. the "want" errors are normal and // should result in a retry. @@ -655,83 +699,49 @@ SecureSocket::disconnect() sendEvent(getEvents()->forIStream().inputShutdown()); } -void SecureSocket::formatFingerprint(std::string& fingerprint, bool hex, bool separator) +bool SecureSocket::verify_cert_fingerprint(const barrier::fs::path& fingerprint_db_path) { - if (hex) { - // to hexidecimal - barrier::string::toHex(fingerprint, 2); - } + // ssl_mutex_ is assumed to be acquired - // all uppercase - barrier::string::uppercase(fingerprint); - - if (separator) { - // add colon to separate each 2 charactors - size_t separators = fingerprint.size() / 2; - for (size_t i = 1; i < separators; i++) { - fingerprint.insert(i * 3 - 1, ":"); - } - } -} - -bool -SecureSocket::verifyCertFingerprint() -{ // calculate received certificate fingerprint - X509 *cert = cert = SSL_get_peer_certificate(m_ssl->m_ssl); - EVP_MD* tempDigest; - unsigned char tempFingerprint[EVP_MAX_MD_SIZE]; - unsigned int tempFingerprintLen; - tempDigest = (EVP_MD*)EVP_sha1(); - int digestResult = X509_digest(cert, tempDigest, tempFingerprint, &tempFingerprintLen); - - if (digestResult <= 0) { - LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult)); + barrier::FingerprintData fingerprint_sha1, fingerprint_sha256; + try { + auto* cert = SSL_get_peer_certificate(m_ssl->m_ssl); + fingerprint_sha1 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA1); + fingerprint_sha256 = barrier::get_ssl_cert_fingerprint(cert, + barrier::FingerprintType::SHA256); + } catch (const std::exception& e) { + LOG((CLOG_ERR "%s", e.what())); return false; } - // format fingerprint into hexdecimal format with colon separator - std::string fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); - formatFingerprint(fingerprint); - LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); - - std::string trustedServersFilename; - trustedServersFilename = barrier::string::sprintf( - "%s/%s/%s", - DataDirectories::profile().c_str(), - kFingerprintDirName, - kFingerprintTrustedServersFilename); + // note: the GUI parses the following two lines of logs, don't change unnecessarily + LOG((CLOG_NOTE "peer fingerprint (SHA1): %s (SHA256): %s", + barrier::format_ssl_fingerprint(fingerprint_sha1.data).c_str(), + barrier::format_ssl_fingerprint(fingerprint_sha256.data).c_str())); // Provide debug hint as to what file is being used to verify fingerprint trust - LOG((CLOG_NOTE "trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "fingerprint_db_path: %s", fingerprint_db_path.u8string().c_str())); - // check if this fingerprint exist - std::string fileLine; - std::ifstream file; - file.open(trustedServersFilename.c_str()); + barrier::FingerprintDatabase db; + db.read(fingerprint_db_path); - if (!file.is_open()) { - LOG((CLOG_NOTE "Unable to open trustedServersFile: %s", trustedServersFilename.c_str() )); + if (!db.fingerprints().empty()) { + LOG((CLOG_NOTE "Read %d fingerprints from: %s", db.fingerprints().size(), + fingerprint_db_path.u8string().c_str())); } else { - LOG((CLOG_NOTE "Opened trustedServersFilename: %s", trustedServersFilename.c_str() )); + LOG((CLOG_NOTE "Could not read fingerprints from: %s", + fingerprint_db_path.u8string().c_str())); } - bool isValid = false; - while (!file.eof() && file.is_open()) { - getline(file,fileLine); - if (!fileLine.empty()) { - if (fileLine.compare(fingerprint) == 0) { - LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); - isValid = true; - break; - } else { - LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); - } - } + if (db.is_trusted(fingerprint_sha256)) { + LOG((CLOG_NOTE "Fingerprint matches trusted fingerprint")); + return true; + } else { + LOG((CLOG_NOTE "Fingerprint does not match trusted fingerprint")); + return false; } - - file.close(); - return isValid; } MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job, @@ -762,9 +772,9 @@ MultiplexerJobStatus SecureSocket::serviceConnect(ISocketMultiplexerJob* job, // Retry case return { true, - std::make_unique>( - this, &SecureSocket::serviceConnect, - getSocket(), isReadable(), isWritable()) + std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceConnect(j, r, w, e); }, + getSocket(), isReadable(), isWritable()) }; } @@ -792,9 +802,12 @@ MultiplexerJobStatus SecureSocket::serviceAccept(ISocketMultiplexerJob* job, } // Retry case - return {true, std::make_unique>( - this, &SecureSocket::serviceAccept, - getSocket(), isReadable(), isWritable())}; + return { + true, + std::make_unique([this](auto j, auto r, auto w, auto e) + { return serviceAccept(j, r, w, e); }, + getSocket(), isReadable(), isWritable()) + }; } void @@ -819,6 +832,8 @@ showCipherStackDesc(STACK_OF(SSL_CIPHER) * stack) { void SecureSocket::showSecureCipherInfo() { + // ssl_mutex_ is assumed to be acquired + STACK_OF(SSL_CIPHER) * sStack = SSL_get_ciphers(m_ssl->m_ssl); if (sStack == NULL) { @@ -830,7 +845,7 @@ SecureSocket::showSecureCipherInfo() } #if OPENSSL_VERSION_NUMBER < 0x10100000L - // m_ssl->m_ssl->session->ciphers is not forward compatable, + // m_ssl->m_ssl->session->ciphers is not forward compatible, // In future release of OpenSSL, it's not visible, STACK_OF(SSL_CIPHER) * cStack = m_ssl->m_ssl->session->ciphers; #else @@ -861,6 +876,8 @@ SecureSocket::showSecureLibInfo() void SecureSocket::showSecureConnectInfo() { + // ssl_mutex_ is assumed to be acquired + const SSL_CIPHER* cipher = SSL_get_current_cipher(m_ssl->m_ssl); if (cipher != NULL) { diff --git a/src/lib/net/SecureSocket.h b/src/lib/net/SecureSocket.h index f861d662..be7dc0dd 100644 --- a/src/lib/net/SecureSocket.h +++ b/src/lib/net/SecureSocket.h @@ -17,8 +17,11 @@ #pragma once +#include "ConnectionSecurityLevel.h" #include "net/TCPSocket.h" #include "net/XSocket.h" +#include "io/filesystem.h" +#include class IEventQueue; class SocketMultiplexer; @@ -32,10 +35,10 @@ A secure socket using SSL. */ class SecureSocket : public TCPSocket { public: - SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family); - SecureSocket(IEventQueue* events, - SocketMultiplexer* socketMultiplexer, - ArchSocket socket); + SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + IArchNetwork::EAddressFamily family, ConnectionSecurityLevel security_level); + SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, + ArchSocket socket, ConnectionSecurityLevel security_level); ~SecureSocket(); // ISocket overrides @@ -55,35 +58,56 @@ public: EJobResult doRead() override; EJobResult doWrite() override; void initSsl(bool server); - bool loadCertificates(const std::string& filename); + bool load_certificates(const barrier::fs::path& path); private: // SSL - void initContext(bool server); - void createSSL(); + void initContext(bool server); // may only be called with ssl_mutex_ acquired + void createSSL(); // may only be called with ssl_mutex_ acquired. int secureAccept(int s); int secureConnect(int s); - bool showCertificate(); - void checkResult(int n, int& retry); + bool ensure_peer_certificate(); // may only be called with ssl_mutex_ acquired + + void checkResult(int n, int& retry); // may only be called with m_ssl_mutex_ acquired. + void showError(const std::string& reason); std::string getError(); void disconnect(); - void formatFingerprint(std::string& fingerprint, bool hex = true, bool separator = true); - bool verifyCertFingerprint(); + + // may only be called with ssl_mutex_ acquired + bool verify_cert_fingerprint(const barrier::fs::path& fingerprint_db_path); MultiplexerJobStatus serviceConnect(ISocketMultiplexerJob*, bool, bool, bool); MultiplexerJobStatus serviceAccept(ISocketMultiplexerJob*, bool, bool, bool); - void showSecureConnectInfo(); - void showSecureLibInfo(); - void showSecureCipherInfo(); + void showSecureConnectInfo(); // may only be called with ssl_mutex_ acquired + void showSecureLibInfo(); + void showSecureCipherInfo(); // may only be called with ssl_mutex_ acquired void handleTCPConnected(const Event& event, void*); void freeSSLResources(); private: + // all accesses to m_ssl must be protected by this mutex. The only function that is called + // from outside SocketMultiplexer thread is close(), so we mostly care about things accessed + // by it. + std::mutex ssl_mutex_; + Ssl* m_ssl; bool m_secureReady; bool m_fatal; + ConnectionSecurityLevel security_level_ = ConnectionSecurityLevel::ENCRYPTED; + + int secure_accept_retry_ = 0; // used only in secureAccept() + int secure_connect_retry_ = 0; // used only in secureConnect() + int secure_read_retry_ = 0; // used only in secureRead() + int secure_write_retry_ = 0; // used only in secureWrite() + + // The following are used only from doWrite() + // FIXME: using std::vector would simplify logic significantly. + bool do_write_retry_ = false; + int do_write_retry_size_ = 0; + std::unique_ptr do_write_retry_buffer_; + std::size_t do_write_retry_buffer_size_ = 0; }; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp new file mode 100644 index 00000000..c581dd49 --- /dev/null +++ b/src/lib/net/SecureUtils.cpp @@ -0,0 +1,312 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + ----------------------------------------------------------------------- + create_fingerprint_randomart() has been taken from the OpenSSH project. + Copyright information follows. + + Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. + Copyright (c) 2008 Alexander von Gernler. All rights reserved. + Copyright (c) 2010,2011 Damien Miller. 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. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ + +#include "SecureUtils.h" +#include "base/String.h" +#include "base/finally.h" +#include "io/filesystem.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#if SYSAPI_WIN32 +// Windows builds require a shim that makes it possible to link to different +// versions of the Win32 C runtime. See OpenSSL FAQ. +#include +#endif + +namespace barrier { + +namespace { + +const EVP_MD* get_digest_for_type(FingerprintType type) +{ + switch (type) { + case FingerprintType::SHA1: return EVP_sha1(); + case FingerprintType::SHA256: return EVP_sha256(); + } + throw std::runtime_error("Unknown fingerprint type " + std::to_string(static_cast(type))); +} + +} // namespace + +std::string format_ssl_fingerprint(const std::vector& fingerprint, bool separator) +{ + std::string result = barrier::string::to_hex(fingerprint, 2); + + // all uppercase + barrier::string::uppercase(result); + + if (separator) { + // add colon to separate each 2 characters + size_t separators = result.size() / 2; + for (size_t i = 1; i < separators; i++) { + result.insert(i * 3 - 1, ":"); + } + } + return result; +} + +std::string format_ssl_fingerprint_columns(const std::vector& fingerprint) +{ + auto max_columns = 8; + + std::string hex = barrier::string::to_hex(fingerprint, 2); + barrier::string::uppercase(hex); + if (hex.empty() || hex.size() % 2 != 0) { + return hex; + } + + std::string separated; + for (std::size_t i = 0; i < hex.size(); i += max_columns * 2) { + for (std::size_t j = i; j < i + 16 && j < hex.size() - 1; j += 2) { + separated.push_back(hex[j]); + separated.push_back(hex[j + 1]); + separated.push_back(':'); + } + separated.push_back('\n'); + } + separated.pop_back(); // we don't need last newline character + return separated; +} + +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type) +{ + if (!cert) { + throw std::runtime_error("certificate is null"); + } + + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int digest_length = 0; + int result = X509_digest(cert, get_digest_for_type(type), digest, &digest_length); + + if (result <= 0) { + throw std::runtime_error("failed to calculate fingerprint, digest result: " + + std::to_string(result)); + } + + std::vector digest_vec; + digest_vec.assign(reinterpret_cast(digest), + reinterpret_cast(digest) + digest_length); + return {fingerprint_type_to_string(type), digest_vec}; +} + +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type) +{ + auto fp = fopen_utf8_path(path, "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + X509* cert = PEM_read_X509(fp, nullptr, nullptr, nullptr); + if (!cert) { + throw std::runtime_error("Certificate could not be parsed"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + return get_ssl_cert_fingerprint(cert, type); +} + +void generate_pem_self_signed_cert(const std::string& path) +{ + auto expiration_days = 365; + + auto* private_key = EVP_PKEY_new(); + if (!private_key) { + throw std::runtime_error("Could not allocate private key for certificate"); + } + auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); }); + + auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr); + if (!rsa) { + throw std::runtime_error("Failed to generate RSA key"); + } + EVP_PKEY_assign_RSA(private_key, rsa); + + auto* cert = X509_new(); + if (!cert) { + throw std::runtime_error("Could not allocate certificate"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), expiration_days * 24 * 3600); + X509_set_pubkey(cert, private_key); + + auto* name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + reinterpret_cast("Barrier"), -1, -1, 0); + X509_set_issuer_name(cert, name); + + X509_sign(cert, private_key, EVP_sha256()); + + auto fp = fopen_utf8_path(path.c_str(), "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate output path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr); + PEM_write_X509(fp, cert); +} + +/* + Draw an ASCII-Art representing the fingerprint so human brain can + profit from its built-in pattern recognition ability. + This technique is called "random art" and can be found in some + scientific publications like this original paper: + + "Hash Visualization: a New Technique to improve Real-World Security", + Perrig A. and Song D., 1999, International Workshop on Cryptographic + Techniques and E-Commerce (CrypTEC '99) + sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf + + The subject came up in a talk by Dan Kaminsky, too. + + If you see the picture is different, the key is different. + If the picture looks the same, you still know nothing. + + The algorithm used here is a worm crawling over a discrete plane, + leaving a trace (augmenting the field) everywhere it goes. + Movement is taken from dgst_raw 2bit-wise. Bumping into walls + makes the respective movement vector be ignored for this turn. + Graphs are not unambiguous, because circles in graphs can be +walked in either direction. + */ + +/* + Field sizes for the random art. Have to be odd, so the starting point + can be in the exact middle of the picture, and FLDBASE should be >=8 . + Else pictures would be too dense, and drawing the frame would + fail, too, because the key type would not fit in anymore. +*/ +#define FLDBASE 8 +#define FLDSIZE_Y (FLDBASE + 1) +#define FLDSIZE_X (FLDBASE * 2 + 1) + +std::string create_fingerprint_randomart(const std::vector& dgst_raw) +{ + /* + * Chars to be used after each other every time the worm + * intersects with itself. Matter of taste. + */ + const char* augmentation_string = " .o+=*BOX@%&#/^SE"; + char *p; + std::uint8_t field[FLDSIZE_X][FLDSIZE_Y]; + std::size_t i; + std::uint32_t b; + int x, y; + std::size_t len = strlen(augmentation_string) - 1; + + std::vector retval; + retval.reserve((FLDSIZE_X + 3) * (FLDSIZE_Y + 2)); + + auto add_char = [&retval](char ch) { retval.push_back(ch); }; + + /* initialize field */ + std::memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); + x = FLDSIZE_X / 2; + y = FLDSIZE_Y / 2; + + /* process raw key */ + for (i = 0; i < dgst_raw.size(); i++) { + /* each byte conveys four 2-bit move commands */ + int input = dgst_raw[i]; + for (b = 0; b < 4; b++) { + /* evaluate 2 bit, rest is shifted later */ + x += (input & 0x1) ? 1 : -1; + y += (input & 0x2) ? 1 : -1; + + /* assure we are still in bounds */ + x = std::max(x, 0); + y = std::max(y, 0); + x = std::min(x, FLDSIZE_X - 1); + y = std::min(y, FLDSIZE_Y - 1); + + /* augment the field */ + if (field[x][y] < len - 2) + field[x][y]++; + input = input >> 2; + } + } + + /* mark starting point and end point*/ + field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; + field[x][y] = len; + + /* output upper border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + add_char('\n'); + + /* output content */ + for (y = 0; y < FLDSIZE_Y; y++) { + add_char('|'); + for (x = 0; x < FLDSIZE_X; x++) + add_char(augmentation_string[std::min(field[x][y], len)]); + add_char('|'); + add_char('\n'); + } + + /* output lower border */ + add_char('+'); + for (i = 0; i < FLDSIZE_X; i++) + add_char('-'); + add_char('+'); + + return std::string{retval.data(), retval.size()}; +} + +} // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h new file mode 100644 index 00000000..c4d51f33 --- /dev/null +++ b/src/lib/net/SecureUtils.h @@ -0,0 +1,43 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_LIB_NET_SECUREUTILS_H +#define BARRIER_LIB_NET_SECUREUTILS_H + +#include "FingerprintData.h" +#include +#include +#include +#include + +namespace barrier { + +std::string format_ssl_fingerprint(const std::vector& fingerprint, + bool separator = true); +std::string format_ssl_fingerprint_columns(const std::vector& fingerprint); + +FingerprintData get_ssl_cert_fingerprint(X509* cert, FingerprintType type); + +FingerprintData get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type); + +void generate_pem_self_signed_cert(const std::string& path); + +std::string create_fingerprint_randomart(const std::vector& dgst_raw); + +} // namespace barrier + +#endif // BARRIER_LIB_NET_SECUREUTILS_H diff --git a/src/lib/net/SocketMultiplexer.cpp b/src/lib/net/SocketMultiplexer.cpp index 74a305fe..5a2328e0 100644 --- a/src/lib/net/SocketMultiplexer.cpp +++ b/src/lib/net/SocketMultiplexer.cpp @@ -26,7 +26,6 @@ #include "arch/Arch.h" #include "arch/XArch.h" #include "base/Log.h" -#include "base/TMethodJob.h" #include "common/stdvector.h" // @@ -58,8 +57,7 @@ SocketMultiplexer::SocketMultiplexer() : m_jobListLockLocker(NULL) { // start thread - m_thread = new Thread(new TMethodJob( - this, &SocketMultiplexer::serviceThread)); + m_thread = new Thread([this](){ service_thread(); }); } SocketMultiplexer::~SocketMultiplexer() @@ -95,7 +93,7 @@ void SocketMultiplexer::addSocket(ISocket* socket, std::unique_ptrsecond)) { @@ -138,8 +136,7 @@ SocketMultiplexer::removeSocket(ISocket* socket) unlockJobList(); } -void -SocketMultiplexer::serviceThread(void*) +void SocketMultiplexer::service_thread() { std::vector pfds; IArchNetwork::PollEntry pfd; diff --git a/src/lib/net/SocketMultiplexer.h b/src/lib/net/SocketMultiplexer.h index b7b7e3e3..b13c725a 100644 --- a/src/lib/net/SocketMultiplexer.h +++ b/src/lib/net/SocketMultiplexer.h @@ -67,7 +67,7 @@ private: // and m_update while m_pollable and m_polling are true. all other // threads must only modify these when m_pollable and m_polling are // false. only the service thread sets m_polling. - void serviceThread(void*); + void service_thread(); // create, iterate, and destroy a cursor. a cursor is used to // safely iterate through the job list while other threads modify diff --git a/src/lib/net/TCPListenSocket.cpp b/src/lib/net/TCPListenSocket.cpp index f8407b0e..2c305fcf 100644 --- a/src/lib/net/TCPListenSocket.cpp +++ b/src/lib/net/TCPListenSocket.cpp @@ -70,8 +70,10 @@ TCPListenSocket::bind(const NetworkAddress& addr) ARCH->bindSocket(m_socket, addr.getAddress()); ARCH->listenOnSocket(m_socket); - auto new_job = std::make_unique>( - this, &TCPListenSocket::serviceListening, m_socket, true, false); + auto new_job = std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceListening(j, r, w, e); }, + m_socket, true, false); m_socketMultiplexer->addSocket(this, std::move(new_job)); } @@ -136,8 +138,10 @@ TCPListenSocket::accept() void TCPListenSocket::setListeningJob() { - auto new_job = std::make_unique>( - this, &TCPListenSocket::serviceListening, m_socket, true, false); + auto new_job = std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceListening(j, r, w, e); }, + m_socket, true, false); m_socketMultiplexer->addSocket(this, std::move(new_job)); } diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index e19786c8..fa7edcc8 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -33,9 +33,7 @@ #include #include -// -// TCPSocket -// +static const std::size_t MAX_INPUT_BUFFER_SIZE = 1024 * 1024; TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) : IDataSocket(events), @@ -345,6 +343,10 @@ TCPSocket::doRead() do { m_inputBuffer.write(buffer, (UInt32)bytesRead); + if (m_inputBuffer.getSize() > MAX_INPUT_BUFFER_SIZE) { + break; + } + bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); } while (bytesRead > 0); @@ -424,18 +426,20 @@ std::unique_ptr TCPSocket::newJob() if (!(m_readable || m_writable)) { return {}; } - return std::make_unique>( - this, &TCPSocket::serviceConnecting, - m_socket, m_readable, m_writable); + return std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceConnecting(j, r, w, e); }, + m_socket, m_readable, m_writable); } else { - if (!(m_readable || (m_writable && (m_outputBuffer.getSize() > 0)))) { + auto writable = m_writable && (m_outputBuffer.getSize() > 0); + if (!(m_readable || writable)) { return {}; } - return std::make_unique>( - this, &TCPSocket::serviceConnected, - m_socket, m_readable, - m_writable && (m_outputBuffer.getSize() > 0)); + return std::make_unique( + [this](auto j, auto r, auto w, auto e) + { return serviceConnected(j, r, w, e); }, + m_socket, m_readable, writable); } } diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index fe24e97f..30e930e3 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -40,11 +40,12 @@ TCPSocketFactory::~TCPSocketFactory() // do nothing } -IDataSocket* -TCPSocketFactory::create(IArchNetwork::EAddressFamily family, bool secure) const +IDataSocket* TCPSocketFactory::create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const { - if (secure) { - SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family); + if (security_level != ConnectionSecurityLevel::PLAINTEXT) { + SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family, + security_level); secureSocket->initSsl (false); return secureSocket; } @@ -53,12 +54,12 @@ TCPSocketFactory::create(IArchNetwork::EAddressFamily family, bool secure) const } } -IListenSocket* -TCPSocketFactory::createListen(IArchNetwork::EAddressFamily family, bool secure) const +IListenSocket* TCPSocketFactory::createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const { IListenSocket* socket = NULL; - if (secure) { - socket = new SecureListenSocket(m_events, m_socketMultiplexer, family); + if (security_level != ConnectionSecurityLevel::PLAINTEXT) { + socket = new SecureListenSocket(m_events, m_socketMultiplexer, family, security_level); } else { socket = new TCPListenSocket(m_events, m_socketMultiplexer, family); diff --git a/src/lib/net/TCPSocketFactory.h b/src/lib/net/TCPSocketFactory.h index 202366e0..ac21cab0 100644 --- a/src/lib/net/TCPSocketFactory.h +++ b/src/lib/net/TCPSocketFactory.h @@ -31,12 +31,11 @@ public: virtual ~TCPSocketFactory(); // ISocketFactory overrides - virtual IDataSocket* create( - IArchNetwork::EAddressFamily family, - bool secure) const; - virtual IListenSocket* createListen( - IArchNetwork::EAddressFamily family, - bool secure) const; + virtual IDataSocket* create(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const; + + virtual IListenSocket* createListen(IArchNetwork::EAddressFamily family, + ConnectionSecurityLevel security_level) const; private: IEventQueue* m_events; diff --git a/src/lib/net/TSocketMultiplexerMethodJob.h b/src/lib/net/TSocketMultiplexerMethodJob.h index c42fab66..4b571ab3 100644 --- a/src/lib/net/TSocketMultiplexerMethodJob.h +++ b/src/lib/net/TSocketMultiplexerMethodJob.h @@ -25,81 +25,43 @@ /*! A socket multiplexer job class that invokes a member function. */ -template class TSocketMultiplexerMethodJob : public ISocketMultiplexerJob { public: - using Method = MultiplexerJobStatus (T::*)(ISocketMultiplexerJob*, bool, bool, bool); + using RunFunction = std::function; //! run() invokes \c object->method(arg) - TSocketMultiplexerMethodJob(T* object, Method method, - ArchSocket socket, bool readable, bool writeable); - virtual ~TSocketMultiplexerMethodJob(); + TSocketMultiplexerMethodJob(const RunFunction& func, + ArchSocket socket, bool readable, bool writable) : + func_{func}, + m_socket(ARCH->copySocket(socket)), + m_readable(readable), + m_writable(writable) + { + } + + ~TSocketMultiplexerMethodJob() override + { + ARCH->closeSocket(m_socket); + } // IJob overrides - virtual MultiplexerJobStatus run(bool readable, bool writable, bool error) override; - virtual ArchSocket getSocket() const override; - virtual bool isReadable() const override; - virtual bool isWritable() const override; + virtual MultiplexerJobStatus run(bool readable, bool writable, bool error) override + { + if (func_) { + return func_(this, readable, writable, error); + } + return {false, {}}; + } + + virtual ArchSocket getSocket() const override { return m_socket; } + virtual bool isReadable() const override { return m_readable; } + virtual bool isWritable() const override { return m_writable; } private: - T* m_object; - Method m_method; + RunFunction func_; ArchSocket m_socket; bool m_readable; bool m_writable; - void* m_arg; }; -template -inline -TSocketMultiplexerMethodJob::TSocketMultiplexerMethodJob(T* object, - Method method, ArchSocket socket, - bool readable, bool writable) : - m_object(object), - m_method(method), - m_socket(ARCH->copySocket(socket)), - m_readable(readable), - m_writable(writable) -{ - // do nothing -} -template -inline -TSocketMultiplexerMethodJob::~TSocketMultiplexerMethodJob() -{ - ARCH->closeSocket(m_socket); -} - -template -inline MultiplexerJobStatus TSocketMultiplexerMethodJob::run(bool read, bool write, bool error) -{ - if (m_object != NULL) { - return (m_object->*m_method)(this, read, write, error); - } - return {false, {}}; -} - -template -inline -ArchSocket -TSocketMultiplexerMethodJob::getSocket() const -{ - return m_socket; -} - -template -inline -bool -TSocketMultiplexerMethodJob::isReadable() const -{ - return m_readable; -} - -template -inline -bool -TSocketMultiplexerMethodJob::isWritable() const -{ - return m_writable; -} diff --git a/src/lib/platform/IMSWindowsClipboardFacade.h b/src/lib/platform/IMSWindowsClipboardFacade.h index 1c314736..d848184a 100644 --- a/src/lib/platform/IMSWindowsClipboardFacade.h +++ b/src/lib/platform/IMSWindowsClipboardFacade.h @@ -33,4 +33,4 @@ public: virtual ~IMSWindowsClipboardFacade() { } }; -#endif \ No newline at end of file +#endif diff --git a/src/lib/platform/ImmuneKeysReader.cpp b/src/lib/platform/ImmuneKeysReader.cpp index 72baed3c..eaeedc17 100644 --- a/src/lib/platform/ImmuneKeysReader.cpp +++ b/src/lib/platform/ImmuneKeysReader.cpp @@ -50,4 +50,4 @@ static void add_key(const char * const buffer, std::vector &keys) } } return true; -} \ No newline at end of file +} diff --git a/src/lib/platform/ImmuneKeysReader.h b/src/lib/platform/ImmuneKeysReader.h index b46cbbe8..536dd45f 100644 --- a/src/lib/platform/ImmuneKeysReader.h +++ b/src/lib/platform/ImmuneKeysReader.h @@ -27,8 +27,4 @@ class ImmuneKeysReader { public: static bool get_list(const char * const path, std::vector &keys, std::string &badLine); - -private: - // static class - explicit ImmuneKeysReader() {} }; diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index be9a28d1..d126e13b 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -27,9 +27,7 @@ #include "arch/win32/ArchMiscWindows.h" #include "base/Log.h" #include "base/IEventQueue.h" -#include "base/IJob.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include "base/IEventQueue.h" #include @@ -97,10 +95,9 @@ // MSWindowsDesks // -MSWindowsDesks::MSWindowsDesks( - bool isPrimary, bool noHooks, +MSWindowsDesks::MSWindowsDesks(bool isPrimary, bool noHooks, const IScreenSaver* screensaver, IEventQueue* events, - IJob* updateKeys, bool stopOnDeskSwitch) : + const std::function& updateKeys, bool stopOnDeskSwitch) : m_isPrimary(isPrimary), m_noHooks(noHooks), m_isOnScreen(m_isPrimary), @@ -130,7 +127,6 @@ MSWindowsDesks::~MSWindowsDesks() disable(); destroyClass(m_deskClass); destroyCursor(m_cursor); - delete m_updateKeys; } void @@ -602,13 +598,11 @@ MSWindowsDesks::deskLeave(Desk* desk, HKL keyLayout) } } -void -MSWindowsDesks::deskThread(void* vdesk) +void MSWindowsDesks::desk_thread(Desk* desk) { MSG msg; // use given desktop for this thread - Desk* desk = static_cast(vdesk); desk->m_threadID = GetCurrentThreadId(); desk->m_window = NULL; desk->m_foregroundWindow = NULL; @@ -709,7 +703,7 @@ MSWindowsDesks::deskThread(void* vdesk) } case BARRIER_MSG_SYNC_KEYS: - m_updateKeys->run(); + m_updateKeys(); break; case BARRIER_MSG_SCREENSAVER: @@ -752,8 +746,7 @@ MSWindowsDesks::Desk* MSWindowsDesks::addDesk(const std::string& name, HDESK hde desk->m_name = name; desk->m_desk = hdesk; desk->m_targetID = GetCurrentThreadId(); - desk->m_thread = new Thread(new TMethodJob( - this, &MSWindowsDesks::deskThread, desk)); + desk->m_thread = new Thread([this, desk]() { desk_thread(desk); }); waitForDesk(); m_desks.insert(std::make_pair(name, desk)); return desk; @@ -803,7 +796,7 @@ MSWindowsDesks::checkDesk() // if active desktop changed then tell the old and new desk threads // about the change. don't switch desktops when the screensaver is - // active becaue we'd most likely switch to the screensaver desktop + // active because we'd most likely switch to the screensaver desktop // which would have the side effect of forcing the screensaver to // stop. if (name != m_activeDeskName && !m_screensaver->isActive()) { diff --git a/src/lib/platform/MSWindowsDesks.h b/src/lib/platform/MSWindowsDesks.h index 65d93ef5..6292f986 100644 --- a/src/lib/platform/MSWindowsDesks.h +++ b/src/lib/platform/MSWindowsDesks.h @@ -26,15 +26,16 @@ #include "mt/CondVar.h" #include "mt/Mutex.h" #include "common/stdmap.h" +#include #include #define WIN32_LEAN_AND_MEAN #include + class Event; class EventQueueTimer; class Thread; -class IJob; class IScreenSaver; class IEventQueue; @@ -68,7 +69,7 @@ public: MSWindowsDesks( bool isPrimary, bool noHooks, const IScreenSaver* screensaver, IEventQueue* events, - IJob* updateKeys, bool stopOnDeskSwitch); + const std::function& updateKeys, bool stopOnDeskSwitch); ~MSWindowsDesks(); //! @name manipulators @@ -219,7 +220,7 @@ private: void deskMouseRelativeMove(SInt32 dx, SInt32 dy) const; void deskEnter(Desk* desk); void deskLeave(Desk* desk, HKL keyLayout); - void deskThread(void* vdesk); + void desk_thread(Desk* desk); // desk switch checking and handling Desk* addDesk(const std::string& name, HDESK hdesk); @@ -284,7 +285,7 @@ private: Desks m_desks; // keyboard stuff - IJob* m_updateKeys; + std::function m_updateKeys; HKL m_keyLayout; // options diff --git a/src/lib/platform/MSWindowsHook.cpp b/src/lib/platform/MSWindowsHook.cpp index b10d5c63..3230d24c 100644 --- a/src/lib/platform/MSWindowsHook.cpp +++ b/src/lib/platform/MSWindowsHook.cpp @@ -574,7 +574,7 @@ MSWindowsHook::install() g_fakeServerInput = false; // setup immune keys - g_immuneKeysPath = DataDirectories::profile() + "\\ImmuneKeys.txt"; + g_immuneKeysPath = (barrier::DataDirectories::profile() / "ImmuneKeys.txt").u8string(); g_immuneKeys = immune_keys_list(); LOG((CLOG_DEBUG "Found %u immune keys in %s", g_immuneKeys.size(), g_immuneKeysPath.c_str())); diff --git a/src/lib/platform/MSWindowsHookResource.cpp b/src/lib/platform/MSWindowsHookResource.cpp index ced5ff12..c6349ab3 100644 --- a/src/lib/platform/MSWindowsHookResource.cpp +++ b/src/lib/platform/MSWindowsHookResource.cpp @@ -30,4 +30,4 @@ bool WindowsHookResource::unset() } bool WindowsHookResource::is_set() const { return _hook != NULL; } -WindowsHookResource::operator HHOOK() const { return _hook; } \ No newline at end of file +WindowsHookResource::operator HHOOK() const { return _hook; } diff --git a/src/lib/platform/MSWindowsHookResource.h b/src/lib/platform/MSWindowsHookResource.h index b66c4b8c..a3c9d836 100644 --- a/src/lib/platform/MSWindowsHookResource.h +++ b/src/lib/platform/MSWindowsHookResource.h @@ -17,4 +17,4 @@ public: private: HHOOK _hook; -}; \ No newline at end of file +}; diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index e1072c40..c723d19e 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -21,7 +21,6 @@ #include "platform/MSWindowsDesks.h" #include "mt/Thread.h" #include "arch/win32/ArchMiscWindows.h" -#include "base/FunctionJob.h" #include "base/Log.h" #include "base/String.h" #include "base/IEventQueue.h" @@ -804,15 +803,14 @@ MSWindowsKeyState::fakeCtrlAltDel() CloseHandle(hEvtSendSas); } else { - Thread cad(new FunctionJob(&MSWindowsKeyState::ctrlAltDelThread)); + Thread cad([this](){ ctrl_alt_del_thread(); }); cad.wait(); } return true; } -void -MSWindowsKeyState::ctrlAltDelThread(void*) +void MSWindowsKeyState::ctrl_alt_del_thread() { // get the Winlogon desktop at whatever privilege we can HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED); @@ -1330,7 +1328,7 @@ MSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button) const if ((LOWORD(m_keyLayout) & 0xffffu) == 0x0412u) { // 0x0412 : Korean Locale ID if (virtualKey == VK_HANGUL || virtualKey == VK_HANJA) { // If shift-space is used to change the input mode, - // the extented bit is not set. So add it to get right key id. + // the extended bit is not set. So add it to get right key id. button |= 0x100u; } } diff --git a/src/lib/platform/MSWindowsKeyState.h b/src/lib/platform/MSWindowsKeyState.h index 6ea917b6..eedcd852 100644 --- a/src/lib/platform/MSWindowsKeyState.h +++ b/src/lib/platform/MSWindowsKeyState.h @@ -169,7 +169,7 @@ private: typedef std::vector GroupList; // send ctrl+alt+del hotkey event on NT family - static void ctrlAltDelThread(void*); + static void ctrl_alt_del_thread(); bool getGroups(GroupList&) const; void setWindowGroup(SInt32 group); diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 739a1e25..c995a40d 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -36,14 +36,12 @@ #include "mt/Thread.h" #include "arch/win32/ArchMiscWindows.h" #include "arch/Arch.h" -#include "base/FunctionJob.h" #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" -#include "common/win32/KnownFolderPaths.h" #include +#include #include #include @@ -134,8 +132,7 @@ MSWindowsScreen::MSWindowsScreen( m_noHooks, m_screensaver, m_events, - new TMethodJob( - this, &MSWindowsScreen::updateKeysCB), + [this]() { updateKeysCB(); }, stopOnDeskSwitch); m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events); @@ -355,17 +352,13 @@ MSWindowsScreen::leave() forceShowCursor(); if (isDraggingStarted() && !m_isPrimary) { - m_sendDragThread = new Thread( - new TMethodJob( - this, - &MSWindowsScreen::sendDragThread)); + m_sendDragThread = new Thread([this](){ send_drag_thread(); }); } return true; } -void -MSWindowsScreen::sendDragThread(void*) +void MSWindowsScreen::send_drag_thread() { std::string& draggingFilename = getDraggingFilename(); size_t size = draggingFilename.size(); @@ -1713,7 +1706,7 @@ MSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const } void -MSWindowsScreen::updateKeysCB(void*) +MSWindowsScreen::updateKeysCB() { // record which keys we think are down bool down[IKeyState::kNumButtons]; @@ -1916,12 +1909,14 @@ const std::string& MSWindowsScreen::getDropTarget() const { if (m_dropTargetPath.empty()) { - m_dropTargetPath = desktopPath(); - if (!m_dropTargetPath.empty()) { - LOG((CLOG_DEBUG "using desktop for drop target: %s", m_dropTargetPath.c_str())); + // SHGetFolderPath is deprecated in vista, but use it for xp support. + char desktopPath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath))) { + m_dropTargetPath = std::string(desktopPath); + LOG((CLOG_INFO "using desktop for drop target: %s", m_dropTargetPath.c_str())); } else { - LOG((CLOG_ERR "failed to get desktop path, no drop target available")); + LOG((CLOG_ERR "failed to get desktop path, no drop target available, error=%d", GetLastError())); } } return m_dropTargetPath; diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index ae53f1ad..eaa7b88a 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -199,7 +199,7 @@ private: // HACK bool mapPressFromEvent(WPARAM msg, LPARAM button) const; // job to update the key state - void updateKeysCB(void*); + void updateKeysCB(); // determine whether the mouse is hidden by the system and force // it to be displayed if user has entered this secondary screen. @@ -222,7 +222,7 @@ private: // HACK KeyModifierMask state, WPARAM wParam) const; // send drag info and data back to server - void sendDragThread(void*); + void send_drag_thread(); private: struct HotKeyItem { diff --git a/src/lib/platform/MSWindowsScreenSaver.cpp b/src/lib/platform/MSWindowsScreenSaver.cpp index 506fd133..9f93792e 100644 --- a/src/lib/platform/MSWindowsScreenSaver.cpp +++ b/src/lib/platform/MSWindowsScreenSaver.cpp @@ -23,7 +23,6 @@ #include "arch/Arch.h" #include "arch/win32/ArchMiscWindows.h" #include "base/Log.h" -#include "base/TMethodJob.h" #include #include @@ -223,8 +222,7 @@ MSWindowsScreenSaver::watchDesktop() // watch desktop in another thread LOG((CLOG_DEBUG "watching screen saver desktop")); m_active = true; - m_watch = new Thread(new TMethodJob(this, - &MSWindowsScreenSaver::watchDesktopThread)); + m_watch = new Thread([this](){ watch_desktop_thread(); }); } void @@ -238,8 +236,7 @@ MSWindowsScreenSaver::watchProcess(HANDLE process) LOG((CLOG_DEBUG "watching screen saver process")); m_process = process; m_active = true; - m_watch = new Thread(new TMethodJob(this, - &MSWindowsScreenSaver::watchProcessThread)); + m_watch = new Thread([this](){ watch_process_thread(); }); } } @@ -260,8 +257,7 @@ MSWindowsScreenSaver::unwatchProcess() } } -void -MSWindowsScreenSaver::watchDesktopThread(void*) +void MSWindowsScreenSaver::watch_desktop_thread() { DWORD reserved = 0; TCHAR* name = NULL; @@ -283,8 +279,7 @@ MSWindowsScreenSaver::watchDesktopThread(void*) } } -void -MSWindowsScreenSaver::watchProcessThread(void*) +void MSWindowsScreenSaver::watch_process_thread() { for (;;) { Thread::testCancel(); diff --git a/src/lib/platform/MSWindowsScreenSaver.h b/src/lib/platform/MSWindowsScreenSaver.h index 7cb37815..4f1dd5fa 100644 --- a/src/lib/platform/MSWindowsScreenSaver.h +++ b/src/lib/platform/MSWindowsScreenSaver.h @@ -64,8 +64,8 @@ private: void watchDesktop(); void watchProcess(HANDLE process); void unwatchProcess(); - void watchDesktopThread(void*); - void watchProcessThread(void*); + void watch_desktop_thread(); + void watch_process_thread(); void setSecure(bool secure, bool saveSecureAsInt); bool isSecure(bool* wasSecureAnInt) const; diff --git a/src/lib/platform/MSWindowsSession.cpp b/src/lib/platform/MSWindowsSession.cpp index 90b8c142..daa92768 100644 --- a/src/lib/platform/MSWindowsSession.cpp +++ b/src/lib/platform/MSWindowsSession.cpp @@ -94,7 +94,7 @@ MSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL) } std::string nameListJoin; - for(std::list::iterator it = nameList.begin(); + for (std::list::iterator it = nameList.begin(); it != nameList.end(); it++) { nameListJoin.append(*it); nameListJoin.append(", "); diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index be18e6c8..2d9a61c0 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -29,7 +29,6 @@ #include "arch/win32/XArchWindows.h" #include "arch/Arch.h" #include "base/log_outputters.h" -#include "base/TMethodJob.h" #include "base/Log.h" #include "common/Version.h" @@ -84,11 +83,8 @@ MSWindowsWatchdog::MSWindowsWatchdog( void MSWindowsWatchdog::startAsync() { - m_thread = new Thread(new TMethodJob( - this, &MSWindowsWatchdog::mainLoop, nullptr)); - - m_outputThread = new Thread(new TMethodJob( - this, &MSWindowsWatchdog::outputLoop, nullptr)); + m_thread = new Thread([this](){ main_loop(); }); + m_outputThread = new Thread([this](){ output_loop(); }); } void @@ -157,8 +153,7 @@ MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security) } } -void -MSWindowsWatchdog::mainLoop(void*) +void MSWindowsWatchdog::main_loop() { shutdownExistingProcesses(); @@ -421,8 +416,7 @@ MSWindowsWatchdog::getCommand() const return cmd; } -void -MSWindowsWatchdog::outputLoop(void*) +void MSWindowsWatchdog::output_loop() { // +1 char for \0 CHAR buffer[kOutputBufferSize + 1]; diff --git a/src/lib/platform/MSWindowsWatchdog.h b/src/lib/platform/MSWindowsWatchdog.h index 4e752c0c..0595ca00 100644 --- a/src/lib/platform/MSWindowsWatchdog.h +++ b/src/lib/platform/MSWindowsWatchdog.h @@ -48,8 +48,8 @@ public: void setFileLogOutputter(FileLogOutputter* outputter); private: - void mainLoop(void*); - void outputLoop(void*); + void main_loop(); + void output_loop(); void shutdownProcess(HANDLE handle, DWORD pid, int timeout); void shutdownExistingProcesses(); HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security); @@ -81,7 +81,7 @@ private: //! Relauncher error /*! -An error occured in the process watchdog. +An error occurred in the process watchdog. */ class XMSWindowsWatchdogError : public XBarrier { public: diff --git a/src/lib/platform/OSXClipboard.cpp b/src/lib/platform/OSXClipboard.cpp index ed995d0b..ff1779ce 100644 --- a/src/lib/platform/OSXClipboard.cpp +++ b/src/lib/platform/OSXClipboard.cpp @@ -52,7 +52,7 @@ OSXClipboard::OSXClipboard() : OSStatus syncErr = PasteboardSynchronize(m_pboard); if (syncErr != noErr) { - LOG((CLOG_DEBUG "failed to syncronize clipboard: error %i", syncErr)); + LOG((CLOG_DEBUG "failed to synchronize clipboard: error %i", syncErr)); } } diff --git a/src/lib/platform/OSXDragSimulator.m b/src/lib/platform/OSXDragSimulator.mm similarity index 96% rename from src/lib/platform/OSXDragSimulator.m rename to src/lib/platform/OSXDragSimulator.mm index affed383..735aa4a1 100644 --- a/src/lib/platform/OSXDragSimulator.m +++ b/src/lib/platform/OSXDragSimulator.mm @@ -30,9 +30,9 @@ void runCocoaApp() { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - + [NSApplication sharedApplication]; - + NSWindow* window = [[NSWindow alloc] initWithContentRect: NSMakeRect(0, 0, 3, 3) styleMask: NSBorderlessWindowMask @@ -41,16 +41,16 @@ runCocoaApp() [window setTitle: @""]; [window setAlphaValue:0.1]; [window makeKeyAndOrderFront:nil]; - + OSXDragView* dragView = [[OSXDragView alloc] initWithFrame:NSMakeRect(0, 0, 3, 3)]; - + g_dragWindow = window; g_dragView = dragView; [window setContentView: dragView]; - + NSLog(@"starting cocoa loop"); [NSApp run]; - + NSLog(@"cocoa: release"); [pool release]; } @@ -65,25 +65,25 @@ void fakeDragging(const char* str, int cursorX, int cursorY) { g_ext = [NSString stringWithUTF8String:str]; - + dispatch_async(dispatch_get_main_queue(), ^{ NSRect screen = [[NSScreen mainScreen] frame]; - NSLog ( @"screen size: witdh = %f height = %f", screen.size.width, screen.size.height); + NSLog ( @"screen size: width = %f height = %f", screen.size.width, screen.size.height); NSLog ( @"mouseLocation: %d %d", cursorX, cursorY); - + int newPosX = 0; int newPosY = 0; newPosX = cursorX - 1; newPosY = screen.size.height - cursorY - 1; - + NSRect rect = NSMakeRect(newPosX, newPosY, 3, 3); NSLog ( @"newPosX: %d", newPosX); NSLog ( @"newPosY: %d", newPosY); - + [g_dragWindow setFrame:rect display:NO]; [g_dragWindow makeKeyAndOrderFront:nil]; [NSApp activateIgnoringOtherApps:YES]; - + [g_dragView setFileExt:g_ext]; CGEventRef down = CGEventCreateMouseEvent(CGEventSourceCreate(kCGEventSourceStateHIDSystemState), kCGEventLeftMouseDown, CGPointMake(cursorX, cursorY), kCGMouseButtonLeft); diff --git a/src/lib/platform/OSXDragView.m b/src/lib/platform/OSXDragView.mm similarity index 100% rename from src/lib/platform/OSXDragView.m rename to src/lib/platform/OSXDragView.mm diff --git a/src/lib/platform/OSXMediaKeySimulator.m b/src/lib/platform/OSXMediaKeySimulator.mm similarity index 96% rename from src/lib/platform/OSXMediaKeySimulator.m rename to src/lib/platform/OSXMediaKeySimulator.mm index 5aacd107..efc4251b 100644 --- a/src/lib/platform/OSXMediaKeySimulator.m +++ b/src/lib/platform/OSXMediaKeySimulator.mm @@ -85,8 +85,8 @@ fakeNativeMediaKey(KeyID id) data2:-1]; CGEventRef upEvent = [upRef CGEvent]; - CGEventPost(0, downEvent); - CGEventPost(0, upEvent); + CGEventPost(kCGHIDEventTap, downEvent); + CGEventPost(kCGHIDEventTap, upEvent); return true; } diff --git a/src/lib/platform/OSXMediaKeySupport.m b/src/lib/platform/OSXMediaKeySupport.mm similarity index 97% rename from src/lib/platform/OSXMediaKeySupport.m rename to src/lib/platform/OSXMediaKeySupport.mm index 9c9dbc31..a4d57679 100644 --- a/src/lib/platform/OSXMediaKeySupport.m +++ b/src/lib/platform/OSXMediaKeySupport.mm @@ -147,8 +147,8 @@ fakeNativeMediaKey(KeyID id) data2:-1]; CGEventRef upEvent = [upRef CGEvent]; - CGEventPost(0, downEvent); - CGEventPost(0, upEvent); + CGEventPost(kCGHIDEventTap, downEvent); + CGEventPost(kCGHIDEventTap, upEvent); return true; } diff --git a/src/lib/platform/OSXPasteboardPeeker.m b/src/lib/platform/OSXPasteboardPeeker.mm similarity index 100% rename from src/lib/platform/OSXPasteboardPeeker.m rename to src/lib/platform/OSXPasteboardPeeker.mm diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 81c1a1da..691b74c4 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -174,7 +174,7 @@ private: EventRef theEvent, void* inUserData); // sleep / wakeup support - void watchSystemPowerThread(void*); + void watchSystemPowerThread(); static void testCanceled(CFRunLoopTimerRef timer, void*info); static void powerChangeCallback(void* refcon, io_service_t service, natural_t messageType, void* messageArgument); @@ -201,7 +201,7 @@ private: // convert CFString to char* static char* CFStringRefToUTF8String(CFStringRef aString); - void getDropTargetThread(void*); + void get_drop_target_thread(); private: struct HotKeyItem { diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index 2b4594ff..d41e321b 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -2,11 +2,11 @@ * barrier -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman - * + * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file LICENSE that should have accompanied this file. - * + * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -38,7 +38,6 @@ #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include #include @@ -110,10 +109,10 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso updateScreenShape(m_displayID, 0); m_screensaver = new OSXScreenSaver(m_events, getEventTarget()); m_keyState = new OSXKeyState(m_events); - + // only needed when running as a server. if (m_isPrimary) { - + #if defined(MAC_OS_X_VERSION_10_9) // we can't pass options to show the dialog, this must be done by the gui. if (!AXIsProcessTrusted()) { @@ -126,7 +125,7 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso } #endif } - + // install display manager notification handler CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this); @@ -157,8 +156,7 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso m_carbonLoopReady = new CondVar(m_carbonLoopMutex, false); #endif LOG((CLOG_DEBUG "starting watchSystemPowerThread")); - m_pmWatchThread = new Thread(new TMethodJob - (this, &OSXScreen::watchSystemPowerThread)); + m_pmWatchThread = new Thread([this](){ watchSystemPowerThread(); }); } catch (...) { m_events->removeHandler(m_events->forOSXScreen().confirmSleep(), @@ -166,7 +164,7 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso if (m_switchEventHandlerRef != 0) { RemoveEventHandler(m_switchEventHandlerRef); } - + CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this); delete m_keyState; @@ -217,7 +215,7 @@ OSXScreen::~OSXScreen() delete m_keyState; delete m_screensaver; - + #if defined(MAC_OS_X_VERSION_10_7) delete m_carbonLoopMutex; delete m_carbonLoopReady; @@ -273,7 +271,7 @@ OSXScreen::warpCursor(SInt32 x, SInt32 y) pos.x = x; pos.y = y; CGWarpMouseCursorPosition(pos); - + // save new cursor position m_xCursor = x; m_yCursor = y; @@ -325,7 +323,7 @@ OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask)); return 0; } - + // choose hotkey id UInt32 id; if (!m_oldHotKeyIDs.empty()) { @@ -351,7 +349,7 @@ OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) } else { EventHotKeyID hkid = { 'SNRG', (UInt32)id }; - OSStatus status = RegisterEventHotKey(macKey, macMask, hkid, + OSStatus status = RegisterEventHotKey(macKey, macMask, hkid, GetApplicationEventTarget(), 0, &ref); okay = (status == noErr); @@ -366,7 +364,7 @@ OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask) } m_hotKeys.insert(std::make_pair(id, HotKeyItem(ref, macKey, macMask))); - + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask, id)); return id; } @@ -468,7 +466,7 @@ OSXScreen::postMouseEvent(CGPoint& pos) const } } } - + CGEventType type = kCGEventMouseMoved; SInt8 button = m_buttonState.getFirstButtonDown(); @@ -478,10 +476,10 @@ OSXScreen::postMouseEvent(CGPoint& pos) const } CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast(button)); - + // Dragging events also need the click state CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState); - + // Fix for sticky keys CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); CGEventSetFlags(event, modifiers); @@ -503,7 +501,7 @@ OSXScreen::postMouseEvent(CGPoint& pos) const CGEventSetDoubleValueField(event, kCGMouseEventDeltaY, deltaFY); CGEventPost(kCGHIDEventTap, event); - + CFRelease(event); } @@ -515,7 +513,7 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) if (index >= NumButtonIDs) { return; } - + CGPoint pos; if (!m_cursorPosValid) { SInt32 x, y; @@ -533,79 +531,77 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) // since we don't have double click distance in NX APIs // we define our own defaults. const double maxDiff = sqrt(2) + 0.0001; - + double clickTime = [NSEvent doubleClickInterval]; - + // As long as the click is within the time window and distance window // increase clickState (double click, triple click, etc) - // This will allow for higher than triple click but the quartz documenation + // This will allow for higher than triple click but the quartz documentation // does not specify that this should be limited to triple click if (press) { - if ((ARCH->time() - m_lastClickTime) <= clickTime && diff <= maxDiff){ + if ((ARCH->time() - m_lastClickTime) <= clickTime && diff <= maxDiff) { m_clickState++; } else { m_clickState = 1; } - + m_lastClickTime = ARCH->time(); } - - if (m_clickState == 1){ + + if (m_clickState == 1) { m_lastSingleClickXCursor = m_xCursor; m_lastSingleClickYCursor = m_yCursor; } - + EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp; - + LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", index, press ? "pressed" : "released")); - + MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index]; CGEventType type = thisButtonMap[state]; CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast(index)); - + CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState); - + // Fix for sticky keys CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); CGEventSetFlags(event, modifiers); - + m_buttonState.set(index, state); CGEventPost(kCGHIDEventTap, event); - + CFRelease(event); - + if (!press && (id == kButtonLeft)) { if (m_fakeDraggingStarted) { - m_getDropTargetThread = new Thread(new TMethodJob( - this, &OSXScreen::getDropTargetThread)); + m_getDropTargetThread = new Thread([this](){ get_drop_target_thread(); }); } - + m_draggingStarted = false; } } -void -OSXScreen::getDropTargetThread(void*) +void OSXScreen::get_drop_target_thread() { #if defined(MAC_OS_X_VERSION_10_7) char* cstr = NULL; - + // wait for 5 secs for the drop destinaiton string to be filled. UInt32 timeout = ARCH->time() + 5; - + while (ARCH->time() < timeout) { CFStringRef cfstr = getCocoaDropTarget(); cstr = CFStringRefToUTF8String(cfstr); CFRelease(cfstr); - + if (cstr != NULL) { break; } ARCH->sleep(.1f); } - + if (cstr != NULL) { LOG((CLOG_DEBUG "drop target: %s", cstr)); m_dropTarget = cstr; @@ -626,12 +622,12 @@ OSXScreen::fakeMouseMove(SInt32 x, SInt32 y) if (m_fakeDraggingStarted) { m_buttonState.set(0, kMouseButtonDown); } - + // index 0 means left mouse button if (m_buttonState.test(0)) { m_draggingStarted = true; } - + // synthesize event CGPoint pos; pos.x = x; @@ -679,11 +675,11 @@ OSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const NULL, kCGScrollEventUnitLine, 2, mapScrollWheelFromBarrier(yDelta), -mapScrollWheelFromBarrier(xDelta)); - + // Fix for sticky keys CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); CGEventSetFlags(scrollEvent, modifiers); - + CGEventPost(kCGHIDEventTap, scrollEvent); CFRelease(scrollEvent); } @@ -754,11 +750,11 @@ OSXScreen::enable() if (m_isPrimary) { // FIXME -- start watching jump zones - + // kCGEventTapOptionDefault = 0x00000000 (Missing in 10.4, so specified literally) m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, - kCGEventMaskForAllEvents, - handleCGInputEvent, + kCGEventMaskForAllEvents, + handleCGInputEvent, this); } else { @@ -773,10 +769,10 @@ OSXScreen::enable() // there may be a better way to do this, but we register an event handler even if we're // not on the primary display (acting as a client). This way, if a local event comes in - // (either keyboard or mouse), we can make sure to show the cursor if we've hidden it. + // (either keyboard or mouse), we can make sure to show the cursor if we've hidden it. m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, - kCGEventMaskForAllEvents, - handleCGInputEventSecondary, + kCGEventMaskForAllEvents, + handleCGInputEventSecondary, this); } @@ -798,9 +794,9 @@ OSXScreen::disable() if (m_autoShowHideCursor) { showCursor(); } - + // FIXME -- stop watching jump zones, stop capturing input - + if (m_eventTapRLSR) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode); CFRelease(m_eventTapRLSR); @@ -846,7 +842,7 @@ OSXScreen::enter() io_registry_entry_t entry = IORegistryEntryFromPath( kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler"); - + if (entry != MACH_PORT_NULL) { IORegistryEntrySetCFProperty(entry, CFSTR("IORequestIdle"), kCFBooleanFalse); IOObjectRelease(entry); @@ -863,15 +859,15 @@ bool OSXScreen::leave() { hideCursor(); - + if (isDraggingStarted()) { String& fileList = getDraggingFilename(); - + if (!m_isPrimary) { if (fileList.empty() == false) { ClientApp& app = ClientApp::instance(); Client* client = app.getClientPtr(); - + DragInformation di; di.setFilename(fileList); DragFileList dragFileList; @@ -881,7 +877,7 @@ OSXScreen::leave() dragFileList, info); client->sendDragInfo(fileCount, info, info.size()); LOG((CLOG_DEBUG "send dragging file to server")); - + // TODO: what to do with multiple file or even // a folder client->sendFileToServer(fileList.c_str()); @@ -889,7 +885,7 @@ OSXScreen::leave() } m_draggingStarted = false; } - + if (m_isPrimary) { avoidHesitatingCursor(); @@ -906,8 +902,8 @@ OSXScreen::setClipboard(ClipboardID, const IClipboard* src) { if (src != NULL) { LOG((CLOG_DEBUG "setting clipboard")); - Clipboard::copy(&m_pasteboard, src); - } + Clipboard::copy(&m_pasteboard, src); + } return true; } @@ -1036,16 +1032,16 @@ OSXScreen::handleSystemEvent(const Event& event, void*) } break; - case kEventClassKeyboard: + case kEventClassKeyboard: switch (GetEventKind(*carbonEvent)) { case kEventHotKeyPressed: case kEventHotKeyReleased: onHotKey(*carbonEvent); break; } - + break; - + case kEventClassWindow: // 2nd param was formerly GetWindowEventTarget(m_userInputWindow) which is 32-bit only, // however as m_userInputWindow is never initialized to anything we can take advantage of @@ -1076,7 +1072,7 @@ OSXScreen::handleSystemEvent(const Event& event, void*) } } -bool +bool OSXScreen::onMouseMove(CGFloat mx, CGFloat my) { LOG((CLOG_DEBUG2 "mouse move %+f,%+f", mx, my)); @@ -1140,7 +1136,7 @@ OSXScreen::onMouseMove(CGFloat mx, CGFloat my) return true; } -bool +bool OSXScreen::onMouseButton(bool pressed, UInt16 macButton) { // Buttons 2 and 3 are inverted on the mac @@ -1176,7 +1172,7 @@ OSXScreen::onMouseButton(bool pressed, UInt16 macButton) } } } - + if (macButton == kButtonLeft) { EMouseButtonState state = pressed ? kMouseButtonDown : kMouseButtonUp; m_buttonState.set(kButtonLeft - 1, state); @@ -1186,10 +1182,9 @@ OSXScreen::onMouseButton(bool pressed, UInt16 macButton) } else { if (m_fakeDraggingStarted) { - m_getDropTargetThread = new Thread(new TMethodJob( - this, &OSXScreen::getDropTargetThread)); + m_getDropTargetThread = new Thread([this](){ get_drop_target_thread(); }); } - + m_draggingStarted = false; } } @@ -1218,16 +1213,16 @@ OSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDisplay // Closing or opening the lid when an external monitor is // connected causes an kCGDisplayBeginConfigurationFlag event - CGDisplayChangeSummaryFlags mask = kCGDisplayBeginConfigurationFlag | kCGDisplayMovedFlag | - kCGDisplaySetModeFlag | kCGDisplayAddFlag | kCGDisplayRemoveFlag | - kCGDisplayEnabledFlag | kCGDisplayDisabledFlag | - kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag | + CGDisplayChangeSummaryFlags mask = kCGDisplayBeginConfigurationFlag | kCGDisplayMovedFlag | + kCGDisplaySetModeFlag | kCGDisplayAddFlag | kCGDisplayRemoveFlag | + kCGDisplayEnabledFlag | kCGDisplayDisabledFlag | + kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag | kCGDisplayDesktopShapeChangedFlag; - + LOG((CLOG_DEBUG1 "event: display was reconfigured: %x %x %x", flags, mask, flags & mask)); if (flags & mask) { /* Something actually did change */ - + LOG((CLOG_DEBUG1 "event: screen changed shape; refreshing dimensions")); screen->updateScreenShape(displayID, flags); } @@ -1274,7 +1269,7 @@ OSXScreen::onKey(CGEventRef event) m_activeModifierHotKeyMask = 0; } } - + return true; } @@ -1342,7 +1337,7 @@ OSXScreen::onKey(CGEventRef event) } void -OSXScreen::onMediaKey(CGEventRef event) +OSXScreen::onMediaKey(CGEventRef event) { KeyID keyID; bool down; @@ -1405,7 +1400,7 @@ OSXScreen::mapBarrierButtonToMac(UInt16 button) const return static_cast(button); } -ButtonID +ButtonID OSXScreen::mapMacButtonToBarrier(UInt16 macButton) const { switch (macButton) { @@ -1418,7 +1413,7 @@ OSXScreen::mapMacButtonToBarrier(UInt16 macButton) const case 3: return kButtonMiddle; } - + return static_cast(macButton); } @@ -1445,8 +1440,8 @@ OSXScreen::getScrollSpeed() const double scaling = 0.0; CFPropertyListRef pref = ::CFPreferencesCopyValue( - CFSTR("com.apple.scrollwheel.scaling") , - kCFPreferencesAnyApplication, + CFSTR("com.apple.scrollwheel.scaling") , + kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); if (pref != NULL) { @@ -1535,7 +1530,7 @@ OSXScreen::updateScreenShape() if (CGGetActiveDisplayList(0, NULL, &displayCount) != CGDisplayNoErr) { return; } - + if (displayCount == 0) { return; } @@ -1579,13 +1574,13 @@ OSXScreen::updateScreenShape() (displayCount == 1) ? "display" : "displays")); } -#pragma mark - +#pragma mark - // // FAST USER SWITCH NOTIFICATION SUPPORT // // OSXScreen::userSwitchCallback(void*) -// +// // gets called if a fast user switch occurs // @@ -1611,18 +1606,17 @@ OSXScreen::userSwitchCallback(EventHandlerCallRef nextHandler, return (CallNextEventHandler(nextHandler, theEvent)); } -#pragma mark - +#pragma mark - // // SLEEP/WAKEUP NOTIFICATION SUPPORT // // OSXScreen::watchSystemPowerThread(void*) -// -// main of thread monitoring system power (sleep/wakup) using a CFRunLoop +// +// main of thread monitoring system power (sleep/wakeup) using a CFRunLoop // -void -OSXScreen::watchSystemPowerThread(void*) +void OSXScreen::watchSystemPowerThread() { io_object_t notifier; IONotificationPortRef notificationPortRef; @@ -1641,7 +1635,7 @@ OSXScreen::watchSystemPowerThread(void*) CFRunLoopAddSource(m_pmRunloop, runloopSourceRef, kCFRunLoopCommonModes); } - + // thread is ready { Lock lock(m_pmMutex); @@ -1658,15 +1652,15 @@ OSXScreen::watchSystemPowerThread(void*) } LOG((CLOG_DEBUG "started watchSystemPowerThread")); - + LOG((CLOG_DEBUG "waiting for event loop")); m_events->waitForReady(); - + #if defined(MAC_OS_X_VERSION_10_7) { Lock lockCarbon(m_carbonLoopMutex); if (*m_carbonLoopReady == false) { - + // we signalling carbon loop ready before starting // unless we know how to do it within the loop LOG((CLOG_DEBUG "signalling carbon loop ready")); @@ -1676,12 +1670,12 @@ OSXScreen::watchSystemPowerThread(void*) } } #endif - + // start the run loop LOG((CLOG_DEBUG "starting carbon loop")); CFRunLoopRun(); LOG((CLOG_DEBUG "carbon loop has stopped")); - + // cleanup if (notificationPortRef) { CFRunLoopRemoveSource(m_pmRunloop, @@ -1716,7 +1710,7 @@ OSXScreen::handlePowerChangeRequest(natural_t messageType, void* messageArg) getEventTarget(), messageArg, Event::kDontFreeData)); return; - + case kIOMessageSystemHasPoweredOn: LOG((CLOG_DEBUG "system wakeup")); m_events->addEvent(Event(m_events->forIScreen().resume(), @@ -1742,16 +1736,16 @@ OSXScreen::handleConfirmSleep(const Event& event, void*) if (m_pmRootPort != 0) { // deliver suspend event immediately. m_events->addEvent(Event(m_events->forIScreen().suspend(), - getEventTarget(), NULL, + getEventTarget(), NULL, Event::kDeliverImmediately)); - + LOG((CLOG_DEBUG "system will sleep")); IOAllowPowerChange(m_pmRootPort, messageArg); } } } -#pragma mark - +#pragma mark - // // GLOBAL HOTKEY OPERATING MODE SUPPORT (10.3) @@ -1941,7 +1935,7 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, case kCGEventMouseMoved: pos = CGEventGetLocation(event); screen->onMouseMove(pos.x, pos.y); - + // The system ignores our cursor-centering calls if // we don't return the event. This should be harmless, // but might register as slight movement to other apps @@ -1980,10 +1974,10 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, } break; } - + LOG((CLOG_DEBUG3 "unknown quartz event type: 0x%02x", type)); } - + if (screen->m_isOnScreen) { return event; } else { @@ -1992,38 +1986,38 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, } void -OSXScreen::MouseButtonState::set(UInt32 button, EMouseButtonState state) +OSXScreen::MouseButtonState::set(UInt32 button, EMouseButtonState state) { bool newState = (state == kMouseButtonDown); m_buttons.set(button, newState); } bool -OSXScreen::MouseButtonState::any() +OSXScreen::MouseButtonState::any() { return m_buttons.any(); } void -OSXScreen::MouseButtonState::reset() +OSXScreen::MouseButtonState::reset() { m_buttons.reset(); } void -OSXScreen::MouseButtonState::overwrite(UInt32 buttons) +OSXScreen::MouseButtonState::overwrite(UInt32 buttons) { m_buttons = std::bitset(buttons); } bool -OSXScreen::MouseButtonState::test(UInt32 button) const +OSXScreen::MouseButtonState::test(UInt32 button) const { return m_buttons.test(button); } SInt8 -OSXScreen::MouseButtonState::getFirstButtonDown() const +OSXScreen::MouseButtonState::getFirstButtonDown() const { if (m_buttons.any()) { for (unsigned short button = 0; button < m_buttons.size(); button++) { @@ -2041,7 +2035,7 @@ OSXScreen::CFStringRefToUTF8String(CFStringRef aString) if (aString == NULL) { return NULL; } - + CFIndex length = CFStringGetLength(aString); CFIndex maxSize = CFStringGetMaximumSizeForEncoding( length, diff --git a/src/lib/platform/OSXScreenSaverUtil.m b/src/lib/platform/OSXScreenSaverUtil.mm similarity index 100% rename from src/lib/platform/OSXScreenSaverUtil.m rename to src/lib/platform/OSXScreenSaverUtil.mm diff --git a/src/lib/platform/XWindowsClipboardHTMLConverter.cpp b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp index a13e80e4..eae032d5 100644 --- a/src/lib/platform/XWindowsClipboardHTMLConverter.cpp +++ b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp @@ -56,10 +56,17 @@ XWindowsClipboardHTMLConverter::getDataSize() const std::string XWindowsClipboardHTMLConverter::fromIClipboard(const std::string& data) const { - return Unicode::UTF8ToUTF16(data); + return data; } std::string XWindowsClipboardHTMLConverter::toIClipboard(const std::string& data) const { - return Unicode::UTF16ToUTF8(data); + // Older Firefox [1] and possibly other applications use UTF-16 for text/html - handle both + // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1497580 + if (Unicode::isUTF8(data)) { + return data; + } else { + return Unicode::UTF16ToUTF8(data); + } + return data; } diff --git a/src/lib/platform/XWindowsScreenSaver.h b/src/lib/platform/XWindowsScreenSaver.h index cce337fe..00285ec2 100644 --- a/src/lib/platform/XWindowsScreenSaver.h +++ b/src/lib/platform/XWindowsScreenSaver.h @@ -115,7 +115,7 @@ private: // the X display Display* m_display; - // window to receive xscreensaver repsonses + // window to receive xscreensaver responses Window m_xscreensaverSink; // the target for the events we generate diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 04d8a596..75724bc0 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -36,18 +36,17 @@ ClientListener::ClientListener(const NetworkAddress& address, ISocketFactory* socketFactory, IEventQueue* events, - bool enableCrypto) : + ConnectionSecurityLevel security_level) : m_socketFactory(socketFactory), m_server(NULL), m_events(events), - m_useSecureNetwork(enableCrypto) + security_level_{security_level} { assert(m_socketFactory != NULL); try { - m_listen = m_socketFactory->createListen( - ARCH->getAddrFamily(address.getAddress()), - m_useSecureNetwork); + m_listen = m_socketFactory->createListen(ARCH->getAddrFamily(address.getAddress()), + security_level); // setup event handler m_events->adoptHandler(m_events->forIListenSocket().connecting(), @@ -140,7 +139,7 @@ ClientListener::handleClientConnecting(const Event&, void*) // When using non SSL, server accepts clients immediately, while SSL // has to call secure accept which may require retry - if (!m_useSecureNetwork) { + if (security_level_ == ConnectionSecurityLevel::PLAINTEXT) { m_events->addEvent(Event(m_events->forClientListener().accepted(), socket->getEventTarget())); } @@ -184,7 +183,6 @@ ClientListener::handleUnknownClient(const Event&, void* vclient) // get the real client proxy and install it ClientProxy* client = unknownClient->orphanClientProxy(); - bool handshakeOk = true; if (client != NULL) { // handshake was successful m_waitingClients.push_back(client); @@ -196,20 +194,17 @@ ClientListener::handleUnknownClient(const Event&, void* vclient) new TMethodEventJob(this, &ClientListener::handleClientDisconnected, client)); - } - else { - handshakeOk = false; + } else { + auto* stream = unknownClient->getStream(); + if (stream) { + stream->close(); + } } // now finished with unknown client m_events->removeHandler(m_events->forClientProxyUnknown().success(), client); m_events->removeHandler(m_events->forClientProxyUnknown().failure(), client); m_newClients.erase(unknownClient); - PacketStreamFilter* streamFileter = dynamic_cast(unknownClient->getStream()); - IDataSocket* socket = NULL; - if (streamFileter != NULL) { - socket = dynamic_cast(streamFileter->getStream()); - } delete unknownClient; } diff --git a/src/lib/server/ClientListener.h b/src/lib/server/ClientListener.h index 86d962ef..1debc2b9 100644 --- a/src/lib/server/ClientListener.h +++ b/src/lib/server/ClientListener.h @@ -23,6 +23,7 @@ #include "base/Event.h" #include "common/stddeque.h" #include "common/stdset.h" +#include "net/ConnectionSecurityLevel.h" class ClientProxy; class ClientProxyUnknown; @@ -36,10 +37,8 @@ class IDataSocket; class ClientListener { public: // The factories are adopted. - ClientListener(const NetworkAddress&, - ISocketFactory*, - IEventQueue* events, - bool enableCrypto); + ClientListener(const NetworkAddress&, ISocketFactory*, IEventQueue* events, + ConnectionSecurityLevel security_level); ~ClientListener(); //! @name manipulators @@ -86,6 +85,6 @@ private: WaitingClients m_waitingClients; Server* m_server; IEventQueue* m_events; - bool m_useSecureNetwork; + ConnectionSecurityLevel security_level_; ClientSockets m_clientSockets; }; diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index 436e366e..33b0f159 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -51,6 +51,10 @@ ClientProxy1_0::ClientProxy1_0(const std::string& name, barrier::IStream* stream stream->getEventTarget(), new TMethodEventJob(this, &ClientProxy1_0::handleDisconnect, NULL)); + m_events->adoptHandler(m_events->forIStream().inputFormatError(), + stream->getEventTarget(), + new TMethodEventJob(this, + &ClientProxy1_0::handleDisconnect, NULL)); m_events->adoptHandler(m_events->forIStream().outputShutdown(), stream->getEventTarget(), new TMethodEventJob(this, @@ -90,6 +94,8 @@ ClientProxy1_0::removeHandlers() getStream()->getEventTarget()); m_events->removeHandler(m_events->forIStream().outputShutdown(), getStream()->getEventTarget()); + m_events->removeHandler(m_events->forIStream().inputFormatError(), + getStream()->getEventTarget()); m_events->removeHandler(Event::kTimer, this); // remove timer @@ -148,9 +154,18 @@ ClientProxy1_0::handleData(const Event&, void*) } // parse message - LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); - if (!(this->*m_parser)(code)) { - LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + try { + LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + if (!(this->*m_parser)(code)) { + LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3])); + disconnect(); + return; + } + } catch (const XBadClient& e) { + // TODO: disconnect handling is currently dispersed across both parseMessage() and + // handleData() functions, we should collect that to a single place + + LOG((CLOG_ERR "protocol error from client: %s", e.what())); disconnect(); return; } @@ -173,6 +188,8 @@ ClientProxy1_0::parseHandshakeMessage(const UInt8* code) } else if (memcmp(code, kMsgDInfo, 4) == 0) { // future messages get parsed by parseMessage + // NOTE: we're taking address of virtual function here, + // not ClientProxy1_0 implementation of it. m_parser = &ClientProxy1_0::parseMessage; if (recvInfo()) { m_events->addEvent(Event(m_events->forClientProxy().ready(), getEventTarget())); diff --git a/src/lib/server/ClientProxyUnknown.cpp b/src/lib/server/ClientProxyUnknown.cpp index 50a6bdec..f9da361d 100644 --- a/src/lib/server/ClientProxyUnknown.cpp +++ b/src/lib/server/ClientProxyUnknown.cpp @@ -118,6 +118,10 @@ ClientProxyUnknown::addStreamHandlers() m_stream->getEventTarget(), new TMethodEventJob(this, &ClientProxyUnknown::handleDisconnect)); + m_events->adoptHandler(m_events->forIStream().inputFormatError(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &ClientProxyUnknown::handleDisconnect)); m_events->adoptHandler(m_events->forIStream().outputShutdown(), m_stream->getEventTarget(), new TMethodEventJob(this, @@ -149,6 +153,8 @@ ClientProxyUnknown::removeHandlers() m_stream->getEventTarget()); m_events->removeHandler(m_events->forIStream().inputShutdown(), m_stream->getEventTarget()); + m_events->removeHandler(m_events->forIStream().inputFormatError(), + m_stream->getEventTarget()); m_events->removeHandler(m_events->forIStream().outputShutdown(), m_stream->getEventTarget()); } diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 334049cf..a169db16 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -39,7 +39,6 @@ #include "net/XSocket.h" #include "mt/Thread.h" #include "arch/Arch.h" -#include "base/TMethodJob.h" #include "base/IEventQueue.h" #include "base/Log.h" #include "base/TMethodEventJob.h" @@ -1136,9 +1135,9 @@ Server::processOptions() return; } - m_switchNeedsShift = false; // it seems if i don't add these + m_switchNeedsShift = false; // it seems if I don't add these m_switchNeedsControl = false; // lines, the 'reload config' option - m_switchNeedsAlt = false; // doesnt' work correct. + m_switchNeedsAlt = false; // doesn't work correct. bool newRelativeMoves = m_relativeMoves; for (Config::ScreenOptions::const_iterator index = options->begin(); @@ -1824,10 +1823,8 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) && m_active != newScreen && m_waitDragInfoThread) { if (m_sendDragInfoThread == NULL) { - m_sendDragInfoThread = new Thread( - new TMethodJob( - this, - &Server::sendDragInfoThread, newScreen)); + m_sendDragInfoThread = new Thread([this, newScreen]() + { send_drag_info_thread(newScreen); }); } return false; @@ -1843,11 +1840,8 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) return false; } -void -Server::sendDragInfoThread(void* arg) +void Server::send_drag_info_thread(BaseClientProxy* newScreen) { - BaseClientProxy* newScreen = static_cast(arg); - m_dragFileList.clear(); std::string& dragFileList = m_screen->getDraggingFilename(); if (!dragFileList.empty()) { @@ -2087,14 +2081,11 @@ void Server::onFileRecieveCompleted() { if (isReceivedFileSizeValid()) { - m_writeToDropDirThread = new Thread( - new TMethodJob( - this, &Server::writeToDropDirThread)); + m_writeToDropDirThread = new Thread([this]() { write_to_drop_dir_thread(); }); } } -void -Server::writeToDropDirThread(void*) +void Server::write_to_drop_dir_thread() { LOG((CLOG_DEBUG "starting write to drop dir thread")); @@ -2394,17 +2385,12 @@ Server::sendFileToClient(const char* filename) StreamChunker::interruptFile(); } - m_sendFileThread = new Thread( - new TMethodJob( - this, &Server::sendFileThread, - static_cast(const_cast(filename)))); + m_sendFileThread = new Thread([this, filename]() { send_file_thread(filename); }); } -void -Server::sendFileThread(void* data) +void Server::send_file_thread(const char* filename) { try { - char* filename = static_cast(data); LOG((CLOG_DEBUG "sending file to client, filename=%s", filename)); StreamChunker::sendFile(filename, m_events, this); } diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 38bbbd8e..ae8b2bd5 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -167,7 +167,7 @@ public: */ void getClients(std::vector& list) const; - //! Return true if recieved file size is valid + //! Return true if received file size is valid bool isReceivedFileSizeValid(); //! Return expected file data size @@ -357,14 +357,14 @@ private: // force the cursor off of \p client void forceLeaveClient(BaseClientProxy* client); - // thread funciton for sending file - void sendFileThread(void*); + // thread function for sending file + void send_file_thread(const char* filename); // thread function for writing file to drop directory - void writeToDropDirThread(void*); + void write_to_drop_dir_thread(); // thread function for sending drag information - void sendDragInfoThread(void*); + void send_drag_info_thread(BaseClientProxy* newScreen); // send drag info to new client screen void sendDragInfo(BaseClientProxy* newScreen); diff --git a/src/test/global/TestUtils.cpp b/src/test/global/TestUtils.cpp new file mode 100644 index 00000000..6a3193bf --- /dev/null +++ b/src/test/global/TestUtils.cpp @@ -0,0 +1,37 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "TestUtils.h" +#include + +namespace barrier { + +std::vector generate_pseudo_random_bytes(std::size_t seed, std::size_t size) +{ + std::mt19937_64 engine{seed}; + std::uniform_int_distribution dist{0, 255}; + std::vector bytes; + + bytes.reserve(size); + for (std::size_t i = 0; i < size; ++i) { + bytes.push_back(dist(engine)); + } + + return bytes; +} + +} // namespace barrier diff --git a/src/test/global/TestUtils.h b/src/test/global/TestUtils.h new file mode 100644 index 00000000..31050ece --- /dev/null +++ b/src/test/global/TestUtils.h @@ -0,0 +1,30 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef BARRIER_TEST_GLOBAL_TEST_UTILS_H +#define BARRIER_TEST_GLOBAL_TEST_UTILS_H + +#include +#include + +namespace barrier { + +std::vector generate_pseudo_random_bytes(std::size_t seed, std::size_t size); + +} // namespace barrier + +#endif // BARRIER_TEST_GLOBAL_TEST_UTILS_H diff --git a/src/test/integtests/ipc/IpcTests.cpp b/src/test/integtests/ipc/IpcTests.cpp index 5f20f03f..ce15d591 100644 --- a/src/test/integtests/ipc/IpcTests.cpp +++ b/src/test/integtests/ipc/IpcTests.cpp @@ -31,7 +31,6 @@ #include "net/SocketMultiplexer.h" #include "mt/Thread.h" #include "arch/Arch.h" -#include "base/TMethodJob.h" #include "base/String.h" #include "base/Log.h" #include "base/EventQueue.h" diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index 4bd19354..92767bfa 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -36,7 +36,6 @@ #include "net/TCPSocketFactory.h" #include "mt/Thread.h" #include "base/TMethodEventJob.h" -#include "base/TMethodJob.h" #include "base/Log.h" #include @@ -115,7 +114,8 @@ TEST_F(NetworkTests, sendToClient_mockData) // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; @@ -173,7 +173,8 @@ TEST_F(NetworkTests, sendToClient_mockFile) // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; @@ -230,7 +231,8 @@ TEST_F(NetworkTests, sendToServer_mockData) // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; @@ -287,7 +289,8 @@ TEST_F(NetworkTests, sendToServer_mockFile) // server SocketMultiplexer serverSocketMultiplexer; TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer); - ClientListener listener(serverAddress, serverSocketFactory, &m_events, false); + ClientListener listener(serverAddress, serverSocketFactory, &m_events, + ConnectionSecurityLevel::PLAINTEXT); NiceMock serverScreen; NiceMock primaryClient; NiceMock serverConfig; diff --git a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp index 79935d89..6f6edf5b 100644 --- a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp @@ -24,7 +24,6 @@ #include "platform/MSWindowsDesks.h" #include "platform/MSWindowsScreen.h" #include "platform/MSWindowsScreenSaver.h" -#include "base/TMethodJob.h" #include "test/global/gtest.h" #include "test/global/gmock.h" @@ -50,10 +49,7 @@ protected: MSWindowsDesks* newDesks(IEventQueue* eventQueue) { - return new MSWindowsDesks( - true, false, m_screensaver, eventQueue, - new TMethodJob( - this, &MSWindowsKeyStateTests::updateKeysCB), false); + return new MSWindowsDesks(true, false, m_screensaver, eventQueue, [](){}, false); } void* getEventTarget() const @@ -62,9 +58,7 @@ protected: } private: - void updateKeysCB(void*) { } IScreenSaver* m_screensaver; - MSWindowsHook m_hook; }; TEST_F(MSWindowsKeyStateTests, disable_eventQueueNotUsed) diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 5643aa53..cc8e4fc6 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -56,12 +56,38 @@ TEST(StringTests, sprintf_formatWithArgument_formatedString) TEST(StringTests, toHex_plaintext_hexString) { - String subject = "foobar"; + std::vector subject{'f', 'o', 'o', 'b', 'a', 'r'}; int width = 2; - string::toHex(subject, width); + EXPECT_EQ("666f6f626172", string::to_hex(subject, width)); +} - EXPECT_EQ("666f6f626172", subject); +TEST(StringTests, fromhex_plaintext_string) +{ + auto result = string::from_hex("666f6f626172"); + std::string expected = "foobar"; + EXPECT_EQ(result, std::vector(expected.begin(), expected.end())); +} + +TEST(StringTests, fromhex_plaintext_string_colons) +{ + auto result = string::from_hex("66:6f:6f:62:61:72"); + std::string expected = "foobar"; + EXPECT_EQ(result, std::vector(expected.begin(), expected.end())); +} + +TEST(StringTests, fromhex_binary_string) +{ + auto result = string::from_hex("01020304050600fff9"); + auto expected = std::vector{1, 2, 3, 4, 5, 6, 0, 0xff, 0xf9}; + EXPECT_EQ(result, expected); +} + +TEST(StringTests, fromhex_invalid_string) +{ + EXPECT_TRUE(string::from_hex("66:6").empty()); + EXPECT_TRUE(string::from_hex("66:612").empty()); + EXPECT_TRUE(string::from_hex("66:WW").empty()); } TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput) diff --git a/src/test/unittests/net/FingerprintDatabaseTests.cpp b/src/test/unittests/net/FingerprintDatabaseTests.cpp new file mode 100644 index 00000000..61bed0ea --- /dev/null +++ b/src/test/unittests/net/FingerprintDatabaseTests.cpp @@ -0,0 +1,95 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "net/FingerprintDatabase.h" +#include "test/global/gtest.h" + +namespace barrier { + +TEST(FingerprintDatabase, parse_db_line) +{ + ASSERT_FALSE(FingerprintDatabase::parse_db_line("").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("abcd").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v1:algo:something").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:something").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304abc").valid()); + ASSERT_FALSE(FingerprintDatabase::parse_db_line("v2:algo:01020304ZZ").valid()); + ASSERT_EQ(FingerprintDatabase::parse_db_line("v2:algo:01020304ab"), + (FingerprintData{"algo", {1, 2, 3, 4, 0xab}})); +} + +TEST(FingerprintDatabase, read) +{ + std::istringstream stream; + stream.str(R"( +v2:algo1:01020304ab +v2:algo2:03040506ab +AB:CD:EF:00:01:02:03:04:05:06:07:08:09:10:11:12:13:14:15:16 +)"); + FingerprintDatabase db; + db.read_stream(stream); + + std::vector expected = { + { "algo1", { 1, 2, 3, 4, 0xab } }, + { "algo2", { 3, 4, 5, 6, 0xab } }, + { "sha1", { 0xab, 0xcd, 0xef, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 } }, + }; + ASSERT_EQ(db.fingerprints(), expected); +} + +TEST(FingerprintDatabase, write) +{ + std::ostringstream stream; + + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } }); + db.write_stream(stream); + + ASSERT_EQ(stream.str(), R"(v2:algo1:01020304ab +v2:algo2:03040506ab +)"); +} + +TEST(FingerprintDatabase, clear) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.clear(); + ASSERT_TRUE(db.fingerprints().empty()); +} + +TEST(FingerprintDatabase, add_trusted_no_duplicates) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + db.add_trusted({ "algo2", { 3, 4, 5, 6, 0xab } }); + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + ASSERT_EQ(db.fingerprints().size(), 2); +} + +TEST(FingerprintDatabase, is_trusted) +{ + FingerprintDatabase db; + db.add_trusted({ "algo1", { 1, 2, 3, 4, 0xab } }); + ASSERT_TRUE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xab } })); + ASSERT_FALSE(db.is_trusted({ "algo2", { 1, 2, 3, 4, 0xab } })); + ASSERT_FALSE(db.is_trusted({ "algo1", { 1, 2, 3, 4, 0xac } })); +} + +} // namespace barrier diff --git a/src/test/unittests/net/SecureUtilsTests.cpp b/src/test/unittests/net/SecureUtilsTests.cpp new file mode 100644 index 00000000..0cce693a --- /dev/null +++ b/src/test/unittests/net/SecureUtilsTests.cpp @@ -0,0 +1,73 @@ +/* + barrier -- mouse and keyboard sharing utility + Copyright (C) 2021 Barrier contributors + + This package is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + found in the file LICENSE that should have accompanied this file. + + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +#include "net/SecureUtils.h" + +#include "test/global/gtest.h" +#include "test/global/TestUtils.h" + +namespace barrier { + +TEST(SecureUtilsTest, FormatSslFingerprintHexWithSeparators) +{ + auto fingerprint = generate_pseudo_random_bytes(0, 32); + ASSERT_EQ(format_ssl_fingerprint(fingerprint, true), + "28:FD:0A:98:8A:0E:A1:6C:D7:E8:6C:A7:EE:58:41:71:" + "CA:B2:8E:49:25:94:90:25:26:05:8D:AF:63:ED:2E:30"); +} + +TEST(SecureUtilsTest, CreateFingerprintRandomArt) +{ + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(0, 32)), + "+-----------------+\n" + "|*X+. . |\n" + "|*oo + |\n" + "| + = |\n" + "| B . . |\n" + "|.+... o S |\n" + "|E+ ++. . |\n" + "|B*++.. . |\n" + "|+o*o o . |\n" + "|+o*Bo . |\n" + "+-----------------+"); + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(1, 32)), + "+-----------------+\n" + "| .oo+ . .B=. |\n" + "| .o.+ . o o.= |\n" + "|o..+.. o . E * |\n" + "|oo..+ . * * |\n" + "|B o.....S. o . |\n" + "|+=o..... |\n" + "| + + . |\n" + "|o. .. |\n" + "|..o.. |\n" + "+-----------------+"); + ASSERT_EQ(create_fingerprint_randomart(generate_pseudo_random_bytes(2, 32)), + "+-----------------+\n" + "| ... .o.o.|\n" + "| o .=.E|\n" + "| . + o ...+.|\n" + "| * o = o ... |\n" + "| * + S & . |\n" + "| = + % @ |\n" + "| . . = X o |\n" + "| . . O . |\n" + "| . + |\n" + "+-----------------+"); +} + +} // namespace barrier diff --git a/towncrier.toml b/towncrier.toml new file mode 100644 index 00000000..d8f0a61c --- /dev/null +++ b/towncrier.toml @@ -0,0 +1,39 @@ +[tool.towncrier] + package = "" + directory = "doc/newsfragments" + filename = "doc/release_notes/index.md" + template = "doc/release_notes/index.template.jinja" + title_format = "\nBarrier `{version}` ( `{project_date}` )\n================================\n" + start_string = "[comment]: <> (towncrier release notes start)" + [[tool.towncrier.section]] + path = "" + + [[tool.towncrier.type]] + directory = "security" + name = "Security fixes" + showcontent = false + + [[tool.towncrier.type]] + directory = "feature" + name = "Features" + showcontent = true + + [[tool.towncrier.type]] + directory = "bugfix" + name = "Bug fixes" + showcontent = true + + [[tool.towncrier.type]] + directory = "doc" + name = "Improved Documentation" + showcontent = true + + [[tool.towncrier.type]] + directory = "removal" + name = "Deprecations and Removals" + showcontent = true + + [[tool.towncrier.type]] + directory = "misc" + name = "Miscellaneous" + showcontent = false