示例#1
0
def get_profile_config(oauth, api_base_uri,
                       profile_id):  # type: (OAuth2Session, str, str) -> str
    """
    Return a profile configuration

    args:
        oauth (requests_oauthlib.OAuth2Session): oauth2 object
        api_base_uri (str): the instance base URI
        profile_id (str):
    """
    logger.info("Retrieving profile config from {}".format(api_base_uri))
    try:
        response = oauth.get(
            api_base_uri + '/profile_config?profile_id={}'.format(profile_id))
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException("can't create profile, error code {}".format(
            response.status_code))
    # note: this is a bit ambiguous, in case there is an error, the result is json, otherwise clear text.
    try:
        json = response.json()['profile_config']
    except Exception:
        # probably valid response
        return response.text
    else:
        if not json['ok']:
            raise EduvpnException(json['error'])
        else:
            raise EduvpnException(
                "Server error! No profile config returned but no error also.")
def two_factor_enroll_yubi(oauth, api_base_uri, yubi_key_otp):
    # type : (OAuth2Session, str, str) -> None
    try:
        response = oauth.post(api_base_uri + '/two_factor_enroll_yubi', data={'yubi_key_otp': yubi_key_otp})
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException("can't retrieve user info, error code {}".format(response.status_code))
    data = response.json()['two_factor_enroll_yubi']
    if not data['ok']:
        raise EduvpnException(data['error'])
示例#3
0
def create_keypair(
        oauth, api_base_uri):  # type: (OAuth2Session, str) -> Tuple[str, str]
    """
    Create remote keypair and return results

    args:
        oauth (requests_oauthlib.OAuth2Session): oauth2 object
        api_base_uri (str): the instance base URI
    returns:
        tuple(str, str): certificate and key
    """
    logger.info(
        "Creating and retrieving key pair from {}".format(api_base_uri))
    try:
        response = oauth.post(api_base_uri + '/create_keypair',
                              data={'display_name': 'eduVPN for Linux'})
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException("can't create keypair, error code {}".format(
            response.status_code))
    keypair = response.json()['create_keypair']['data']
    cert = keypair['certificate']
    key = keypair['private_key']
    return cert, key
示例#4
0
def user_messages(oauth, api_base_uri):  # type: (OAuth2Session, str) -> Any
    """
    These are messages specific to the user. It can contain a message about the user being blocked, or other personal
    messages from the VPN administrator.

    args:
        oauth (requests_oauthlib.OAuth2Session): oauth2 object
        api_base_uri (str): the instance base URI
    returns:
        list: a list of dicts with date_time, message, type keys
    """
    logger.info("Retrieving user messages from {}".format(api_base_uri))
    try:
        response = oauth.get(api_base_uri + '/user_messages')
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException(
            "can't fetch user messages, error code {}".format(
                response.status_code))
    messages = response.json()['user_messages']
    _ = messages['ok']
    data = messages['data']
    for d in data:
        yield dateutil.parser.parse(d['date_time']), d['type'], d['message']
示例#5
0
def create_config(oauth, api_base_uri, display_name,
                  profile_id):  # type: (OAuth2Session, str, str, str) -> str
    """
    Create a configuration for a given profile.

    args:
        oauth (requests_oauthlib.OAuth2Session): oauth2 object
        api_base_uri (str): the instance base URI
        display_name (str):
        profile_id (str):
    """
    logger.info("Creating config with name '{}' and profile '{}' at {}".format(
        display_name, profile_id, api_base_uri))
    try:
        response = oauth.post(api_base_uri + '/create_config',
                              data={
                                  'display_name': display_name,
                                  'profile_id': profile_id
                              })
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException("can't create config, error code {}".format(
            response.status_code))
    return response.json()
def disconnect_provider(uuid):
    """
    Disconnect the network manager configuration by its UUID

    args:
        uuid (str): the unique ID of the configuration
    """
    logger.info("Disconnecting profile with uuid {} using NetworkManager".format(uuid))
    if not have_dbus():
        raise EduvpnException("No DBus daemon running")

    conns = [i for i in NetworkManager.NetworkManager.ActiveConnections if i.Uuid == uuid]
    if len(conns) == 0:
        raise EduvpnException("no active connection found with uuid {}".format(uuid))
    for conn in conns:
        NetworkManager.NetworkManager.DeactivateConnection(conn)
