summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/vpn/launchers/darwin.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/bitmask/vpn/launchers/darwin.py')
-rw-r--r--src/leap/bitmask/vpn/launchers/darwin.py199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/leap/bitmask/vpn/launchers/darwin.py b/src/leap/bitmask/vpn/launchers/darwin.py
new file mode 100644
index 0000000..f19404c
--- /dev/null
+++ b/src/leap/bitmask/vpn/launchers/darwin.py
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+# darwin.py
+# Copyright (C) 2013-2017 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/>.
+"""
+Darwin VPN launcher implementation.
+"""
+import commands
+import getpass
+import os
+import sys
+
+from twisted.logger import Logger
+
+from leap.bitmask.vpn.launcher import VPNLauncher
+from leap.bitmask.vpn.launcher import VPNLauncherException
+from leap.bitmask.vpn.utils import get_path_prefix
+
+
+logger = Logger()
+
+
+class EIPNoTunKextLoaded(VPNLauncherException):
+ pass
+
+
+class DarwinVPNLauncher(VPNLauncher):
+ """
+ VPN launcher for the Darwin Platform
+ """
+ COCOASUDO = "cocoasudo"
+ # XXX need the good old magic translate for these strings
+ # (look for magic in 0.2.0 release)
+ SUDO_MSG = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ INSTALL_MSG = ("\"Bitmask needs administrative privileges to install "
+ "missing scripts and fix permissions.\"")
+
+ # Hardcode the installation path for OSX for security, openvpn is
+ # run as root
+ INSTALL_PATH = "/Applications/Bitmask.app/"
+ INSTALL_PATH_ESCAPED = os.path.realpath(os.getcwd() + "/../../")
+ OPENVPN_BIN = 'openvpn.leap'
+ OPENVPN_PATH = "%s/Contents/Resources/openvpn" % (INSTALL_PATH,)
+ OPENVPN_PATH_ESCAPED = "%s/Contents/Resources/openvpn" % (
+ INSTALL_PATH_ESCAPED,)
+ OPENVPN_BIN_PATH = "%s/Contents/Resources/%s" % (INSTALL_PATH,
+ OPENVPN_BIN)
+
+ UP_SCRIPT = "%s/client.up.sh" % (OPENVPN_PATH,)
+ DOWN_SCRIPT = "%s/client.down.sh" % (OPENVPN_PATH,)
+ OPENVPN_DOWN_PLUGIN = '%s/openvpn-down-root.so' % (OPENVPN_PATH,)
+
+ UPDOWN_FILES = (UP_SCRIPT, DOWN_SCRIPT, OPENVPN_DOWN_PLUGIN)
+ OTHER_FILES = []
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath):
+ """
+ Returns a command that can copy the missing scripts.
+ :rtype: str
+ """
+ to = kls.OPENVPN_PATH_ESCAPED
+
+ cmd = "#!/bin/sh\n"
+ cmd += "mkdir -p {0}\n".format(to)
+ cmd += "cp '{0}'/* {1}\n".format(frompath, to)
+ cmd += "chmod 744 {0}/*".format(to)
+
+ return cmd
+
+ @classmethod
+ def is_kext_loaded(kls):
+ """
+ Checks if the needed kext is loaded before launching openvpn.
+
+ :returns: True if kext is loaded, False otherwise.
+ :rtype: bool
+ """
+ return bool(commands.getoutput('kextstat | grep "leap.tun"'))
+
+ @classmethod
+ def _get_icon_path(kls):
+ """
+ Returns the absolute path to the app icon.
+
+ :rtype: str
+ """
+ resources_path = os.path.abspath(
+ os.path.join(os.getcwd(), "../../Contents/Resources"))
+
+ return os.path.join(resources_path, "bitmask.tiff")
+
+ @classmethod
+ def get_cocoasudo_ovpn_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to run openvpn
+ as admin with a nice password prompt. The actual command needs to be
+ appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ sudo_msg = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (sudo_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_cocoasudo_installmissing_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to install missing
+ files as admin with a nice password prompt. The actual command needs to
+ be appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ install_msg = ('"Bitmask needs administrative privileges to install '
+ 'missing scripts and fix permissions."')
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (install_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="unix", openvpn_verb=1):
+ """
+ Returns the OSX implementation for the vpn launching command.
+
+ Might raise:
+ EIPNoTunKextLoaded,
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ if not kls.is_kext_loaded():
+ raise EIPNoTunKextLoaded
+
+ # we use `super` in order to send the class to use
+ command = super(DarwinVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ cocoa, cargs = kls.get_cocoasudo_ovpn_cmd()
+ cargs.extend(command)
+ command = cargs
+ command.insert(0, cocoa)
+
+ command.extend(['--setenv', "LEAPUSER", getpass.getuser()])
+
+ return command
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ ld_library_path = os.path.join(get_path_prefix(), "..", "lib")
+ ld_library_path.encode(sys.getfilesystemencoding())
+ return {
+ "DYLD_LIBRARY_PATH": ld_library_path
+ }