From 738b4bf8c6b75a1d73b7fa3e1a5edb69adf9d8a0 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 21 Aug 2012 04:58:05 +0900 Subject: fix out-of-sync refactor. manually merge changes from the develop branch that were lost due to having branched a previous state when refactored former "conductor" class. also, moved more exceptions to its own file. --- src/leap/eip/config.py | 38 ++----- src/leap/eip/eipconnection.py | 7 +- src/leap/eip/exceptions.py | 34 ++++++ src/leap/eip/openvpnconnection.py | 231 +++++++++++++++++++------------------- 4 files changed, 159 insertions(+), 151 deletions(-) (limited to 'src/leap/eip') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 6118c9de..8e55d789 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -9,37 +9,15 @@ from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) +from leap.eip import exceptions as eip_exceptions logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') -# XXX move exceptions to -# from leap.eip import exceptions as eip_exceptions - - -class EIPNoPkexecAvailable(Exception): - pass - - -class EIPNoPolkitAuthAgentAvailable(Exception): - pass - - -class EIPInitNoProviderError(Exception): - pass - - -class EIPInitBadProviderError(Exception): - pass - - -class EIPInitNoKeyFileError(Exception): - pass - - -class EIPInitBadKeyFilePermError(Exception): - pass - +# XXX this has to be REMOVED +# and all these options passed in the +# command line --> move to build_ovpn_command +# issue #447 OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} @@ -278,11 +256,12 @@ def build_ovpn_command(config, debug=False): # XXX check for both pkexec (done) # AND a suitable authentication # agent running. + # (until we implement setuid helper) logger.info('use_pkexec set to True') if not is_pkexec_in_system(): logger.error('no pkexec in system') - raise EIPNoPkexecAvailable + raise eip_exceptions.EIPNoPkexecAvailable if not is_auth_agent_running(): logger.warning( @@ -290,7 +269,7 @@ def build_ovpn_command(config, debug=False): "pkexec will use its own text " "based authentication agent. " "that's probably a bad idea") - raise EIPNoPolkitAuthAgentAvailable + raise eip_exceptions.EIPNoPolkitAuthAgentAvailable command.append('pkexec') @@ -312,7 +291,6 @@ def build_ovpn_command(config, debug=False): command.append(opt) # XXX check len and raise proper error - return [command[0], command[1:]] diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py index 7e6c4038..139ee750 100644 --- a/src/leap/eip/eipconnection.py +++ b/src/leap/eip/eipconnection.py @@ -8,8 +8,7 @@ logger = logging.getLogger(name=__name__) from leap.base.connection import ConnectionError from leap.eip import exceptions as eip_exceptions -from leap.eip.openvpnconnection import ( - OpenVPNConnection, ConnectionRefusedError) +from leap.eip.openvpnconnection import OpenVPNConnection class EIPConnection(OpenVPNConnection): @@ -25,7 +24,7 @@ class EIPConnection(OpenVPNConnection): self.settingsfile = kwargs.get('settingsfile', None) self.logfile = kwargs.get('logfile', None) self.error_queue = [] - self.desired_con_state = None # ??? + #self.desired_con_state = None # not in use status_signals = kwargs.pop('status_signals', None) self.status = EIPConnectionStatus(callbacks=status_signals) @@ -70,7 +69,7 @@ class EIPConnection(OpenVPNConnection): """ try: state = self.get_connection_state() - except ConnectionRefusedError: + except eip_exceptions.ConnectionRefusedError: # connection refused. might be not ready yet. return if not state: diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py index bd6489ce..ac61f42b 100644 --- a/src/leap/eip/exceptions.py +++ b/src/leap/eip/exceptions.py @@ -29,3 +29,37 @@ class UnrecoverableError(EIPClientError): # to translate whatever kind of error # to user-friendly msg in dialog. pass + + +class MissingSocketError(Exception): + pass + + +class ConnectionRefusedError(Exception): + pass + + +class EIPNoPkexecAvailable(Exception): + pass + + +class EIPNoPolkitAuthAgentAvailable(Exception): + pass + + +class EIPInitNoProviderError(Exception): + pass + + +class EIPInitBadProviderError(Exception): + pass + + +class EIPInitNoKeyFileError(Exception): + pass + + +class EIPInitBadKeyFilePermError(Exception): + pass + + diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py index d3ce3578..601bb54a 100644 --- a/src/leap/eip/openvpnconnection.py +++ b/src/leap/eip/openvpnconnection.py @@ -1,20 +1,20 @@ """ OpenVPN Connection """ - from __future__ import (print_function) import logging -import os import socket -import telnetlib import time from functools import partial logger = logging.getLogger(name=__name__) -from leap.util.coroutines import spawn_and_watch_process -from leap.eip.config import get_config from leap.base.connection import Connection +from leap.util.coroutines import spawn_and_watch_process + +from leap.eip.udstelnet import UDSTelnet +from leap.eip import config as eip_config +from leap.eip import exceptions as eip_exceptions class OpenVPNConnection(Connection): @@ -43,6 +43,8 @@ to be triggered for each one of them. :type watcher_cb: function :type signal_map: dict """ + self.debug = debug + #print('conductor:%s' % debug) self.config_file = config_file self.watcher_cb = watcher_cb @@ -55,12 +57,29 @@ to be triggered for each one of them. self.port = None self.proto = None + self.missing_pkexec = False + self.missing_auth_agent = False + self.bad_keyfile_perms = False + self.missing_vpn_keyfile = False + self.missing_provider = False + self.bad_provider = False + + #XXX workaround for signaling + #the ui that we don't know how to + #manage a connection error + self.with_errors = False + + self.command = None + self.args = None + self.autostart = True + self._get_or_create_config() + self._check_vpn_keys() - self._get_config() + # + # management init methods + # - #Get this info from the Configuration Class - #XXX hardcoded host here. change. self.host = host if isinstance(port, str) and port.isdigit(): port = int(port) @@ -68,48 +87,92 @@ to be triggered for each one of them. self.password = password self.tn = None - #XXX workaround for signaling - #the ui that we don't know how to - #manage a connection error - self.with_errors = False - - #def _set_command_mockup(self): - #""" - #sets command and args for a command mockup - #that just mimics the output from the real thing - #""" - #command, args = get_vpn_stdout_mockup() - #self.command, self.args = command, args - - def _get_config(self): - """ - retrieves the config options from defaults or - home file, or config file passed in command line. - """ - #XXX merge! was changed in test-eip branch!!! - config = get_config(config_file=self.config_file) - self.config = config + def _set_autostart(self): + config = self.config + if config.has_option('openvpn', 'autostart'): + autostart = config.getboolean('openvpn', + 'autostart') + self.autostart = autostart + else: + if config.has_option('DEFAULT', 'autostart'): + autostart = config.getboolean('DEFAULT', + 'autostart') + self.autostart = autostart + def _set_ovpn_command(self): + config = self.config if config.has_option('openvpn', 'command'): commandline = config.get('openvpn', 'command') - if commandline == "mockup": - self._set_command_mockup() - return + command_split = commandline.split(' ') command = command_split[0] if len(command_split) > 1: args = command_split[1:] else: args = [] + + self.command = command + self.args = args + else: + # no command in config, we build it up. + # XXX check also for command-line --command flag + try: + command, args = eip_config.build_ovpn_command( + config, + debug=self.debug) + except eip_exceptions.EIPNoPolkitAuthAgentAvailable: + command = args = None + self.missing_auth_agent = True + except eip_exceptions.EIPNoPkexecAvailable: + command = args = None + self.missing_pkexec = True + + # XXX if not command, signal error. self.command = command - #print("debug: command = %s" % command) self.args = args - #else: - #self._set_command_mockup() - if config.has_option('openvpn', 'autostart'): - autostart = config.get('openvpn', 'autostart') - self.autostart = autostart + def _check_ovpn_config(self): + """ + checks if there is a default openvpn config. + if not, it writes one with info from the provider + definition file + """ + # TODO + # - get --with-openvpn-config from opts + try: + eip_config.check_or_create_default_vpnconf(self.config) + except eip_exceptions.EIPInitNoProviderError: + logger.error('missing default provider definition') + self.missing_provider = True + except eip_exceptions.EIPInitBadProviderError: + logger.error('bad provider definition') + self.bad_provider = True + + def _get_or_create_config(self): + """ + retrieves the config options from defaults or + home file, or config file passed in command line. + populates command and args to be passed to subprocess. + """ + config = eip_config.get_config( + config_file=self.config_file) + self.config = config + + self._set_autostart() + self._set_ovpn_command() + self._check_ovpn_config() + + def _check_vpn_keys(self): + """ + checks for correct permissions on vpn keys + """ + try: + eip_config.check_vpn_keys(self.config) + except eip_exceptions.EIPInitNoKeyFileError: + self.missing_vpn_keyfile = True + except eip_exceptions.EIPInitBadKeyFilePermError: + logger.error('error while checking vpn keys') + self.bad_keyfile_perms = True def _launch_openvpn(self): """ @@ -126,7 +189,7 @@ to be triggered for each one of them. linewrite_callback = lambda line: print('watcher: %s' % line) observers = (linewrite_callback, - partial(self.status_watcher, self.status)) + partial(lambda: None, self.status)) subp, watcher = spawn_and_watch_process( self.command, self.args, @@ -134,13 +197,12 @@ to be triggered for each one of them. self.subp = subp self.watcher = watcher - conn_result = self.status.CONNECTED - return conn_result - def _try_connection(self): """ attempts to connect """ + if self.command is None: + raise eip_exceptions.EIPNoCommandError if self.subp is not None: print('cowardly refusing to launch subprocess again') return @@ -153,17 +215,14 @@ to be triggered for each one of them. if self.subp: self.subp.terminate() - - #Here are the actual code to manage OpenVPN Connection - #TODO: Look into abstraction them and moving them up into base class - # this code based on code from cube-routed project - - """ - Run commands over OpenVPN management interface - and parses the output. - """ - # XXX might need a lock to avoid - # race conditions here... + # + # management methods + # + # XXX REVIEW-ME + # REFACTOR INFO: (former "manager". + # Can we move to another + # base class to test independently?) + # def forget_errors(self): print('forgetting errors') @@ -182,7 +241,7 @@ to be triggered for each one of them. self.tn = UDSTelnet(self.host, self.port) # XXX make password optional - # specially for win plat. we should generate + # specially for win. we should generate # the pass on the fly when invoking manager # from conductor @@ -207,7 +266,6 @@ to be triggered for each one of them. Returns True if connected rtype: bool """ - #return bool(getattr(self, 'tn', None)) try: assert self.tn return True @@ -235,7 +293,7 @@ to be triggered for each one of them. if not self.connected(): try: self.connect() - except MissingSocketError: + except eip_exceptions.MissingSocketError: #XXX capture more helpful error #messages #pass @@ -352,64 +410,3 @@ to be triggered for each one of them. ts = time.gmtime(float(ts)) # XXX this could be a named tuple. prettier. return ts, status_step, ok, ip, remote - - def status_watcher(self, cs, line): - """ - a wrapper that calls to ConnectionStatus object - :param cs: a EIPConnectionStatus instance - :type cs: EIPConnectionStatus object - :param line: a single line of the watched output - :type line: str - """ - #print('status watcher watching') - - # from the mullvad code, should watch for - # things like: - # "Initialization Sequence Completed" - # "With Errors" - # "Tap-Win32" - - if "Completed" in line: - cs.change_to(cs.CONNECTED) - return - - if "Initial packet from" in line: - cs.change_to(cs.CONNECTING) - return - - - -class MissingSocketError(Exception): - pass - - -class ConnectionRefusedError(Exception): - pass - -class UDSTelnet(telnetlib.Telnet): - - def open(self, host, port=23, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - """Connect to a host. If port is 'unix', it - will open a connection over unix docmain sockets. - - The optional second argument is the port number, which - defaults to the standard telnet port (23). - - Don't try to reopen an already connected instance. - """ - self.eof = 0 - self.host = host - self.port = port - self.timeout = timeout - - if self.port == "unix": - # unix sockets spoken - if not os.path.exists(self.host): - raise MissingSocketError - self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - try: - self.sock.connect(self.host) - except socket.error: - raise ConnectionRefusedError - else: - self.sock = socket.create_connection((host, port), timeout) -- cgit v1.2.3