def connect_provider(uuid):
    """
    Enable the network manager configuration by its UUID

    args:
        uuid (str): the unique ID of the configuration
    """
    logger.info("connecting profile with uuid {} using NetworkManager".format(uuid))
    if not have_dbus():
        raise EduvpnException("No DBus daemon running")

    try:
        connection = NetworkManager.Settings.GetConnectionByUuid(uuid)
        return NetworkManager.NetworkManager.ActivateConnection(connection, "/", "/")
    except DBusException as e:
        raise EduvpnException(e)
示例#8
0
def user_info(oauth, api_base_uri):  # type: (OAuth2Session, str) -> dict
    """
    returns the user information

    args:
        oauth (requests_oauthlib.OAuth2Session): oauth2 object
        api_base_uri (str): the instance base URI
    """
    logger.info("Retrieving user info from {}".format(api_base_uri))
    try:
        response = oauth.get(api_base_uri + '/user_info')
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException("can't retrieve user info, error code {}".format(
            response.status_code))
    data = response.json()['user_info']['data']
    assert ("is_disabled" in data)
    assert ("two_factor_enrolled" in data)
    if data["two_factor_enrolled"]:
        assert ("two_factor_enrolled_with" in data)
    assert ("user_id" in data)
    return data
示例#9
0
def _background(oauth, meta, builder, dialog, lets_connect):
    # type: (str, Metadata, Gtk.builder, Any, bool) -> None
    try:
        profiles = list_profiles(oauth, meta.api_base_uri)
        logger.info("There are {} profiles on {}".format(
            len(profiles), meta.api_base_uri))
        if len(profiles) > 1:
            GLib.idle_add(lambda: dialog.hide())
            GLib.idle_add(
                lambda: _select_profile_step(builder=builder,
                                             profiles=profiles,
                                             meta=meta,
                                             oauth=oauth,
                                             lets_connect=lets_connect))
        elif len(profiles) == 1:
            _parse_choice(builder,
                          meta,
                          oauth,
                          profiles[0],
                          lets_connect=lets_connect)
        else:
            raise EduvpnException(
                "Either there are no VPN profiles defined, or this account does not have the "
                "required permissions to create a new VPN configurations for any of the "
                "available profiles.")

    except Exception as e:
        error = str(e)
        GLib.idle_add(
            lambda: error_helper(dialog, "Can't fetch profile list", error))
        GLib.idle_add(lambda: dialog.hide())
        raise
示例#10
0
def detect_distro(release_file='/etc/os-release'):
    # type: (str) -> Tuple[str, str]
    params = {}
    if not os.access(release_file, os.R_OK):
        raise EduvpnException("Can't detect distribution version, '/etc/os-release' doesn't exist.")

    with open(release_file, 'r') as f:
        for line in f.readlines():
            splitted = line.strip().split('=')
            if len(splitted) == 2:
                key, value = splitted
                params[key] = value.strip('""')

    if 'ID' not in params and 'VERSION_ID' not in params:
        raise EduvpnException("Can't detect distribution version, '/etc/os-release' doesn't "
                              "contain ID and VERSION_ID fields")

    return params['ID'], params['VERSION_ID']
def two_factor_enroll_totp(oauth, api_base_uri, secret, key):
    # type : (OAuth2Session, str, secret, str) -> None
    prefix = '/two_factor_enroll_totp'
    url = api_base_uri + prefix
    logger.info("2fa totp enroling on {} with secret={} and key={}".format(url, secret, key))
    try:
        response = oauth.post(url, data={'totp_secret': secret, 'totp_key': key})
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        logger.error(str(response.content))
        raise EduvpnException("can't enable 2fa otp, error code {} response {}".format(response.status_code,
                                                                                       response.content))
    data = response.json()['two_factor_enroll_totp']
    if not data['ok']:
        raise EduvpnException(data['error'])
def check_certificate(oauth, api_base_uri, common_name):
    # type : (OAuth2Session, str, common_name) -> dict
    prefix = '/check_certificate'
    url = api_base_uri + prefix
    logger.info("checking client certificate on {} with common_name={}".format(url, common_name))
    try:
        response = oauth.get("{}?common_name={}".format(url, common_name))
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        logger.error(str(response.content))
        raise EduvpnException("can't check client certificate, error code {} response {}".format(response.status_code,
                                                                                                 response.content))
    parsed = response.json()['check_certificate']
    if not parsed['ok']:
        raise EduvpnException(parsed['error'])
    return parsed['data']
