From bff8dd5a5c73bc2eddca94c8dab3cdf888b01eb9 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Fri, 4 Sep 2015 15:31:55 +0530 Subject: [PATCH] tor: Fixes for APT transport setup - When Deb822 formatted source files are present, error out and don't show as enabled. - When /etc/apt/sources.list.d/*.sources files are present, error out and don't show as enabled. - Handle files /etc/apt/sources.list.d/*.list. - Workaround Augeas lens not understanding '[options]' in sources.list. --- actions/tor | 30 ++++++++++---- plinth/modules/tor/tor.py | 87 +++++++++++++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 17 deletions(-) diff --git a/actions/tor b/actions/tor index 91815cbb0..96be7d503 100755 --- a/actions/tor +++ b/actions/tor @@ -22,16 +22,15 @@ Configuration helper for the Tor service """ import argparse -import augeas import codecs import os import re import socket from plinth import action_utils +from plinth.modules.tor.tor import get_augeas, get_real_apt_uri_path, \ + iter_apt_uris, APT_TOR_PREFIX -APT_SOURCES_URI_PATH = '/files/etc/apt/sources.list/*/uri' -APT_TOR_PREFIX = 'tor+' TOR_CONFIG = '/etc/tor/torrc' TOR_STATE_FILE = '/var/lib/tor/state' TOR_AUTH_COOKIE = '/var/run/tor/control.authcookie' @@ -204,21 +203,36 @@ def subcommand_get_ports(_): def subcommand_enable_apt_transport_tor(_): """Enable package download over Tor.""" - aug = augeas.Augeas() - for uri_path in aug.match(APT_SOURCES_URI_PATH): + try: + aug = get_augeas() + except Exception: + # If there was an error, don't proceed + print('Error: Unable to understand sources format.') + exit(1) + + for uri_path in iter_apt_uris(aug): + uri_path = get_real_apt_uri_path(aug, uri_path) uri = aug.get(uri_path) - if not uri.startswith(APT_TOR_PREFIX): + if uri.startswith('http://') or uri.startswith('https://'): aug.set(uri_path, APT_TOR_PREFIX + uri) + aug.save() def subcommand_disable_apt_transport_tor(_): """Disable package download over Tor.""" - aug = augeas.Augeas() - for uri_path in aug.match(APT_SOURCES_URI_PATH): + try: + aug = get_augeas() + except Exception: + # Disable what we can, so APT is not unusable. + pass + + for uri_path in iter_apt_uris(aug): + uri_path = get_real_apt_uri_path(aug, uri_path) uri = aug.get(uri_path) if uri.startswith(APT_TOR_PREFIX): aug.set(uri_path, uri[len(APT_TOR_PREFIX):]) + aug.save() diff --git a/plinth/modules/tor/tor.py b/plinth/modules/tor/tor.py index 530328d52..aa5fdfbdb 100644 --- a/plinth/modules/tor/tor.py +++ b/plinth/modules/tor/tor.py @@ -24,13 +24,17 @@ from django import forms from django.contrib import messages from django.template.response import TemplateResponse from gettext import gettext as _ +import glob +import itertools from plinth import actions from plinth import action_utils from plinth import cfg from plinth import package +from plinth.errors import ActionError -APT_SOURCES_URI_PATH = '/files/etc/apt/sources.list/*/uri' +APT_SOURCES_URI_PATHS = ('/files/etc/apt/sources.list/*/uri', + '/files/etc/apt/sources.list.d/*/*/uri') APT_TOR_PREFIX = 'tor+' @@ -91,6 +95,78 @@ def index(request): 'form': form}) +def get_augeas(): + """Return an instance of Augeaus for processing APT configuration.""" + aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + + augeas.Augeas.NO_MODL_AUTOLOAD) + aug.set('/augeas/load/Aptsources/lens', 'Aptsources.lns') + aug.set('/augeas/load/Aptsources/incl[last() + 1]', '/etc/apt/sources.list') + aug.set('/augeas/load/Aptsources/incl[last() + 1]', + '/etc/apt/sources.list.d/*.list') + aug.load() + + # Currently, augeas does not handle Deb822 format, it error out. + if aug.match('/augeas/files/etc/apt/sources.list/error') or \ + aug.match('/augeas/files/etc/apt/sources.list.d//error'): + raise Exception('Error parsing sources list') + + # Starting with Apt 1.1, /etc/apt/sources.list.d/*.sources will + # contain files with Deb822 format. If they are found, error out + # for now. XXX: Provide proper support Deb822 format with a new + # Augeas lens. + if glob.glob('/etc/apt/sources.list.d/*.sources'): + raise Exception('Can not handle Deb822 source files') + + return aug + + +def iter_apt_uris(aug): + """Iterate over all the APT source URIs.""" + return itertools.chain.from_iterable([aug.match(path) + for path in APT_SOURCES_URI_PATHS]) + + +def get_real_apt_uri_path(aug, path): + """Return the actual path which contains APT URL. + + XXX: This is a workaround for Augeas bug parsing Apt source files + with '[options]'. Remove this workaround after Augeas lens is + fixed. + """ + uri = aug.get(path) + if uri[0] == '[': + parent_path = path.rsplit('/', maxsplit=1)[0] + skipped = False + for child_path in aug.match(parent_path + '/*')[1:]: + if skipped: + return child_path + + value = aug.get(child_path) + if value[-1] == ']': + skipped = True + + return path + + +def is_apt_transport_tor_enabled(): + """Return whether APT is set to download packages over Tor.""" + try: + aug = get_augeas() + except Exception: + # If there was an error with parsing or there are Deb822 + # files. + return False + + for uri_path in iter_apt_uris(aug): + uri_path = get_real_apt_uri_path(aug, uri_path) + uri = aug.get(uri_path) + if not uri.startswith(APT_TOR_PREFIX) and \ + (uri.startswith('http://') or uri.startswith('https://')): + return False + + return True + + def get_status(): """Return the current status""" output = actions.superuser_run('tor', ['get-ports']) @@ -119,20 +195,13 @@ def get_status(): hs_hostname = hs_info[0] hs_ports = hs_info[1] - apt_transport_tor_enabled = False - aug = augeas.Augeas() - for uri_path in aug.match(APT_SOURCES_URI_PATH): - if aug.get(uri_path).startswith(APT_TOR_PREFIX): - apt_transport_tor_enabled = True - break - return {'enabled': action_utils.service_is_enabled('tor'), 'is_running': action_utils.service_is_running('tor'), 'ports': ports, 'hs_enabled': hs_enabled, 'hs_hostname': hs_hostname, 'hs_ports': hs_ports, - 'apt_transport_tor_enabled': apt_transport_tor_enabled} + 'apt_transport_tor_enabled': is_apt_transport_tor_enabled()} def _apply_changes(request, old_status, new_status):