# -*- coding: utf-8 -*-
# Copyright 2018 Ross Jacobs All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Given VPN vars, uses OS built-ins to create/connect a L2TP/IPSEC VPN."""
from src.modules.os_utils import pyinstaller_path
import sys
import subprocess
from os import system
[docs]class VpnConnection:
"""
VpnConnection list arguments
vpn_data
vpn_name
psk
ip
ddns
username
password
windows_options
dns_suffix
idle_disconnect_seconds
split_tunneled
remember_credentials
use_winlogon
DEBUG
VpnConnection takes 2 lists as args: vpn_data and vpn_options
Required VPN parameters will arrive in vpn_data
Any OS-specific VPN parameters will go into vpn_options
"""
def __init__(self, vpn_data):
super(VpnConnection, self).__init__()
print('showing')
self.vpn_data = vpn_data
self.vpn_options = []
self.vpn_name = ''
[docs] def sanitize_variables(self):
"""Sanitize variables for powershell/bash input."""
for i in range(len(self.vpn_data)):
# Convert to string
self.vpn_data[i] = str(self.vpn_data[i])
# '$' -> '`$' for powershell and '$' -> '\$' for bash
self.vpn_data[i] = self.vpn_data[i].replace('$', '`$')
# Surround each var with double quotes in case of spaces
self.vpn_data[i] = '\"' + self.vpn_data[i] + '\"'
[docs] def attempt_windows_vpn(self, vpn_options):
"""Attempt to connect to Windows VPN.
* Arguments sent to powershell MUST BE STRINGS
* Each argument cannot be the empty string or null or PS will think
there's no param there!!!
* Last 3 ps params are bools converted to ints (0/1) converted to
strings. It's easy to force convert '0' and '1' to ints on
powershell side.
* Setting execution policy to unrestricted is necessary so that we
can access VPN functions
* Email CANNOT have spaces, but password can.
"""
self.sanitize_variables()
self.vpn_options = vpn_options
# 32bit powershell path :
# 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
# Opinionated view that 32bit is not necessary
powershell_path = \
'C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe'
for i in range(len(self.vpn_options)):
# Convert to string
self.vpn_options[i] = str(self.vpn_options[i])
return subprocess.call(
[powershell_path, '-ExecutionPolicy', 'Unrestricted',
pyinstaller_path('src\scripts\connect_windows.ps1'),
*self.vpn_data, *self.vpn_options])
# subprocess.Popen([], creationflags=subprocess.CREATE_NEW_CONSOLE)
# open ps window
[docs] def attempt_macos_vpn(self, vpn_options):
"""Attempt to connect over VPN on macOS.
scutil is required to add the VPN to the active set. Without this,
it is not possible to connect, even if a VPN is listed in Network
Services
scutil --nc select <connection> throws '0:227: execution error: No
service (1)'. if it's a part of the build script instead of here.
This is why it's added directly to the osacript request.
Connection name with forced quotes in case it has spaces.
"""
self.vpn_options = vpn_options
print("Creating macOS VPN")
self.vpn_name = self.vpn_data[0]
scutil_string = 'scutil --nc select ' + '\'' + self.vpn_name + '\''
print("scutil_string: " + scutil_string)
# Create an applescript execution string so we don't
# need to bother with parsing arguments with Popen
command = 'do shell script \"/bin/bash src/scripts/build_macos_vpn.sh' \
+ ' \'' + self.vpn_data[0] + '\' \'' + self.vpn_data[1] + \
'\' \'' + self.vpn_data[2] + '\' \'' + self.vpn_data[3] + \
'\' \'' + self.vpn_data[4] + '\'; ' + scutil_string + \
'\" with administrator privileges'
# Applescript will prompt the user for credentials in order to create
# the VPN connection
print("command being run: " + command)
result = subprocess.Popen(['/usr/bin/osascript', '-e', command],
stdout=subprocess.PIPE)
# Get the result of VPN creation and print
output = result.stdout.read()
print(output.decode('utf-8'))
# Connect to VPN.
# Putting 'f' before a string allows you to insert vars in scope
print("Connecting to macOS VPN")
print("Current working directory: " + str(system('pwd')))
return subprocess.call(
['bash',
'src/scripts/connect_macos.sh',
self.vpn_name]
)
[docs] def attempt_linux_vpn(self, vpn_options):
"""Attempt to connect on linux.
* sudo required to create a connection with nmcli
* pkexec is built into latest Fedora, Debian, Ubuntu.
* 'pkexec <cmd>' correctly asks in GUI on Debian, Ubuntu but in
terminal on Fedora
* pkexec is PolicyKit, which is the preferred means of asking for
permission on LSB
"""
self.vpn_options = vpn_options
# set execution bit on bash script
system('chmod a+x ' + pyinstaller_path('src/scripts/connect_linux.sh'))
return subprocess.Popen(['pkexec', pyinstaller_path(
'src/scripts/connect_linux.sh'), *self.vpn_data])
[docs] def disconnect(self):
"""Disconnect any connected VPN """
if self.is_vpn_connected():
system('rasdial ' + self.vpn_name + ' /disconnect')
[docs] @staticmethod
def is_vpn_connected():
"""Detect whether VPN is connected or not."""
if sys.platform == 'win32':
rasdial_status = \
subprocess.Popen(['rasdial'], stdout=subprocess.PIPE
).communicate()[0].decode('utf-8')
return 'Connected to' in rasdial_status
elif sys.platform == 'darwin':
pass
elif sys.platform.startswith('linux'):
pass