示例#13
0
 def write(self):
     if not self.uuid:
         raise EduvpnException('uuid field not set')
     fields = [f for f in dir(self) if not f.startswith('_') and not callable(getattr(self, f))]
     d = {field: getattr(self, field) for field in fields}
     p = path.join(providers_path, self.uuid + '.json')
     logger.info("storing metadata in {}".format(p))
     serialized = json.dumps(d)
     mkdir_p(providers_path)
     with open(p, 'w') as f:
         f.write(serialized)
示例#14
0
def _cert_check(meta, oauth, builder, info):
    common_name = common_name_from_cert(meta.cert.encode('ascii'))
    cert_valid = check_certificate(oauth, meta.api_base_uri, common_name)

    if not cert_valid['is_valid']:
        logger.warning('client certificate not valid, reason: {}'.format(
            cert_valid['reason']))
        if cert_valid['reason'] in ('certificate_missing',
                                    'certificate_not_yet_valid',
                                    'certificate_expired'):
            logger.info('Going to try to fetch new keypair')
            cert, key = create_keypair(oauth, meta.api_base_uri)
            update_keys_provider(meta.uuid, cert, key)
        elif cert_valid['reason'] == 'user_disabled':
            raise EduvpnException('Your account has been disabled.')
        else:
            raise EduvpnException(
                'Your client certificate is invalid ({})'.format(
                    cert_valid['reason']))

    _fetch_updated_config(oauth, meta, builder, info)
示例#15
0
def ovpn_to_nm(config, meta, display_name, username=None):  # type: (dict, Metadata, str, Optional[str]) -> object
    """Generate a NetworkManager style config dict from a parsed ovpn config dict."""
    logger.info("generating config for {} ({})".format(display_name, meta.uuid))
    settings = {'connection': {'id': display_name,
                               'type': 'vpn',
                               'uuid': meta.uuid},
                'ipv4': {'method': 'auto'},
                'ipv6': {'method': 'auto'},
                'vpn': {'data': {'auth': config.get('auth', 'SHA256'),
                                 'cipher': config.get('cipher', 'AES-256-CBC'),
                                 'connection-type': config.get('connection-type', 'tls'),
                                 'dev': 'tun',
                                 'remote': ",".join(":".join(r) for r in config['remote']),
                                 'remote-cert-tls': 'server',
                                 # 'tls-cipher' is not supported on older nm (like ubuntu 16.04)
                                 # 'tls-cipher': config.get('tls-cipher', 'TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384')
                                 },
                        'service-type': 'org.freedesktop.NetworkManager.openvpn'}
                }

    # issue #138, not supported by older network-manager-openvpn
    # if 'server-poll-timeout' in config:
    #     settings['vpn']['data']['connect-timeout'] = config['server-poll-timeout']

    if 'comp-lzo' in config:
        settings['vpn']['data']['comp-lzo'] = config['comp-lzo'] or 'adaptive'

    # 2 factor auth enabled
    if 'auth-user-pass' in config:
        if not username:
            raise EduvpnException("You need to enroll for 2FA in the user portal "
                                  "first before being able to connect to this profile.")
        logger.info("looks like 2 factor authentication is enabled, enabling this in NM config")
        settings['vpn']['data']['cert-pass-flags'] = '0'
        settings['vpn']['data']['connection-type'] = 'password-tls'
        settings['vpn']['data']['password-flags'] = '2'
        settings['vpn']['data']['username'] = username

    if 'ca' in config:
        ca_path = write_cert(config.get('ca'), 'ca', meta.uuid)
        settings['vpn']['data']['ca'] = ca_path

    if 'tls-auth' in config:
        settings['vpn']['data']['ta'] = write_cert(config.get('tls-auth'), 'ta', meta.uuid)
        settings['vpn']['data']['ta-dir'] = config.get('key-direction', '1')
    elif 'tls-crypt' in config:
        settings['vpn']['data']['tls-crypt'] = write_cert(config.get('tls-crypt'), 'tc', meta.uuid)
    else:
        logging.info("'tls-crypt' and 'tls-auth' not found in configuration returned by server")

    return settings
