[feat] use named pipe to launch openvpn

- Resolves: #339
merge-requests/162/head
kali 2021-04-05 16:15:29 +02:00 committed by kali kaneko (leap communications)
parent 048e2914a7
commit d1252ef5b9
7 changed files with 186 additions and 36 deletions

View File

@ -43,7 +43,7 @@ endif
SCRIPTS = branding/scripts
TEMPLATES = branding/templates
TAP_WINDOWS = https://build.openvpn.net/downloads/releases/tap-windows-9.24.2-I601-Win10.exe
OPENVPN_WINDOWS_INSTALLER = https://build.openvpn.net/downloads/releases/OpenVPN-2.5.1-I601-amd64.msi
HAS_QTIFW != which binarycreator.exe 2>/dev/null || PATH=$(PATH) which binarycreator
OPENVPN_BIN != echo -n "$(HOME)/openvpn_build/sbin/$$(grep OPENVPN branding/thirdparty/openvpn/build_openvpn.sh | head -n 1 | cut -d = -f 2 | tr -d '"')"
@ -160,21 +160,11 @@ ifeq (${PLATFORM}, windows)
"c:\windows\system32\rcedit.exe" ${QTBUILD}/release/${TARGET}.exe --set-version-string CompanyName "LEAP Encryption Access Project"
"c:\windows\system32\rcedit.exe" ${QTBUILD}/release/${TARGET}.exe --set-version-string FileDescription "${APPNAME}"
"c:\windows\system32\signtool.exe" sign -debug -f "z:\leap\LEAP.pfx" -p ${WINCERTPASS} ${QTBUILD}/release/${TARGET}.exe
# XXX need to deprecate helper and embrace interactive service
cp build/bin/${PLATFORM}/bitmask-helper build/bin/${PLATFORM}/bitmask-helper.exe
"c:\windows\system32\rcedit.exe" build/bin/${PLATFORM}/bitmask-helper.exe --set-file-version ${VERSION}
"c:\windows\system32\rcedit.exe" build/bin/${PLATFORM}/bitmask-helper.exe --set-product-version ${VERSION}
"c:\windows\system32\rcedit.exe" build/bin/${PLATFORM}/bitmask-helper.exe --set-version-string ProductName "bitmask-helper-v2"
"c:\windows\system32\rcedit.exe" build/bin/${PLATFORM}/bitmask-helper.exe --set-version-string CompanyName "LEAP Encryption Access Project"
"c:\windows\system32\rcedit.exe" build/bin/${PLATFORM}/bitmask-helper.exe --set-version-string FileDescription "Administrative helper for ${APPNAME}"
"c:\windows\system32\signtool.exe" sign -debug -f "z:\leap\LEAP.pfx" -p ${WINCERTPASS} build/bin/${PLATFORM}/bitmask-helper.exe
endif
checksign:
ifeq (${PLATFORM}, windows)
@"c:\windows\system32\sigcheck.exe" ${QTBUILD}/release/${TARGET}.exe
@"c:\windows\system32\sigcheck.exe" build/bin/${PLATFORM}/bitmask-helper.exe
@"c:\windows\system32\sigcheck.exe" "/c/Program Files/OpenVPN/bin/openvpn.exe"
endif
installer: check_qtifw checksign
@ -207,29 +197,24 @@ endif
endif
ifeq (${PLATFORM}, windows)
@VERSION=${VERSION} VENDOR_PATH=${VENDOR_PATH} ${SCRIPTS}/gen-qtinstaller windows ${INSTALLER}
@cp build/bin/${PLATFORM}/bitmask-helper.exe ${INST_DATA}helper.exe
ifeq (${VENDOR_PATH}, providers)
@cp ${VENDOR_PATH}/${PROVIDER}/assets/icon.ico ${INST_DATA}/icon.ico
else
@cp ${VENDOR_PATH}/assets/icon.ico ${INST_DATA}/icon.ico
endif
@cp ${QTBUILD}/release/${TARGET}.exe ${INST_DATA}${TARGET}.exe
@cp "/c/Program Files/OpenVPN/bin/openvpn.exe" ${INST_DATA}
@cp "/c/Program Files/OpenVPN/bin/"*.dll ${INST_DATA}
ifeq (${RELEASE}, yes)
#@windeployqt --release --qmldir gui/components ${INST_DATA}${TARGET}.exe
#FIXME -- cannot find platform plugin
@windeployqt --qmldir gui/components ${INST_DATA}${TARGET}.exe
@windeployqt --qmldir gui/qml ${INST_DATA}${TARGET}.exe # FIXME --release flag cannot find platform plugin
else
@windeployqt --qmldir gui/components ${INST_DATA}${TARGET}.exe
endif
# TODO stage it to shave some time
@wget ${TAP_WINDOWS} -O ${INST_DATA}/tap-windows.exe
# XXX this is a workaround for missing libs after windeployqt ---
@cp /c/Qt/5.15.2/mingw81_64/bin/libgcc_s_seh-1.dll ${INST_DATA}
@cp /c/Qt/5.15.2/mingw81_64/bin/libstdc++-6.dll ${INST_DATA}
@cp /c/Qt/5.15.2/mingw81_64/bin/libwinpthread-1.dll ${INST_DATA}
@cp -r /c/Qt/5.15.2/mingw81_64/qml ${INST_DATA}
# TODO stage it
@wget ${OPENVPN_WINDOWS_INSTALLER} -O ${INST_DATA}openvpn-installer.msi
endif
ifeq (${PLATFORM}, linux)
@VERSION=${VERSION} ${SCRIPTS}/gen-qtinstaller linux ${INSTALLER}

