def _extract_bin_path(self, app):
     '''Get bin-path for app'''
     rel = self.bin_paths[app]
     fn = os.path.join(self.unpack_dir, rel)
     if not os.path.exists(fn):
         error("Could not find '%s'" % rel)
     return fn
예제 #2
0
    def __init__(self, fn, overrides=None):
        peer_hooks = dict()
        my_hook = 'push-helper'
        peer_hooks[my_hook] = dict()
        peer_hooks[my_hook]['allowed'] = ['apparmor']
        peer_hooks[my_hook]['required'] = ['apparmor']

        ClickReview.__init__(self, fn, "push_helper", peer_hooks=peer_hooks,
                             overrides=overrides)

        if not self.is_click and not self.is_snap1:
            return

        self.required_keys = ['exec']
        self.optional_keys = ['app_id']

        self.push_helper_files = dict()  # click-show-files and tests
        self.push_helper = dict()

        if self.manifest is None:
            return

        for app in self.manifest['hooks']:
            if 'push-helper' not in self.manifest['hooks'][app]:
                # msg("Skipped missing push-helper hook for '%s'" % app)
                continue
            if not isinstance(self.manifest['hooks'][app]['push-helper'], str):
                error("manifest malformed: hooks/%s/push-helper is not str" %
                      app)
            (full_fn, jd) = self._extract_push_helper(app)
            self.push_helper_files[app] = full_fn
            self.push_helper[app] = jd
    def _extract_url_dispatcher(self, app):
        '''Get url dispatcher json'''
        u = self.manifest['hooks'][app]['urls']
        if not u:
            error("'urls' definition is empty for '%s'" % app)
        fn = os.path.join(self.unpack_dir, u)
        if not os.path.isfile(fn):
            error("'%s' is not a file" % fn)

        bn = os.path.basename(fn)
        if not os.path.exists(fn):
            error("Could not find '%s'" % bn)

        fh = open_file_read(fn)
        contents = ""
        for line in fh.readlines():
            contents += line
        fh.close()

        try:
            jd = json.loads(contents)
        except Exception as e:
            error("url-dispatcher json unparseable: %s (%s):\n%s" % (bn,
                  str(e), contents))

        if not isinstance(jd, list):
            error("url-dispatcher json is malformed: %s:\n%s" % (bn, contents))

        return (fn, jd)
    def __init__(self, fn, overrides=None):
        peer_hooks = dict()
        my_hook = 'content-hub'
        peer_hooks[my_hook] = dict()
        peer_hooks[my_hook]['allowed'] = ClickReview.app_allowed_peer_hooks
        peer_hooks[my_hook]['required'] = []

        ClickReview.__init__(self,
                             fn,
                             "content_hub",
                             peer_hooks=peer_hooks,
                             overrides=overrides)
        if not self.is_click and not self.is_snap1:
            return

        self.valid_keys = ['destination', 'share', 'source']

        self.content_hub_files = dict()  # click-show-files and tests
        self.content_hub = dict()

        if self.manifest is None:
            return

        for app in self.manifest['hooks']:
            if 'content-hub' not in self.manifest['hooks'][app]:
                # msg("Skipped missing content-hub hook for '%s'" % app)
                continue
            if not isinstance(self.manifest['hooks'][app]['content-hub'], str):
                error("manifest malformed: hooks/%s/urls is not str" % app)
            (full_fn, jd) = self._extract_content_hub(app)
            self.content_hub_files[app] = full_fn
            self.content_hub[app] = jd
    def __init__(self, fn, overrides=None):
        ClickReview.__init__(self, fn, "framework", overrides=overrides)

        self.frameworks_file = dict()
        self.frameworks = dict()

        if not self.is_snap1:
            return

        if self.manifest is not None:
            for app in self.manifest['hooks']:
                if 'framework' not in self.manifest['hooks'][app]:
                    # msg("Skipped missing framework hook for '%s'" % app)
                    continue
                if not isinstance(self.manifest['hooks'][app]['framework'],
                                  str):
                    error("manifest malformed: hooks/%s/framework is not str" %
                          app)
                (full_fn, data) = self._extract_framework(app)
                self.frameworks_file[app] = full_fn
                self.frameworks[app] = data

        self.framework_policy_dirs = ['apparmor', 'seccomp']
        self.framework_policy_subdirs = ['templates', 'policygroups']
        (self.framework_policy, self.framework_policy_unknown) = \
            self._extract_framework_policy()
