From 23502b72f8cd8a9ec2fd28387f7788aeef54c2d1 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 2 Aug 2012 02:21:45 +0900 Subject: create config file if not exist. also locate openvpn binary when building eip configparser defaults. implement half of feature #356 --- src/leap/eip/config.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/leap/eip/config.py (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py new file mode 100644 index 00000000..d8ffeb28 --- /dev/null +++ b/src/leap/eip/config.py @@ -0,0 +1,72 @@ +import ConfigParser +import os + +from leap.util.fileutil import which, mkdir_p + + +def get_sensible_defaults(): + """ + gathers a dict of sensible defaults, + platform sensitive, + to be used to initialize the config parser + """ + defaults = dict() + defaults['openvpn_binary'] = which('openvpn') + return defaults + + +def get_config(config_file=None): + """ + temporary method for getting configs, + mainly for early stage development process. + in the future we will get preferences + from the storage api + """ + # TODO + # - refactor out common things and get + # them to util/ or baseapp/ + + defaults = get_sensible_defaults() + config = ConfigParser.ConfigParser(defaults) + + if not config_file: + fpath = os.path.expanduser('~/.config/leap/eip.cfg') + if not os.path.isfile(fpath): + dpath, cfile = os.path.split(fpath) + if not os.path.isdir(dpath): + mkdir_p(dpath) + with open(fpath, 'wb') as configfile: + config.write(configfile) + config_file = open(fpath) + + #TODO + # - get a more sensible path for win/mac + # - convert config_file to list; + # look in places like /etc/leap/eip.cfg + # for global settings. + # - raise warnings/error if bad options. + + try: + config.readfp(config_file) + except: + # XXX no file exists? + raise + return config + + +# XXX wrapper around config? to get default values +def get_with_defaults(config, section, option): + # XXX REMOVE ME + if config.has_option(section, option): + return config.get(section, option) + else: + # XXX lookup in defaults dict??? + pass + + +def get_vpn_stdout_mockup(): + # XXX REMOVE ME + command = "python" + args = ["-u", "-c", "from eip_client import fakeclient;\ +fakeclient.write_output()"] + return command, args -- cgit v1.2.3 From b9c9b5536f9d1648a196e741cdf4570f64c3fb11 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 07:32:47 +0900 Subject: build default invocation command + options if not found in config file. fix #182 and #356 --- src/leap/eip/config.py | 144 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 126 insertions(+), 18 deletions(-) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index d8ffeb28..3fca329c 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,17 +1,132 @@ import ConfigParser +import grp import os +import platform from leap.util.fileutil import which, mkdir_p +def build_ovpn_options(): + """ + build a list of options + to be passed in the + openvpn invocation + @rtype: list + @rparam: options + """ + # XXX review which of the + # options we don't need. + + # TODO pass also the config file, + # since we will need to take some + # things from there if present. + + # get user/group name + # also from config. + user = os.getlogin() + gid = os.getgroups()[-1] + group = grp.getgrgid(gid).gr_name + + opts = [] + opts.append('--persist-tun') + + # set user and group + opts.append('--user') + opts.append('%s' % user) + opts.append('--group') + opts.append('%s' % group) + + opts.append('--management-client-user') + opts.append('%s' % user) + opts.append('--management-signal') + + # set default options for management + # interface. unix sockets or telnet interface for win. + # XXX take them from the config object. + + ourplatform = platform.system() + if ourplatform in ("Linux", "Mac"): + opts.append('--management') + opts.append('/tmp/.eip.sock') + opts.append('unix') + if ourplatform == "Windows": + opts.append('--management') + opts.append('localhost') + # XXX which is a good choice? + opts.append('7777') + + # remaining config options, in a file + # NOTE: we will build this file from + # the service definition file. + ovpncnf = os.path.expanduser( + '~/.config/leap/openvpn.conf') + opts.append('--config') + opts.append(ovpncnf) + + return opts + + +def build_ovpn_command(config): + """ + build a string with the + complete openvpn invocation + + @param config: config object + @type config: ConfigParser instance + + @rtype [string, [list of strings]] + @rparam: a list containing the command string + and a list of options. + """ + command = [] + use_pkexec = False + ovpn = None + + if config.has_option('openvpn', 'openvpn_binary'): + ovpn = config.get('openvpn', 'openvpn_binary') + if not ovpn and config.has_option('DEFAULT', 'openvpn_binary'): + ovpn = config.get('DEFAULT', 'openvpn_binary') + + if config.has_option('openvpn', 'use_pkexec'): + use_pkexec = config.get('openvpn', 'use_pkexec') + + if use_pkexec: + command.append('pkexec') + if ovpn: + command.append(ovpn) + + for opt in build_ovpn_options(): + command.append(opt) + + # XXX check len and raise proper error + + return [command[0], command[1:]] + + def get_sensible_defaults(): """ gathers a dict of sensible defaults, platform sensitive, to be used to initialize the config parser + @rtype: dict + @rparam: default options. """ + + # this way we're passing a simple dict + # that will initialize the configparser + # and will get written to "DEFAULTS" section, + # which is fine for now. + # if we want to write to a particular section + # we can better pass a tuple of triples + # (('section1', 'foo', '23'),) + # and config.set them + defaults = dict() defaults['openvpn_binary'] = which('openvpn') + defaults['autostart'] = 'true' + + # TODO + # - management. return defaults @@ -21,6 +136,9 @@ def get_config(config_file=None): mainly for early stage development process. in the future we will get preferences from the storage api + + @rtype: ConfigParser instance + @rparam: a config object """ # TODO # - refactor out common things and get @@ -30,7 +148,8 @@ def get_config(config_file=None): config = ConfigParser.ConfigParser(defaults) if not config_file: - fpath = os.path.expanduser('~/.config/leap/eip.cfg') + fpath = os.path.expanduser( + '~/.config/leap/eip.cfg') if not os.path.isfile(fpath): dpath, cfile = os.path.split(fpath) if not os.path.isdir(dpath): @@ -46,27 +165,16 @@ def get_config(config_file=None): # for global settings. # - raise warnings/error if bad options. - try: - config.readfp(config_file) - except: - # XXX no file exists? - raise - return config - + # at this point, the file should exist. + # errors would have been raised above. + config.readfp(config_file) -# XXX wrapper around config? to get default values -def get_with_defaults(config, section, option): - # XXX REMOVE ME - if config.has_option(section, option): - return config.get(section, option) - else: - # XXX lookup in defaults dict??? - pass + return config def get_vpn_stdout_mockup(): # XXX REMOVE ME command = "python" - args = ["-u", "-c", "from eip_client import fakeclient;\ -fakeclient.write_output()"] + args = ["-u", "-c", ("from eip_client import fakeclient;" + "fakeclient.write_output()")] return command, args -- cgit v1.2.3 From 81613b2ef70e5d73b7c34eb4b78ee63189b45ab6 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 09:42:14 +0900 Subject: pkexec check --- src/leap/eip/config.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 3fca329c..c632ba40 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -4,6 +4,11 @@ import os import platform from leap.util.fileutil import which, mkdir_p +from leap.baseapp.permcheck import is_pkexec_in_system + + +class EIPNoPkexecAvailable(Exception): + pass def build_ovpn_options(): @@ -79,19 +84,35 @@ def build_ovpn_command(config): and a list of options. """ command = [] - use_pkexec = False + use_pkexec = True ovpn = None - if config.has_option('openvpn', 'openvpn_binary'): - ovpn = config.get('openvpn', 'openvpn_binary') - if not ovpn and config.has_option('DEFAULT', 'openvpn_binary'): - ovpn = config.get('DEFAULT', 'openvpn_binary') - if config.has_option('openvpn', 'use_pkexec'): use_pkexec = config.get('openvpn', 'use_pkexec') + if platform.system() == "Linux" and use_pkexec: + + # XXX check for both pkexec (done) + # AND a suitable authentication + # agent running. + + if not is_pkexec_in_system(): + raise EIPNoPkexecAvailable + + #TBD -- + #if not is_auth_agent_running() + # raise EIPNoPolkitAuthAgentAvailable - if use_pkexec: command.append('pkexec') + + if config.has_option('openvpn', + 'openvpn_binary'): + ovpn = config.get('openvpn', + 'openvpn_binary') + if not ovpn and config.has_option('DEFAULT', + 'openvpn_binary'): + ovpn = config.get('DEFAULT', + 'openvpn_binary') + if ovpn: command.append(ovpn) -- cgit v1.2.3 From 5c34052ef9261a47947e3e03616fe34b099b9fa4 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 10:18:50 +0900 Subject: stub for daemon mode; disabled by now until #383 is fixed --- src/leap/eip/config.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index c632ba40..4577837a 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -11,7 +11,7 @@ class EIPNoPkexecAvailable(Exception): pass -def build_ovpn_options(): +def build_ovpn_options(daemon=False): """ build a list of options to be passed in the @@ -68,10 +68,16 @@ def build_ovpn_options(): opts.append('--config') opts.append(ovpncnf) + # we cannot run in daemon mode + # with the current subp setting. + # see: https://leap.se/code/issues/383 + #if daemon is True: + # opts.append('--daemon') + return opts -def build_ovpn_command(config): +def build_ovpn_command(config, debug=False): """ build a string with the complete openvpn invocation @@ -116,7 +122,9 @@ def build_ovpn_command(config): if ovpn: command.append(ovpn) - for opt in build_ovpn_options(): + daemon_mode = not debug + + for opt in build_ovpn_options(daemon=daemon_mode): command.append(opt) # XXX check len and raise proper error @@ -191,11 +199,3 @@ def get_config(config_file=None): config.readfp(config_file) return config - - -def get_vpn_stdout_mockup(): - # XXX REMOVE ME - command = "python" - args = ["-u", "-c", ("from eip_client import fakeclient;" - "fakeclient.write_output()")] - return command, args -- cgit v1.2.3 From 0bb8cca027ab32a54f6792ab1b1368e2f1845368 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 10:46:22 +0900 Subject: check also for a suitable polkit-authentication-agent running fix #382. --- src/leap/eip/config.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 4577837a..9583720e 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -4,13 +4,18 @@ import os import platform from leap.util.fileutil import which, mkdir_p -from leap.baseapp.permcheck import is_pkexec_in_system +from leap.baseapp.permcheck import (is_pkexec_in_system, + is_auth_agent_running) class EIPNoPkexecAvailable(Exception): pass +class EIPNoPolkitAuthAgentAvailable(Exception): + pass + + def build_ovpn_options(daemon=False): """ build a list of options @@ -34,6 +39,7 @@ def build_ovpn_options(daemon=False): opts = [] opts.append('--persist-tun') + opts.append('--persist-key') # set user and group opts.append('--user') @@ -104,9 +110,8 @@ def build_ovpn_command(config, debug=False): if not is_pkexec_in_system(): raise EIPNoPkexecAvailable - #TBD -- - #if not is_auth_agent_running() - # raise EIPNoPolkitAuthAgentAvailable + if not is_auth_agent_running(): + raise EIPNoPolkitAuthAgentAvailable command.append('pkexec') -- cgit v1.2.3 From a6416bd5e4dc57390ba0748878d229098aeca42e Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 Aug 2012 11:17:04 +0900 Subject: added log info for polkit checks --- src/leap/eip/config.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 9583720e..f0cf1d86 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -1,5 +1,6 @@ import ConfigParser import grp +import logging import os import platform @@ -7,6 +8,8 @@ from leap.util.fileutil import which, mkdir_p from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) +logger = logging.getLogger(name=__name__) + class EIPNoPkexecAvailable(Exception): pass @@ -106,11 +109,18 @@ def build_ovpn_command(config, debug=False): # XXX check for both pkexec (done) # AND a suitable authentication # agent running. + logger.info('use_pkexec set to True') if not is_pkexec_in_system(): + logger.error('no pkexec in system') raise EIPNoPkexecAvailable if not is_auth_agent_running(): + logger.warning( + "no polkit auth agent found. " + "pkexec will use its own text " + "based authentication agent. " + "that's probably a bad idea") raise EIPNoPolkitAuthAgentAvailable command.append('pkexec') -- cgit v1.2.3 From 36b0dfacca794e9cb899b5dde2dae3b8bbc6cc43 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 7 Aug 2012 04:14:06 +0900 Subject: build default provider openvpn config. preparation for completion of #356, #355, #354, #182 if no default openvpn config is present, we build one with a preset template and the remote_ip of the eip service as the only input. right now we're taking it from the eip.cfg file. --- src/leap/eip/config.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 9 deletions(-) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index f0cf1d86..9af6f57a 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -9,6 +9,7 @@ from leap.baseapp.permcheck import (is_pkexec_in_system, is_auth_agent_running) logger = logging.getLogger(name=__name__) +logger.setLevel('DEBUG') class EIPNoPkexecAvailable(Exception): @@ -19,6 +20,129 @@ class EIPNoPolkitAuthAgentAvailable(Exception): pass +OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard +remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} + +client +dev tun +persist-tun +persist-key +proto udp +tls-client +remote-cert-tls server + +cert {LEAP_EIP_KEYS} +key {LEAP_EIP_KEYS} +ca {LEAP_EIP_KEYS} +""" + + +def get_config_dir(): + """ + get the base dir for all leap config + @rparam: config path + @rtype: string + """ + # TODO + # check for $XDG_CONFIG_HOME var? + # get a more sensible path for win/mac + # kclair: opinion? ^^ + return os.path.expanduser( + os.path.join('~', + '.config', + 'leap')) + + +def get_config_file(filename, folder=None): + """ + concatenates the given filename + with leap config dir. + @param filename: name of the file + @type filename: string + @rparam: full path to config file + """ + path = [] + path.append(get_config_dir()) + if folder is not None: + path.append(folder) + path.append(filename) + return os.path.join(*path) + + +def get_default_provider_path(): + default_subpath = os.path.join("providers", + "default") + default_provider_path = get_config_file( + '', + folder=default_subpath) + return default_provider_path + + +def check_or_create_default_vpnconf(config): + """ + checks that a vpn config file + exists for a default provider, + or creates one if it does not. + ATM REQURES A [provider] section in + eip.cfg with _at least_ a remote_ip value + """ + default_provider_path = get_default_provider_path() + + if not os.path.isdir(default_provider_path): + mkdir_p(default_provider_path) + + conf_file = get_config_file( + 'openvpn.conf', + folder=default_provider_path) + + if os.path.isfile(conf_file): + return + else: + logger.debug( + 'missing default openvpn config\n' + 'creating one...') + + # We're getting provider from eip.cfg + # by now. Get it from a list of gateways + # instead. + + remote_ip = config.get('provider', + 'remote_ip') + + # XXX check that IT LOOKS LIKE AN IP!!! + if config.has_option('provider', 'remote_port'): + remote_port = config.get('provider', + 'remote_port') + else: + remote_port = 1194 + + default_subpath = os.path.join("providers", + "default") + default_provider_path = get_config_file( + '', + folder=default_subpath) + + if not os.path.isdir(default_provider_path): + mkdir_p(default_provider_path) + + conf_file = get_config_file( + 'openvpn.conf', + folder=default_provider_path) + + # XXX keys have to be manually placed by now + keys_file = get_config_file( + 'openvpn.keys', + folder=default_provider_path) + + ovpn_config = OPENVPN_CONFIG_TEMPLATE.format( + VPN_REMOTE_HOST=remote_ip, + VPN_REMOTE_PORT=remote_port, + LEAP_EIP_KEYS=keys_file) + + with open(conf_file, 'wb') as f: + f.write(ovpn_config) + + def build_ovpn_options(daemon=False): """ build a list of options @@ -41,8 +165,10 @@ def build_ovpn_options(daemon=False): group = grp.getgrgid(gid).gr_name opts = [] - opts.append('--persist-tun') - opts.append('--persist-key') + + #moved to config files + #opts.append('--persist-tun') + #opts.append('--persist-key') # set user and group opts.append('--user') @@ -69,19 +195,25 @@ def build_ovpn_options(daemon=False): # XXX which is a good choice? opts.append('7777') - # remaining config options, in a file + # remaining config options will go in a file + # NOTE: we will build this file from # the service definition file. - ovpncnf = os.path.expanduser( - '~/.config/leap/openvpn.conf') + # XXX override from --with-openvpn-config + opts.append('--config') + + default_provider_path = get_default_provider_path() + ovpncnf = get_config_file( + 'openvpn.conf', + folder=default_provider_path) opts.append(ovpncnf) # we cannot run in daemon mode # with the current subp setting. # see: https://leap.se/code/issues/383 #if daemon is True: - # opts.append('--daemon') + #opts.append('--daemon') return opts @@ -192,8 +324,7 @@ def get_config(config_file=None): config = ConfigParser.ConfigParser(defaults) if not config_file: - fpath = os.path.expanduser( - '~/.config/leap/eip.cfg') + fpath = get_config_file('eip.cfg') if not os.path.isfile(fpath): dpath, cfile = os.path.split(fpath) if not os.path.isdir(dpath): @@ -203,7 +334,6 @@ def get_config(config_file=None): config_file = open(fpath) #TODO - # - get a more sensible path for win/mac # - convert config_file to list; # look in places like /etc/leap/eip.cfg # for global settings. @@ -211,6 +341,7 @@ def get_config(config_file=None): # at this point, the file should exist. # errors would have been raised above. + config.readfp(config_file) return config -- cgit v1.2.3 From 530e10214a6f018909714b288d997df13ab4f9df Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 8 Aug 2012 06:53:10 +0900 Subject: check for bad permissions on vpn key files --- src/leap/eip/config.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 9af6f57a..91c3953b 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -4,13 +4,17 @@ import logging import os import platform -from leap.util.fileutil import which, mkdir_p +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) logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') +# XXX move exceptions to +# from leap.eip import exceptions as eip_exceptions + class EIPNoPkexecAvailable(Exception): pass @@ -20,6 +24,14 @@ class EIPNoPolkitAuthAgentAvailable(Exception): pass +class EIPInitNoKeyFileError(Exception): + pass + + +class EIPInitBadKeyFilePermError(Exception): + pass + + OPENVPN_CONFIG_TEMPLATE = """#Autogenerated by eip-client wizard remote {VPN_REMOTE_HOST} {VPN_REMOTE_PORT} @@ -345,3 +357,45 @@ def get_config(config_file=None): config.readfp(config_file) return config + + +def check_vpn_keys(config): + """ + performs an existance and permission check + over the openvpn keys file. + Currently we're expecting a single file + per provider, containing the CA cert, + the provider key, and our client certificate + """ + + keyopt = ('provider', 'keyfile') + + # XXX at some point, + # should separate between CA, provider cert + # and our certificate. + # make changes in the default provider template + # accordingly. + + # get vpn keys + if config.has_option(*keyopt): + keyfile = config.get(*keyopt) + else: + keyfile = get_config_file( + 'openvpn.keys', + folder=get_default_provider_path()) + logger.debug('keyfile = %s', keyfile) + + # if no keys, raise error. + # should be catched by the ui and signal user. + + if not os.path.isfile(keyfile): + logger.error('key file %s not found. aborting.', + keyfile) + raise EIPInitNoKeyFileError + + # check proper permission on keys + # bad perms? try to fix them + try: + check_and_fix_urw_only(keyfile) + except OSError: + raise EIPInitBadKeyFilePermError -- cgit v1.2.3 From c217bd1f1456cf10ceabf698ea6f4dd8f636f454 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 8 Aug 2012 07:22:36 +0900 Subject: check for validity of the remote_ip entry (is ip?) --- src/leap/eip/config.py | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) (limited to 'src/leap/eip/config.py') diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index 91c3953b..6118c9de 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -3,6 +3,7 @@ import grp import logging import os import platform +import socket from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only) @@ -24,6 +25,14 @@ class EIPNoPolkitAuthAgentAvailable(Exception): pass +class EIPInitNoProviderError(Exception): + pass + + +class EIPInitBadProviderError(Exception): + pass + + class EIPInitNoKeyFileError(Exception): pass @@ -90,6 +99,14 @@ def get_default_provider_path(): return default_provider_path +def validate_ip(ip_str): + """ + raises exception if the ip_str is + not a valid representation of an ip + """ + socket.inet_aton(ip_str) + + def check_or_create_default_vpnconf(config): """ checks that a vpn config file @@ -118,10 +135,18 @@ def check_or_create_default_vpnconf(config): # by now. Get it from a list of gateways # instead. - remote_ip = config.get('provider', - 'remote_ip') + try: + remote_ip = config.get('provider', + 'remote_ip') + validate_ip(remote_ip) + + except ConfigParser.NoOptionError: + raise EIPInitNoProviderError + + except socket.error: + # this does not look like an ip, dave + raise EIPInitBadProviderError - # XXX check that IT LOOKS LIKE AN IP!!! if config.has_option('provider', 'remote_port'): remote_port = config.get('provider', 'remote_port') -- cgit v1.2.3