View File

@ -99,24 +99,18 @@ function preInstallWindows() {
}
function postInstallWindows() {
// TODO - check if we're on Windows10 or older, and use the needed tap-windows installer accordingly.
console.log("Installing OpenVPN tap driver");
component.addElevatedOperation("Execute", "@TargetDir@/tap-windows.exe", "/S", "/SELECT_UTILITIES=1"); /* TODO uninstall */
/* remove an existing service, if it is stopped. Remove-Service is only in PS>6, and sc.exe delete leaves some garbage on the registry, so let's use the binary itself */
console.log("Removing any previously installer helper...");
component.addElevatedOperation("Execute", "{0,1}", "@TargetDir@/helper.exe", "remove");
console.log("Now trying to install latest helper");
component.addElevatedOperation("Execute", "@TargetDir@/helper.exe", "install", "UNDOEXECUTE", "@TargetDir@/helper.exe", "remove");
component.addElevatedOperation("Execute", "@TargetDir@/helper.exe", "start", "UNDOEXECUTE", "@TargetDir@/helper.exe", "stop");
console.log("Adding shortcut entries/...");
// TODO - we probably need to package different flavors of the installer for windows 8, arm, i386 etc, and change the installer we ship too.
console.log("Installing OpenVPN binaries and service");
component.addElevatedOperation("Execute", "{0}", "msiexec", "/i", "@TargetDir@\\openvpn-installer.msi", "ADDLOCAL=OpenVPN.Service,OpenVPN,Drivers,Drivers.TAPWindows6,Drivers.Wintun", "/passive")
console.log("Adding shortcut entries...");
component.addElevatedOperation("Mkdir", "@StartMenuDir@");
component.addElevatedOperation("CreateShortcut", "@TargetDir@/$BINNAME.exe", "@StartMenuDir@/$APPNAME.lnk", "workingDirectory=@TargetDir@", "iconPath=@TargetDir@/icon.ico", "description=Start $APPNAME");
component.addElevatedOperation("CreateShortcut", "@TargetDir@\\$BINNAME.exe", "@StartMenuDir@\\$APPNAME.lnk", "workingDirectory=@TargetDir@", "iconPath=@TargetDir@\\icon.ico", "description=Start $APPNAME");
// TODO I think this one is not being created because the path doesn't exist yet. We might want to do this by hooking on the installation finished signal instead.
component.addElevatedOperation(
"CreateShortcut",
"@TargetDir@/Uninstall-$APPNAME.exe",
"@StartMenuDir@/Uninstall.lnk"
"@TargetDir@\\Uninstall-$APPNAME.exe",
"@StartMenuDir@\\Uninstall.lnk"
);
}

View File