예제 #6
0
    def __init__(self, fn, overrides=None):
        peer_hooks = dict()
        my_hook = 'scope'
        peer_hooks[my_hook] = dict()
        peer_hooks[my_hook]['allowed'] = ClickReview.scope_allowed_peer_hooks
        peer_hooks[my_hook]['required'] = ['apparmor']

        ClickReview.__init__(self, fn, "scope", peer_hooks=peer_hooks,
                             overrides=overrides)

        if not self.is_click and not self.is_snap1:
            return

        self.scopes = dict()

        if self.manifest is None:
            return

        for app in self.manifest['hooks']:
            if 'scope' not in self.manifest['hooks'][app]:
                # msg("Skipped missing scope hook for '%s'" % app)
                continue
            if not isinstance(self.manifest['hooks'][app]['scope'], str):
                error("manifest malformed: hooks/%s/scope is not str" % app)
            self.scopes[app] = self._extract_scopes(app)
    def __init__(self, fn, overrides=None):
        peer_hooks = dict()
        my_hook = 'urls'
        peer_hooks[my_hook] = dict()
        peer_hooks[my_hook]['allowed'] = ClickReview.app_allowed_peer_hooks
        peer_hooks[my_hook]['required'] = []

        ClickReview.__init__(self, fn, "url_dispatcher", peer_hooks=peer_hooks,
                             overrides=overrides)

        if not self.is_click and not self.is_snap1:
            return

        self.required_keys = ['protocol']
        self.optional_keys = ['domain-suffix']

        self.url_dispatcher_files = dict()  # click-show-files and tests
        self.url_dispatcher = dict()

        if self.manifest is None:
            return

        for app in self.manifest['hooks']:
            if 'urls' not in self.manifest['hooks'][app]:
                # msg("Skipped missing urls hook for '%s'" % app)
                continue
            if not isinstance(self.manifest['hooks'][app]['urls'], str):
                error("manifest malformed: hooks/%s/urls is not str" % app)
            (full_fn, jd) = self._extract_url_dispatcher(app)
            self.url_dispatcher_files[app] = full_fn
            self.url_dispatcher[app] = jd
    def __init__(self, fn, overrides=None):
        peer_hooks = dict()
        my_hook = 'desktop'
        peer_hooks[my_hook] = dict()
        peer_hooks[my_hook]['allowed'] = ClickReview.app_allowed_peer_hooks
        peer_hooks[my_hook]['required'] = ["apparmor"]

        ClickReview.__init__(self,
                             fn,
                             "desktop",
                             peer_hooks=peer_hooks,
                             overrides=overrides)
        if not self.is_click and not self.is_snap1:
            return

        self.desktop_files = dict()  # click-show-files and a couple tests
        self.desktop_entries = dict()
        self.desktop_hook_entries = 0

        if self.manifest is None:
            return

        for app in self.manifest['hooks']:
            if 'desktop' not in self.manifest['hooks'][app]:
                # msg("Skipped missing desktop hook for '%s'" % app)
                continue
            if not isinstance(self.manifest['hooks'][app]['desktop'], str):
                error("manifest malformed: hooks/%s/desktop is not str" % app)
            self.desktop_hook_entries += 1
            (de, full_fn) = self._extract_desktop_entry(app)
            self.desktop_entries[app] = de
            self.desktop_files[app] = full_fn

        self.required_keys = [
            'Name',
            'Type',
            'Icon',
            'Exec',
            'X-Ubuntu-Touch',
        ]
        self.expected_execs = [
            'qmlscene',
            'webbrowser-app',
            'webapp-container',
            'ubuntu-html5-app-launcher',
        ]
        self.deprecated_execs = [
            'cordova-ubuntu-2.8',
        ]
        # TODO: the desktop hook will actually handle this correctly
        self.blacklisted_keys = ['Path']
예제 #9
0
    def _extract_scopes(self, app):
        '''Get scopes'''
        d = dict()

        s = self.manifest['hooks'][app]['scope']
        fn = os.path.join(self.unpack_dir, s)

        bn = os.path.basename(fn)
        if not os.path.exists(fn):
            error("Could not find '%s'" % bn)
        elif not os.path.isdir(fn):
            error("'%s' is not a directory" % bn)

        ini_fn = os.path.join(fn, "%s_%s.ini" % (self.manifest['name'], app))
        ini_fn_bn = os.path.relpath(ini_fn, self.unpack_dir)
        if not os.path.exists(ini_fn):
            error("Could not find scope INI file '%s'" % ini_fn_bn)
        try:
            d["scope_config"] = configparser.ConfigParser()
            d["scope_config"].read_file(codecs.open(ini_fn, "r", "utf8"))
        except Exception as e:
            error("scope config unparseable: %s (%s)" % (ini_fn_bn, str(e)))

        d["dir"] = fn
        d["dir_rel"] = bn
        d["ini_file"] = ini_fn
        d["ini_file_rel"] = ini_fn_bn

        return d