示例#16
0
def _background(oauth, meta, builder, dialog):
    try:
        profiles = list_profiles(oauth, meta.api_base_uri)
        logger.info("There are {} profiles on {}".format(len(profiles), meta.api_base_uri))
        if len(profiles) > 1:
            GLib.idle_add(lambda: dialog.hide())
            GLib.idle_add(lambda: select_profile_step(builder=builder, profiles=profiles, meta=meta, oauth=oauth))
        elif len(profiles) == 1:
            meta.profile_display_name, meta.profile_id, meta.two_factor = profiles[0]
            two_auth_step(builder=builder, oauth=oauth, meta=meta)
        else:
            raise EduvpnException("Either there are no VPN profiles defined, or this account does not have the "
                                  "required permissions to create a new VPN configurations for any of the "
                                  "available profiles.")

    except Exception as e:
        GLib.idle_add(lambda: error_helper(dialog, "Can't fetch profile list", str(e)))
        GLib.idle_add(lambda: dialog.hide())
        raise
示例#17
0
def delete_provider(uuid):
    """
    Delete the network manager configuration by its UUID

    args:
        uuid (str): the unique ID of the configuration
    """
    metadata = os.path.join(providers_path, uuid + '.json')
    logger.info("deleting metadata file {}".format(metadata))
    try:
        os.remove(metadata)
    except Exception as e:
        logger.error("can't remove ovpn file: {}".format(str(e)))

    if not have_dbus():
        return

    logger.info("deleting profile with uuid {} using NetworkManager".format(uuid))
    all_connections = NetworkManager.Settings.ListConnections()
    conns = [c for c in all_connections if c.GetSettings()['connection']['uuid'] == uuid]
    if len(conns) != 1:
        raise EduvpnException("{} connections matching uid {}".format(len(conns), uuid))

    conn = conns[0]
    logger.info("removing certificates for {}".format(uuid))
    for f in ['ca', 'cert', 'key', 'ta']:
        if f not in conn.GetSettings()['vpn']['data']:
            logger.error("key {} not in config for {}".format(f, uuid))
            continue
        path = conn.GetSettings()['vpn']['data'][f]
        logger.info("removing certificate {}".format(path))
        try:
            os.remove(path)
        except (IOError, OSError) as e:
            logger.error("can't remove certificate {}: {}".format(path, e))

    try:
        conn.Delete()
    except Exception as e:
        logger.error("can't remove networkmanager connection: {}".format(str(e)))
        raise
def system_messages(oauth, api_base_uri):
    """
    Return all system messages

    args:
        oauth (requests_oauthlib.OAuth2Session): oauth2 object
        api_base_uri (str): the instance base URI
    """
    logger.info("Retrieving system messages from {}".format(api_base_uri))
    try:
        response = oauth.get(api_base_uri + '/system_messages')
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException("can't fetch system messages, error code {}".format(response.status_code))
    messages = response.json()['system_messages']
    _ = messages['ok']
    data = messages['data']
    for d in data:
        yield dateutil.parser.parse(d['date_time']), d['type'], d['message']
示例#19
0
def list_profiles(oauth, api_base_uri):  # type (OAuth2Session, str) -> dict
    """
    List profiles on instance

    args:
        oauth (requests_oauthlib.OAuth2Session): oauth2 object
        api_base_uri (str): the instance base URI

    returns:
        list: of available profiles on the instance (display_name, profile_id, two_factor)
    """
    logger.info("Retrieving profile list from {}".format(api_base_uri))
    try:
        response = oauth.get(api_base_uri + '/profile_list')
    except InvalidGrantError as e:
        raise EduvpnAuthException(str(e))
    if response.status_code == 401:
        raise EduvpnAuthException("request returned error 401")
    elif response.status_code != 200:
        raise EduvpnException("can't list profiles, error code {}".format(
            response.status_code))
    data = response.json()['profile_list']['data']
    profiles = []
    for profile in data:
        display_name = translate_display_name(profile["display_name"])
        profile_id = profile["profile_id"]
        two_factor = profile["two_factor"]
        if two_factor:
            if "two_factor_method" in profile:
                two_factor_method = profile["two_factor_method"]
            else:
                two_factor_method = ["yubi", "totp"]
        else:
            two_factor_method = []
        # we load this into a GtkListModel which doesnt support lists, so we concat them with ,
        profiles.append((display_name, profile_id, two_factor,
                         ",".join(two_factor_method)))
    return profiles