@ -42,8 +42,9 @@ var (
func getPlatformOpenvpnFlags() []string {
return []string{
"--script-security", "1",
"--script-security", "0",
"--block-outside-dns",
"--redirect-gateway",
}
}

View File

@ -40,6 +40,7 @@ var bitmaskRootPaths = []string{
type launcher struct {
openvpnCh chan []string
failed bool
mngPass string
}
func newLauncher() (*launcher, error) {

View File

@ -1,5 +1,5 @@
// +build !linux
// Copyright (C) 2018-2020 LEAP
// +build osx
// Copyright (C) 2018-2021 LEAP
//
// 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
@ -36,6 +36,7 @@ import (
type launcher struct {
helperAddr string
failed bool
mngPass string
}
const initialHelperPort = 7171

158
pkg/vpn/launcher_windows.go Normal file
View File

@ -0,0 +1,158 @@
// +build windows
// Copyright (C) 2018-2021 LEAP
//
// 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 3 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, see <http://www.gnu.org/licenses/>.
package vpn
import (
"errors"
"log"
"os"
"strings"
"bufio"
"fmt"
"unicode/utf16"
"bytes"
"time"
"encoding/binary"
"github.com/natefinch/npipe"
"0xacab.org/leap/bitmask-vpn/pkg/vpn/bonafide"
)
const pipeName = `\\.\pipe\openvpn\service`
type launcher struct {
mngPass string
}
func newLauncher() (*launcher, error) {
l := launcher{}
return &l, nil
}
func (l *launcher) close() error {
return nil
}
func (l *launcher) check() (helpers bool, privilege bool, err error) {
// TODO check if the named pipe exists
return true, true, nil
}
func (l *launcher) openvpnStart(flags ...string) error {
var b bytes.Buffer
var filtered []string
for _, v := range flags {
if v != "--tun-ipv6" {
filtered = append(filtered, v)
}
}
cwd, _ := os.Getwd()
opts := `--client --dev tun --block-outside-dns --redirect-gateway --script-security 0 ` + strings.Join(filtered, " ")
log.Println("openvpn start: ", opts)
timeout := 3 * time.Second
conn, err := npipe.DialTimeout(pipeName, timeout)
if err != nil {
fmt.Println("ERROR opening pipe")
return errors.New("cannot open openvpn pipe")
}
defer conn.Close()
writeUTF16Bytes(&b, cwd)
writeUTF16Bytes(&b, opts)
writeUTF16Bytes(&b, `\n`)
encoded := b.Bytes()
rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
_, err = rw.Write(encoded)
if err != nil {
fmt.Println("ERROR writing to pipe")
return errors.New("cannot write to openvpn pipe")
}
rw.Flush()
pid, err := getCommandResponse(rw)
if err != nil {
fmt.Println("ERROR getting pid")
}
fmt.Println("OpenVPN PID:", pid)
return nil
}
func (l *launcher) openvpnStop() error {
return nil
}
// TODO we will have to bring our helper back to do firewall
func (l *launcher) firewallStart(gateways []bonafide.Gateway) error {
log.Println("NO firewall in windows")
return nil
}
func (l *launcher) firewallStop() error {
log.Println("NO firewall in windows")
return nil
}
func (l *launcher) firewallIsUp() bool {
log.Println("NO firewall in windows")
return true
}
func writeUTF16Bytes(b *bytes.Buffer, in string) {
var u16 []uint16 = utf16.Encode([]rune(in + "\x00"))
binary.Write(b, binary.LittleEndian, u16)
}
func decodeUTF16String(s string) int {
var code int
var dec []byte
for _, v := range []byte(s) {
if byte(v) != byte(0) {
dec = append(dec, v)
}
}
_, err := fmt.Sscanf(string(dec), "%v", &code)
if err != nil {
fmt.Println("ERROR decoding")
}
return code
}
func getCommandResponse(rw *bufio.ReadWriter) (int, error) {
msg, err := rw.ReadString('\n')
if err != nil {
fmt.Println("ERROR reading")
}
ok := decodeUTF16String(msg)
if ok != 0 {
return -1, errors.New("command failed")
}
msg, err = rw.ReadString('\n')
if err != nil {
fmt.Println("ERROR reading")
}
pid := decodeUTF16String(msg)
if pid == 0 {
return -1, errors.New("command failed")
}
return pid, nil
}

View File

@ -265,7 +265,17 @@ func (b *Bitmask) StopVPN() error {
b.obfsvpnProxy.Stop()
b.obfsvpnProxy = nil
}
return b.launch.openvpnStop()
b.stopFromManagement()
b.launch.openvpnStop()
return nil
}
func (b *Bitmask) stopFromManagement() error {
if b.managementClient == nil {
return fmt.Errorf("No management connected")
}
b.managementClient.SendSignal("SIGTERM")
return nil
}
// Reconnect to the VPN