예제 #10
0
    def _extract_webapp_manifests(self):
        '''Extract webapp manifest file'''
        files = sorted(
            glob.glob("%s/unity-webapps-*/manifest.json" % self.unpack_dir))

        manifests = dict()
        for fn in files:
            key = os.path.relpath(fn, self.unpack_dir)
            try:
                manifests[key] = json.load(open_file_read(fn))
            except Exception:
                manifests[key] = None
                error("Could not parse '%s'" % fn, do_exit=False)

        return manifests
    def _extract_framework(self, app):
        '''Get framework for app'''
        rel = self.manifest['hooks'][app]['framework']
        fn = os.path.join(self.unpack_dir, rel)
        if not os.path.exists(fn):
            error("Could not find '%s'" % rel)

        data = dict()
        fh = open_file_read(fn)
        for line in fh.readlines():
            tmp = line.split(':')
            if len(tmp) != 2:
                continue
            data[tmp[0].strip()] = tmp[1].strip()
        fh.close()

        return (fn, data)
    def __init__(self, fn, overrides=None):
        # bin-path is ignored by snappy install so don't bother with peerhooks
        ClickReview.__init__(self, fn, "bin-path", overrides=overrides)

        self.bin_paths_files = dict()
        self.bin_paths = dict()

        if not self.is_snap1:
            return

        # snappy yaml currently only allows specifying:
        # - exec (optional)
        # - description (optional)
        self.required_keys = []
        self.optional_keys = ['description', 'exec'] + self.snappy_exe_security

        if self.is_snap1 and 'binaries' in self.pkg_yaml:
            if len(self.pkg_yaml['binaries']) == 0:
                error("package.yaml malformed: 'binaries' is empty")
            for binary in self.pkg_yaml['binaries']:
                if 'name' not in binary:
                    error("package.yaml malformed: required 'name' not found "
                          "for entry in %s" % self.pkg_yaml['binaries'])
                elif not isinstance(binary['name'], str):
                    error("package.yaml malformed: required 'name' is not str"
                          "for entry in %s" % self.pkg_yaml['binaries'])

                app = os.path.basename(binary['name'])
                if 'exec' in binary:
                    rel = binary['exec']
                else:
                    rel = binary['name']
                self.bin_paths[app] = rel
                self.bin_paths_files[app] = self._extract_bin_path(app)
예제 #13
0
    def _extract_push_helper(self, app):
        '''Get push-helper hook content'''
        c = self.manifest['hooks'][app]['push-helper']
        fn = os.path.join(self.unpack_dir, c)

        bn = os.path.basename(fn)
        if not os.path.exists(fn):
            error("Could not find '%s'" % bn)

        fh = open_file_read(fn)
        contents = ""
        for line in fh.readlines():
                contents += line
        fh.close()

        try:
            jd = json.loads(contents)
        except Exception as e:
            error("push-helper json unparseable: %s (%s):\n%s" % (bn,
                  str(e), contents))

        if not isinstance(jd, dict):
            error("push-helper json is malformed: %s:\n%s" % (bn, contents))

        return (fn, jd)
예제 #14
0
    def _extract_desktop_entry(self, app):
        '''Get DesktopEntry for desktop file and verify it'''
        d = self.manifest['hooks'][app]['desktop']
        fn = os.path.join(self.unpack_dir, d)

        bn = os.path.basename(fn)
        if not os.path.exists(fn):
            error("Could not find '%s'" % bn)

        fh = open_file_read(fn)
        contents = ""
        for line in fh.readlines():
            contents += line
        fh.close()

        try:
            de = DesktopEntry(fn)
        except xdgParsingError as e:
            error("desktop file unparseable: %s (%s):\n%s" %
                  (bn, str(e), contents))
        try:
            de.parse(fn)
        except Exception as e:
            error("desktop file unparseable: %s (%s):\n%s" %
                  (bn, str(e), contents))
        return de, fn
예제 #15
0
    def _extract_account(self, app, account_type):
        '''Extract accounts'''
        if self.manifest is None:
            return

        a = self.manifest['hooks'][app][account_type]
        fn = os.path.join(self.unpack_dir, a)

        bn = os.path.basename(fn)
        if not os.path.exists(fn):
            error("Could not find '%s'" % bn)

        # qml-plugin points to a QML file, so just set that we have the
        # the hook present for now
        if account_type == "account-qml-plugin":
            return (fn, True)
        elif account_type == "accounts":
            fh = open_file_read(fn)
            contents = ""
            for line in fh.readlines():
                contents += line
            fh.close()

            try:
                jd = json.loads(contents)
            except Exception as e:
                error("accounts json unparseable: %s (%s):\n%s" %
                      (bn, str(e), contents))

            if not isinstance(jd, dict):
                error("accounts json is malformed: %s:\n%s" % (bn, contents))

            return (fn, jd)
        else:
            try:
                tree = etree.parse(fn)
                xml = tree.getroot()
            except Exception as e:
                error("accounts xml unparseable: %s (%s)" % (bn, str(e)))
            return (fn, xml)
예제 #16
0
    def __init__(self, fn, overrides=None):
        # systemd isn't implemented as a hook any more so don't setup peerhooks
        ClickReview.__init__(self, fn, "snappy-systemd", overrides=overrides)

        self.systemd_files = dict()  # click-show-files and tests
        self.systemd = dict()

        if not self.is_snap1:
            return

        # snappy-systemd currently only allows specifying:
        # - start (required)
        # - description (required)
        # - stop
        # - poststop
        # - stop-timeout
        # - caps (checked in in cr_security.py)
        # - security-template (checked in in cr_security.py)
        # - security-override (checked in in cr_security.py)
        # - security-policy (checked in in cr_security.py)
        self.required_keys = ['start', 'description']
        self.optional_keys = [
            'stop', 'poststop', 'stop-timeout', 'bus-name', 'listen-stream',
            'socket', 'socket-user', 'socket-group', 'ports'
        ] + self.snappy_exe_security

        if self.is_snap1 and 'services' in self.pkg_yaml:
            if len(self.pkg_yaml['services']) == 0:
                error("package.yaml malformed: 'services' is empty")
            for service in self.pkg_yaml['services']:
                if 'name' not in service:
                    error("package.yaml malformed: required 'name' not found "
                          "for entry in %s" % self.pkg_yaml['services'])
                elif not isinstance(service['name'], str):
                    error("package.yaml malformed: required 'name' is not str"
                          "for entry in %s" % self.pkg_yaml['services'])

                app = service['name']
                self.systemd[app] = copy.deepcopy(service)
                del self.systemd[app]['name']
예제 #17
0
    def __init__(self, fn, overrides=None):
        peer_hooks = dict()
        peer_hooks['account-application'] = dict()
        peer_hooks['account-application']['allowed'] = \
            ClickReview.app_allowed_peer_hooks + \
            ClickReview.scope_allowed_peer_hooks
        peer_hooks['account-application']['required'] = ['apparmor']

        peer_hooks['account-service'] = dict()
        peer_hooks['account-service']['required'] = [
            'account-application', 'apparmor'
        ]
        peer_hooks['account-service']['allowed'] = \
            ClickReview.app_allowed_peer_hooks + \
            ClickReview.scope_allowed_peer_hooks

        peer_hooks['accounts'] = dict()
        peer_hooks['accounts']['allowed'] = \
            [h for h in (ClickReview.app_allowed_peer_hooks +
                         ClickReview.scope_allowed_peer_hooks)
             if not h.startswith('account-')]
        peer_hooks['accounts']['required'] = ['apparmor']

        peer_hooks['account-provider'] = dict()
        peer_hooks['account-provider']['required'] = [
            'account-qml-plugin', 'apparmor'
        ]
        peer_hooks['account-provider']['allowed'] = \
            peer_hooks['account-provider']['required']

        peer_hooks['account-qml-plugin'] = dict()
        peer_hooks['account-qml-plugin']['required'] = [
            'account-provider', 'apparmor'
        ]
        peer_hooks['account-qml-plugin']['allowed'] = \
            peer_hooks['account-qml-plugin']['required']

        ClickReview.__init__(
            self,
            fn,
            "online_accounts",
            peer_hooks=peer_hooks,
            overrides=overrides,
            peer_hooks_link=
            "https://wiki.ubuntu.com/SecurityTeam/Specifications/OnlineAccountsConfinement"
        )
        if not self.is_click and not self.is_snap1:
            return

        self.accounts_files = dict()
        self.accounts = dict()

        self.account_hooks = [
            'accounts', 'account-application', 'account-provider',
            'account-qml-plugin', 'account-service'
        ]

        if self.manifest is None:
            return

        for app in self.manifest['hooks']:
            for h in self.account_hooks:
                if h not in self.manifest['hooks'][app]:
                    # msg("Skipped missing %s hook for '%s'" % (h, app))
                    continue
                if not isinstance(self.manifest['hooks'][app][h], str):
                    error("manifest malformed: hooks/%s/%s is not a str" %
                          (app, h))

                (full_fn, parsed) = self._extract_account(app, h)

                if app not in self.accounts_files:
                    self.accounts_files[app] = dict()
                self.accounts_files[app][h] = full_fn

                if app not in self.accounts:
                    self.accounts[app] = dict()
                self.accounts[app][h] = parsed

        self.required_keys = dict()
        self.allowed_keys = dict()
        self.required_keys["service"] = [
            ('provider', str),
        ]
        self.allowed_keys["service"] = [
            ('auth', dict),
            ('name', str),
            ('description', str),
        ]
        self.required_keys["plugin"] = [
            ('name', str),
            ('icon', str),
            ('qml', str),
        ]
        self.allowed_keys["plugin"] = [
            ('auth', dict),
        ]
        self.provider_re = re.compile('^[a-zA-Z0-9_.-]+$')