Пример #1
0
    def _read_rules(self):
        """Read in rules that were added by ufw."""
        rfns = [self.files["rules"]]
        if self.use_ipv6():
            rfns.append(self.files["rules6"])

        for f in rfns:
            try:
                orig = ufw.util.open_file_read(f)
            except Exception:
                err_msg = _("Couldn't open '%s' for reading") % (f)
                raise UFWError(err_msg)

            pat_tuple = re.compile(r"^### tuple ###\s*")
            for line in orig:
                if pat_tuple.match(line):
                    tupl = pat_tuple.sub("", line)
                    tmp = re.split(r"\s+", tupl.strip())
                    if len(tmp) < 6 or len(tmp) > 9:
                        warn_msg = _("Skipping malformed tuple (bad length): %s") % (tupl)
                        warn(warn_msg)
                        continue
                    else:
                        # set direction to "in" to support upgrades
                        # from old format, which only had 6 or 8 fields
                        type = "in"
                        interface = ""
                        if len(tmp) == 7 or len(tmp) == 9:
                            if "_" in tmp[-1]:
                                (type, interface) = tmp[-1].split("_")
                            else:
                                type = tmp[-1]
                        try:
                            if len(tmp) < 8:
                                rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], type)
                            else:
                                rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], type)
                                # Removed leading [sd]app_ and unescape spaces
                                pat_space = re.compile("%20")
                                if tmp[6] != "-":
                                    rule.dapp = pat_space.sub(" ", tmp[6])
                                if tmp[7] != "-":
                                    rule.sapp = pat_space.sub(" ", tmp[7])
                            if interface != "":
                                rule.set_interface(type, interface)

                        except UFWError:
                            warn_msg = _("Skipping malformed tuple: %s") % (tupl)
                            warn(warn_msg)
                            continue
                        if f == self.files["rules6"]:
                            rule.set_v6(True)
                            self.rules6.append(rule)
                        else:
                            rule.set_v6(False)
                            self.rules.append(rule)

            orig.close()
Пример #2
0
    def reset(self):
        '''Reset the firewall'''
        res = ""
        # First make sure we have all the original files
        allfiles = []
        for i in self.files:
            if not self.files[i].endswith('.rules'):
                continue
            allfiles.append(self.files[i])
            fn = os.path.join(ufw.common.share_dir, "iptables", \
                              os.path.basename(self.files[i]))
            if not os.path.isfile(fn):
                err_msg = _("Could not find '%s'. Aborting") % (fn)
                raise UFWError(err_msg)

        ext = time.strftime("%Y%m%d_%H%M%S")

        # This implementation will intentionally traceback if someone tries to
        # do something to take advantage of the race conditions here.

        # Don't do anything if the files already exist
        for i in allfiles:
            fn = "%s.%s" % (i, ext)
            if os.path.exists(fn):
                err_msg = _("'%s' already exists. Aborting") % (fn)
                raise UFWError(err_msg)

        # Move the old to the new
        for i in allfiles:
            fn = "%s.%s" % (i, ext)
            res += _("Backing up '%(old)s' to '%(new)s'\n") % (\
                     {'old': os.path.basename(i), 'new': fn})
            os.rename(i, fn)

        # Copy files into place
        for i in allfiles:
            old = "%s.%s" % (i, ext)
            shutil.copy(os.path.join(ufw.common.share_dir, "iptables", \
                                     os.path.basename(i)), \
                        os.path.dirname(i))
            shutil.copymode(old, i)

            try:
                statinfo = os.stat(i)
                mode = statinfo[stat.ST_MODE]
            except Exception:
                warn_msg = _("Couldn't stat '%s'") % (i)
                warn(warn_msg)
                continue

            if mode & stat.S_IWOTH:
                res += _("WARN: '%s' is world writable") % (i)
            elif mode & stat.S_IROTH:
                res += _("WARN: '%s' is world readable") % (i)

        return res
Пример #3
0
    def reset(self):
        '''Reset the firewall'''
        res = ""
        # First make sure we have all the original files
        allfiles = []
        for i in self.files:
            if not self.files[i].endswith('.rules'):
                continue
            allfiles.append(self.files[i])
            fn = os.path.join(ufw.common.share_dir, "iptables", \
                              os.path.basename(self.files[i]))
            if not os.path.isfile(fn):
                err_msg = _("Could not find '%s'. Aborting") % (fn)
                raise UFWError(err_msg)

        ext = time.strftime("%Y%m%d_%H%M%S")

	# This implementation will intentionally traceback if someone tries to
        # do something to take advantage of the race conditions here.

        # Don't do anything if the files already exist
        for i in allfiles:
            fn = "%s.%s" % (i, ext)
            if os.path.exists(fn):
                err_msg = _("'%s' already exists. Aborting") % (fn)
                raise UFWError(err_msg)

        # Move the old to the new
        for i in allfiles:
            fn = "%s.%s" % (i, ext)
            res += _("Backing up '%(old)s' to '%(new)s'\n") % (\
                     {'old': os.path.basename(i), 'new': fn})
            os.rename(i, fn)

        # Copy files into place
        for i in allfiles:
            old = "%s.%s" % (i, ext)
            shutil.copy(os.path.join(ufw.common.share_dir, "iptables", \
                                     os.path.basename(i)), \
                        os.path.dirname(i))
            shutil.copymode(old, i)

            try:
                statinfo = os.stat(i)
                mode = statinfo[stat.ST_MODE]
            except Exception:
                warn_msg = _("Couldn't stat '%s'") % (i)
                warn(warn_msg)
                continue

            if mode & stat.S_IWOTH:
                res += _("WARN: '%s' is world writable") % (i)
            elif mode & stat.S_IROTH:
                res += _("WARN: '%s' is world readable") % (i)

        return res
Пример #4
0
    def initcaps(self):
        '''Initialize the capabilities database. This needs to be called
           before accessing the database.'''

        # Only initialize if not initialized already
        if self.caps is not None:
            return

        self.caps = {}
        self.caps['limit'] = {}

        # Set defaults for dryrun, non-root, etc
        self.caps['limit']['4'] = True
        self.caps['limit']['6'] = False  # historical default for the testsuite

        # Try to get capabilities from the running system if root
        if self.do_checks and os.getuid(
        ) == 0 and not self.dryrun:  # pragma: no coverage
            # v4
            try:
                nf_caps = ufw.util.get_netfilter_capabilities(self.iptables)
            except OSError as e:
                msg = "initcaps\n%s" % e
                if self.is_enabled():
                    error(msg)
                warn(msg)
                return

            if 'recent-set' in nf_caps and 'recent-update' in nf_caps:
                self.caps['limit']['4'] = True
            else:
                self.caps['limit']['4'] = False

            # v6 (skip capabilities check for ipv6 if ipv6 is disabled in ufw
            # because the system may not have ipv6 support (LP: #1039729)
            if self.use_ipv6():
                try:
                    nf_caps = ufw.util.get_netfilter_capabilities(
                        self.ip6tables)
                except OSError as e:
                    error("initcaps\n%s" % e)
                if 'recent-set' in nf_caps and 'recent-update' in nf_caps:
                    self.caps['limit']['6'] = True
                else:
                    self.caps['limit']['6'] = False
Пример #5
0
    def set_rule(self, rule, ip_version):
        '''Updates firewall with rule'''
        res = ""
        err_msg = ""
        tmp = ""
        rules = []

        if rule.dapp == "" and rule.sapp == "":
            rules.append(rule)
        else:
            tmprules = []
            try:
                if rule.remove:
                    if ip_version == "v4":
                        tmprules = self.backend.get_app_rules_from_system(
                            rule, False)
                    elif ip_version == "v6":
                        tmprules = self.backend.get_app_rules_from_system(
                            rule, True)
                    elif ip_version == "both":
                        tmprules = self.backend.get_app_rules_from_system(
                            rule, False)
                        tmprules6 = self.backend.get_app_rules_from_system(
                            rule, True)
                        # Only add rules that are different by more than v6 (we
                        # will handle 'ip_version == both' specially, below).
                        for x in tmprules:
                            for y in tmprules6:
                                prev6 = y.v6
                                y.v6 = False
                                if not x.match(y):
                                    y.v6 = prev6
                                    tmprules.append(y)
                    else:
                        err_msg = _("Invalid IP version '%s'") % (ip_version)
                        raise UFWError(err_msg)

                    # Don't process removal of non-existing application rules
                    if len(tmprules) == 0 and not self.backend.dryrun:
                        tmp = _("Could not delete non-existent rule")
                        if ip_version == "v4":
                            res = tmp
                        elif ip_version == "v6":
                            res = tmp + " (v6)"
                        elif ip_version == "both":
                            res = tmp + "\n" + tmp + " (v6)"
                        return res

                    for tmp in tmprules:
                        r = tmp.dup_rule()
                        r.remove = rule.remove
                        r.set_action(rule.action)
                        r.set_logtype(rule.logtype)
                        rules.append(r)
                else:
                    rules = self.backend.get_app_rules_from_template(rule)
                    # Reverse the order of rules for inserted rules, so they
                    # are inserted in the right order
                    if rule.position > 0:
                        rules.reverse()
            except Exception:
                raise

        count = 0
        set_error = False
        pos_err_msg = _("Invalid position '")
        num_v4 = self.backend.get_rules_count(False)
        num_v6 = self.backend.get_rules_count(True)
        for i, r in enumerate(rules):
            count = i
            if r.position > num_v4 + num_v6:
                pos_err_msg += str(r.position) + "'"
                raise UFWError(pos_err_msg)
            try:
                if self.backend.use_ipv6():
                    if ip_version == "v4":
                        if r.position > num_v4:
                            pos_err_msg += str(r.position) + "'"
                            raise UFWError(pos_err_msg)
                        r.set_v6(False)
                        tmp = self.backend.set_rule(r)
                    elif ip_version == "v6":
                        if r.position > num_v4:
                            r.set_position(r.position - num_v4)
                        elif r.position != 0 and r.position <= num_v4:
                            pos_err_msg += str(r.position) + "'"
                            raise UFWError(pos_err_msg)
                        r.set_v6(True)
                        tmp = self.backend.set_rule(r)
                    elif ip_version == "both":
                        user_pos = r.position  # user specified position
                        r.set_v6(False)
                        if not r.remove and user_pos > num_v4:
                            # The user specified a v6 rule, so try to find a
                            # match in the v4 rules and use its position.
                            p = self.backend.find_other_position( \
                                user_pos - num_v4 + count, True)
                            if p > 0:
                                r.set_position(p)
                            else:
                                # If not found, then add the rule
                                r.set_position(0)
                        tmp = self.backend.set_rule(r)

                        # We need to readjust the position since the number
                        # the number of ipv4 rules increased
                        if not r.remove and user_pos > 0:
                            num_v4 = self.backend.get_rules_count(False)
                            r.set_position(user_pos + 1)

                        r.set_v6(True)
                        if not r.remove and r.position > 0 and \
                           r.position <= num_v4:
                            # The user specified a v4 rule, so try to find a
                            # match in the v6 rules and use its position.
                            p = self.backend.find_other_position(r.position, \
                                                                 False)
                            if p > 0:
                                # Subtract count since the list is reversed
                                r.set_position(p - count)
                            else:
                                # If not found, then add the rule
                                r.set_position(0)
                        if tmp != "":
                            tmp += "\n"

                        # Readjust position to send to set_rule
                        if not r.remove and r.position > num_v4:
                            r.set_position(r.position - num_v4)

                        tmp += self.backend.set_rule(r)
                    else:
                        err_msg = _("Invalid IP version '%s'") % (ip_version)
                        raise UFWError(err_msg)
                else:
                    if ip_version == "v4" or ip_version == "both":
                        r.set_v6(False)
                        tmp = self.backend.set_rule(r)
                    elif ip_version == "v6":
                        err_msg = _("IPv6 support not enabled")
                        raise UFWError(err_msg)
                    else:
                        err_msg = _("Invalid IP version '%s'") % (ip_version)
                        raise UFWError(err_msg)
            except UFWError as e:
                err_msg = e.value
                set_error = True
                break

            if r.updated:
                warn_msg = _("Rule changed after normalization")
                warnings.warn(warn_msg)

        if not set_error:
            # Just return the last result if no error
            res += tmp
        elif len(rules) == 1:
            # If no error, and just one rule, error out
            error(err_msg)  # pragma: no cover
        else:
            # If error and more than one rule, delete the successfully added
            # rules in reverse order
            undo_error = False
            indexes = list(range(count + 1))
            indexes.reverse()
            for j in indexes:
                if count > 0 and rules[j]:
                    backout_rule = rules[j].dup_rule()
                    backout_rule.remove = True
                    try:
                        self.set_rule(backout_rule, ip_version)
                    except Exception:
                        # Don't fail, so we can try to backout more
                        undo_error = True
                        warn_msg = _("Could not back out rule '%s'") % \
                                     r.format_rule()
                        warn(warn_msg)

            err_msg += _("\nError applying application rules.")
            if undo_error:
                err_msg += _(" Some rules could not be unapplied.")
            else:
                err_msg += _(" Attempted rules successfully unapplied.")

            raise UFWError(err_msg)

        return res
Пример #6
0
def get_profiles(profiles_dir):
    '''Get profiles found in profiles database.  Returns dictionary with
       profile name as key and tuples for fields
    '''
    if not os.path.isdir(profiles_dir):
        err_msg = _("Profiles directory does not exist")
        raise UFWError(err_msg)

    max_size = 10 * 1024 * 1024  # 10MB
    profiles = {}

    files = os.listdir(profiles_dir)
    files.sort()

    total_size = 0
    pat = re.compile(r'^\.')
    for f in files:
        abs_path = profiles_dir + "/" + f
        if not os.path.isfile(abs_path):
            continue

        if pat.search(f):
            debug("Skipping '%s': hidden file" % (f))
            continue

        if f.endswith('.dpkg-new') or f.endswith('.dpkg-old') or \
           f.endswith('.dpkg-dist') or f.endswith('.rpmnew') or \
           f.endswith('.rpmsave') or f.endswith('~'):
            debug("Skipping '%s'" % (f))
            continue

	# Try to gracefully handle huge files for the user (no security
        # benefit, just usability)
        size = 0
        try:
            size = os.stat(abs_path)[stat.ST_SIZE]
        except Exception:
            warn_msg = _("Skipping '%s': couldn't stat") % (f)
            warn(warn_msg)
            continue

        if size > max_size:
            warn_msg = _("Skipping '%s': too big") % (f)
            warn(warn_msg)
            continue

        if total_size + size > max_size:
            warn_msg = _("Skipping '%s': too many files read already") % (f)
            warn(warn_msg)
            continue

        total_size += size

        if sys.version_info[0] < 3:
            cdict = ConfigParser.RawConfigParser()
        else:
            cdict = configparser.RawConfigParser()

        try:
            cdict.read(abs_path)
        except Exception:
            warn_msg = _("Skipping '%s': couldn't process") % (f)
            warn(warn_msg)
            continue

        # If multiple occurences of profile name, use the last one
        for p in cdict.sections():
            if len(p) > 64:
                warn_msg = _("Skipping '%s': name too long") % (p)
                warn(warn_msg)
                continue

            if not valid_profile_name(p):
                warn_msg = _("Skipping '%s': invalid name") % (p)
                warn(warn_msg)
                continue

            try:
                ufw.util.get_services_proto(p)
                warn_msg = _("Skipping '%s': also in /etc/services") % (p)
                warn(warn_msg)
                continue
            except Exception:
                pass

            skip = False
            for key, value in cdict.items(p):
                if len(key) > 64:
                    warn_msg = _("Skipping '%s': field too long") % (p)
                    warn(warn_msg)
                    skip = True
                    break
                if len(value) > 1024:
                    warn_msg = _("Skipping '%(value)s': value too long for " \
                                 "'%(field)s'") % \
                                 ({'value': p, 'field': key})
                    warn(warn_msg)
                    skip = True
                    break
            if skip:
                continue

            if p in profiles:
                warn_msg = _("Duplicate profile '%s', using last found") % (p)
                warn(warn_msg)

            pdict = {}
            for key, value in cdict.items(p):
                #debug("add '%s' = '%s' to '%s'" % (key, value, p))
                pdict[key] = value

            profiles[p] = pdict

    return profiles
Пример #7
0
    def _do_checks(self):
        '''Perform basic security checks:
        is setuid or setgid (for non-Linux systems)
        checks that script is owned by root
        checks that every component in absolute path are owned by root
        warn if script is group writable
        warn if part of script path is group writable

        Doing this at the beginning causes a race condition with later
        operations that don't do these checks.  However, if the user running
        this script is root, then need to be root to exploit the race
        condition (and you are hosed anyway...)
        '''

        if not self.do_checks:
            err_msg = _("Checks disabled")
            warn(err_msg)
            return True

        # Not needed on Linux, but who knows the places we will go...
        if os.getuid() != os.geteuid():
            err_msg = _("ERROR: this script should not be SUID")
            raise UFWError(err_msg)
        if os.getgid() != os.getegid():
            err_msg = _("ERROR: this script should not be SGID")
            raise UFWError(err_msg)
        uid = os.getuid()

        if uid != 0:
            err_msg = _("You need to be root to run this script")
            raise UFWError(err_msg)

        # Use these so we only warn once
        warned_world_write = {}
        warned_group_write = {}
        warned_owner = {}

        profiles = []
        if not os.path.isdir(self.files['apps']):
            warn_msg = _("'%s' does not exist") % (self.files['apps'])
            warn(warn_msg)
        else:
            pat = re.compile(r'^\.')
            for profile in os.listdir(self.files['apps']):
                if not pat.search(profile):
                    profiles.append(os.path.join(self.files['apps'], profile))

        for path in self.files.values() + [ os.path.abspath(sys.argv[0]) ] + \
                profiles:
            while True:
                debug("Checking " + path)
                if path == self.files['apps'] and \
                           not os.path.isdir(self.files['apps']):
                    break

                try:
                    statinfo = os.stat(path)
                    mode = statinfo[ST_MODE]
                except OSError, e:
                    err_msg = _("Couldn't stat '%s'") % (path)
                    raise UFWError(err_msg)
                except Exception:
                    raise

                if statinfo.st_uid != 0 and not warned_owner.has_key(path):
                    warn_msg = _("uid is %(uid)s but '%(path)s' is owned by " \
                                 "%(st_uid)s") % ({'uid': str(uid), \
                                               'path': path, \
                                               'st_uid': str(statinfo.st_uid)})
                    warn(warn_msg)
                    warned_owner[path] = True
                if mode & S_IWOTH and not warned_world_write.has_key(path):
                    warn_msg = _("%s is world writable!") % (path)
                    warn(warn_msg)
                    warned_world_write[path] = True
                if mode & S_IWGRP and not warned_group_write.has_key(path):
                    warn_msg = _("%s is group writable!") % (path)
                    warn(warn_msg)
                    warned_group_write[path] = True

                if path == "/":
                    break

                path = os.path.dirname(path)
                if not path:
                    raise OSError(errno.ENOENT, "Could not find '%s'" % (path))
Пример #8
0
def get_profiles(profiles_dir):
    '''Get profiles found in profiles database.  Returns dictionary with
       profile name as key and tuples for fields
    '''
    if not os.path.isdir(profiles_dir):
        err_msg = _("Profiles directory does not exist")
        raise UFWError(err_msg)

    max_size = 10 * 1024 * 1024  # 10MB
    profiles = {}

    files = os.listdir(profiles_dir)
    files.sort()

    total_size = 0
    pat = re.compile(r'^\.')
    for f in files:
        abs_path = profiles_dir + "/" + f
        if not os.path.isfile(abs_path):
            continue

        if pat.search(f):
            debug("Skipping '%s': hidden file" % (f))
            continue

        if f.endswith('.dpkg-new') or f.endswith('.dpkg-old') or \
           f.endswith('.dpkg-dist') or f.endswith('.rpmnew') or \
           f.endswith('.rpmsave') or f.endswith('~'):
            debug("Skipping '%s'" % (f))
            continue

# Try to gracefully handle huge files for the user (no security
# benefit, just usability)
        size = 0
        try:
            size = os.stat(abs_path)[stat.ST_SIZE]
        except Exception:
            warn_msg = _("Skipping '%s': couldn't stat") % (f)
            warn(warn_msg)
            continue

        if size > max_size:
            warn_msg = _("Skipping '%s': too big") % (f)
            warn(warn_msg)
            continue

        if total_size + size > max_size:
            warn_msg = _("Skipping '%s': too many files read already") % (f)
            warn(warn_msg)
            continue

        total_size += size

        if sys.version_info[0] < 3:  # pragma: no cover
            cdict = ConfigParser.RawConfigParser()
        else:  # pragma: no cover
            cdict = configparser.RawConfigParser()

        try:
            cdict.read(abs_path)
        except Exception:
            warn_msg = _("Skipping '%s': couldn't process") % (f)
            warn(warn_msg)
            continue

        # If multiple occurences of profile name, use the last one
        for p in cdict.sections():
            if len(p) > 64:
                warn_msg = _("Skipping '%s': name too long") % (p)
                warn(warn_msg)
                continue

            if not valid_profile_name(p):
                warn_msg = _("Skipping '%s': invalid name") % (p)
                warn(warn_msg)
                continue

            try:
                ufw.util.get_services_proto(p)
                warn_msg = _("Skipping '%s': also in /etc/services") % (p)
                warn(warn_msg)
                continue
            except Exception:
                pass

            skip = False
            for key, value in cdict.items(p):
                if len(key) > 64:
                    warn_msg = _("Skipping '%s': field too long") % (p)
                    warn(warn_msg)
                    skip = True
                    break
                if len(value) > 1024:
                    warn_msg = _("Skipping '%(value)s': value too long for " \
                                 "'%(field)s'") % \
                                 ({'value': p, 'field': key})
                    warn(warn_msg)
                    skip = True
                    break
            if skip:
                continue

            if p in profiles:
                warn_msg = _("Duplicate profile '%s', using last found") % (p)
                warn(warn_msg)

            pdict = {}
            for key, value in cdict.items(p):
                #debug("add '%s' = '%s' to '%s'" % (key, value, p))
                pdict[key] = value

            try:
                verify_profile(p, pdict)
                profiles[p] = pdict
            except UFWError as e:
                warn(e)

    return profiles
    def set_rule(self, rule, ip_version):
        '''Updates firewall with rule'''
        res = ""
        err_msg = ""
        tmp = ""
        rules = []

        if rule.dapp == "" and rule.sapp == "":
            rules.append(rule)
        else:
            tmprules = []
            try:
                if rule.remove:
                    if ip_version == "v4":
                        tmprules = self.backend.get_app_rules_from_system(
                                                                   rule, False)
                    elif ip_version == "v6":
                        tmprules = self.backend.get_app_rules_from_system(
                                                                   rule, True)
                    elif ip_version == "both":
                        tmprules = self.backend.get_app_rules_from_system(
                                                                   rule, False)
                        tmprules6 = self.backend.get_app_rules_from_system(
                                                                   rule, True)
                        # Only add rules that are different by more than v6 (we
                        # will handle 'ip_version == both' specially, below).
                        for x in tmprules:
                            for y in tmprules6:
                                prev6 = y.v6
                                y.v6 = False
                                if not x.match(y):
                                    y.v6 = prev6
                                    tmprules.append(y)
                    else:
                        err_msg = _("Invalid IP version '%s'") % (ip_version)
                        raise UFWError(err_msg)

                    # Don't process removal of non-existing application rules
                    if len(tmprules) == 0 and not self.backend.dryrun:
                        tmp =  _("Could not delete non-existent rule")
                        if ip_version == "v4":
                            res = tmp
                        elif ip_version == "v6":
                            res = tmp + " (v6)"
                        elif ip_version == "both":
                            res = tmp + "\n" + tmp + " (v6)"
                        return res

                    for tmp in tmprules:
                        r = tmp.dup_rule()
                        r.remove = rule.remove
                        r.set_action(rule.action)
                        r.set_logtype(rule.logtype)
                        rules.append(r)
                else:
                    rules = self.backend.get_app_rules_from_template(rule)
                    # Reverse the order of rules for inserted rules, so they
                    # are inserted in the right order
                    if rule.position > 0:
                        rules.reverse()
            except Exception:
                raise

        count = 0
        set_error = False
        pos_err_msg = _("Invalid position '")
        num_v4 = self.backend.get_rules_count(False)
        num_v6 = self.backend.get_rules_count(True)
        for i, r in enumerate(rules):
            count = i
            if r.position > num_v4 + num_v6:
                pos_err_msg += str(r.position) + "'"
                raise UFWError(pos_err_msg)
            try:
                if self.backend.use_ipv6():
                    if ip_version == "v4":
                        if r.position > num_v4:
                            pos_err_msg += str(r.position) + "'"
                            raise UFWError(pos_err_msg)
                        r.set_v6(False)
                        tmp = self.backend.set_rule(r)
                    elif ip_version == "v6":
                        if r.position > num_v4:
                            r.set_position(r.position - num_v4)
                        elif r.position != 0 and r.position <= num_v4:
                            pos_err_msg += str(r.position) + "'"
                            raise UFWError(pos_err_msg)
                        r.set_v6(True)
                        tmp = self.backend.set_rule(r)
                    elif ip_version == "both":
                        user_pos = r.position # user specified position
                        r.set_v6(False)
                        if not r.remove and user_pos > num_v4:
			    # The user specified a v6 rule, so try to find a
			    # match in the v4 rules and use its position.
                            p = self.backend.find_other_position( \
                                user_pos - num_v4 + count, True)
                            if p > 0:
                                r.set_position(p)
                            else:
                                # If not found, then add the rule
                                r.set_position(0)
                        tmp = self.backend.set_rule(r)

                        # We need to readjust the position since the number
                        # the number of ipv4 rules increased
                        if not r.remove and user_pos > 0:
                            num_v4 = self.backend.get_rules_count(False)
                            r.set_position(user_pos + 1)

                        r.set_v6(True)
                        if not r.remove and r.position > 0 and \
                           r.position <= num_v4:
			    # The user specified a v4 rule, so try to find a
			    # match in the v6 rules and use its position.
                            p = self.backend.find_other_position(r.position, \
                                                                 False)
                            if p > 0:
                                # Subtract count since the list is reversed
                                r.set_position(p - count)
                            else:
                                # If not found, then add the rule
                                r.set_position(0)
                        if tmp != "":
                            tmp += "\n"

                        # Readjust position to send to set_rule
                        if not r.remove and r.position > num_v4:
                            r.set_position(r.position - num_v4)

                        tmp += self.backend.set_rule(r)
                    else:
                        err_msg = _("Invalid IP version '%s'") % (ip_version)
                        raise UFWError(err_msg)
                else:
                    if ip_version == "v4" or ip_version == "both":
                        r.set_v6(False)
                        tmp = self.backend.set_rule(r)
                    elif ip_version == "v6":
                        err_msg = _("IPv6 support not enabled")
                        raise UFWError(err_msg)
                    else:
                        err_msg = _("Invalid IP version '%s'") % (ip_version)
                        raise UFWError(err_msg)
            except UFWError as e:
                err_msg = e.value
                set_error = True
                break

            if r.updated:
                warn_msg = _("Rule changed after normalization")
                warnings.warn(warn_msg)

        if not set_error:
            # Just return the last result if no error
            res += tmp
        elif len(rules) == 1:
            # If no error, and just one rule, error out
            error(err_msg) # pragma: no cover
        else:
	    # If error and more than one rule, delete the successfully added
	    # rules in reverse order
            undo_error = False
            indexes = list(range(count+1))
            indexes.reverse()
            for j in indexes:
                if count > 0 and rules[j]:
                    backout_rule = rules[j].dup_rule()
                    backout_rule.remove = True
                    try:
                        self.set_rule(backout_rule, ip_version)
                    except Exception:
                        # Don't fail, so we can try to backout more
                        undo_error = True
                        warn_msg = _("Could not back out rule '%s'") % \
                                     r.format_rule()
                        warn(warn_msg)

            err_msg += _("\nError applying application rules.")
            if undo_error:
                err_msg += _(" Some rules could not be unapplied.")
            else:
                err_msg += _(" Attempted rules successfully unapplied.")

            raise UFWError(err_msg)

        return res
Пример #10
0
	    # rules in reverse order
            undo_error = False
            indexes = range(count+1)
            indexes.reverse()
            for j in indexes:
                if count > 0 and rules[j]:
                    backout_rule = rules[j].dup_rule()
                    backout_rule.remove = True
                    try:
                        self.set_rule(backout_rule, ip_version)
                    except Exception:
                        # Don't fail, so we can try to backout more
                        undo_error = True
                        warn_msg = _("Could not back out rule '%s'") % \
                                     r.format_rule()
                        warn(warn_msg)

            err_msg += _("\nError applying application rules.")
            if undo_error:
                err_msg += _(" Some rules could not be unapplied.")
            else:
                err_msg += _(" Attempted rules successfully unapplied.")

            raise UFWError(err_msg)

        return res

    def delete_rule(self, number, force=False):
        '''Delete rule'''
        try:
            n = int(number)
Пример #11
0
    def _do_checks(self):  # pragma: no coverage
        '''Perform basic security checks:
        is setuid or setgid (for non-Linux systems)
        checks that script is owned by root
        checks that every component in absolute path are owned by root
        warn if script is group writable
        warn if part of script path is group writable

        Doing this at the beginning causes a race condition with later
        operations that don't do these checks.  However, if the user running
        this script is root, then need to be root to exploit the race
        condition (and you are hosed anyway...)
        '''

        if not self.do_checks:
            err_msg = _("Checks disabled")
            warn(err_msg)
            return True

        # Not needed on Linux, but who knows the places we will go...
        if os.getuid() != os.geteuid():
            err_msg = _("ERROR: this script should not be SUID")
            raise UFWError(err_msg)
        if os.getgid() != os.getegid():
            err_msg = _("ERROR: this script should not be SGID")
            raise UFWError(err_msg)

        uid = os.getuid()
        if uid != 0:
            err_msg = _("You need to be root to run this script")
            raise UFWError(err_msg)

        # Use these so we only warn once
        warned_world_write = {}
        warned_group_write = {}
        warned_owner = {}

        profiles = []
        if not os.path.isdir(self.files['apps']):
            warn_msg = _("'%s' does not exist") % (self.files['apps'])
            warn(warn_msg)
        else:
            pat = re.compile(r'^\.')
            for profile in os.listdir(self.files['apps']):
                if not pat.search(profile):
                    profiles.append(os.path.join(self.files['apps'], profile))

        for path in list(self.files.values()) + \
                [ os.path.abspath(sys.argv[0]) ] + \
                profiles:
            if not path.startswith('/'):
                path = "%s/%s" % (os.getcwd(), path)
            while True:
                debug("Checking " + path)
                if path == self.files['apps'] and \
                           not os.path.isdir(self.files['apps']):
                    break

                try:
                    statinfo = os.stat(path)
                    mode = statinfo[stat.ST_MODE]
                except OSError:
                    err_msg = _("Couldn't stat '%s'") % (path)
                    raise UFWError(err_msg)
                except Exception:
                    raise

                # snaps and clicks unpack to this, so handle it
                click_user = '******'
                snap_user = '******'
                is_unpack_user = False
                try:
                    if pwd.getpwuid(statinfo.st_uid)[0] == click_user or \
                            pwd.getpwuid(statinfo.st_uid)[0] == snap_user:
                        is_unpack_user = True
                except KeyError:
                    pass

                if statinfo.st_uid != 0 and not is_unpack_user and \
                        path not in warned_owner:
                    warn_msg = _("uid is %(uid)s but '%(path)s' is owned by " \
                                 "%(st_uid)s") % ({'uid': str(uid), \
                                               'path': path, \
                                               'st_uid': str(statinfo.st_uid)})
                    warn(warn_msg)
                    warned_owner[path] = True
                if mode & stat.S_IWOTH and path not in warned_world_write:
                    warn_msg = _("%s is world writable!") % (path)
                    warn(warn_msg)
                    warned_world_write[path] = True
                if mode & stat.S_IWGRP and path not in warned_group_write and \
                        statinfo.st_gid != 0:
                    warn_msg = _("%s is group writable!") % (path)
                    warn(warn_msg)
                    warned_group_write[path] = True

                if path == "/":
                    break

                last_path = path
                path = os.path.dirname(path)
                if not path:
                    raise OSError(errno.ENOENT, \
                                  "Could not find parent for '%s'" % \
                                  (last_path))

        for f in self.files:
            if f != 'apps' and not os.path.isfile(self.files[f]):
                err_msg = _("'%(f)s' file '%(name)s' does not exist") % \
                           ({'f': f, 'name': self.files[f]})
                raise UFWError(err_msg)
Пример #12
0
	    # rules in reverse order
            undo_error = False
            indexes = range(count+1)
            indexes.reverse()
            for j in indexes:
                if count > 0 and rules[j]:
                    backout_rule = rules[j].dup_rule()
                    backout_rule.remove = True
                    try:
                        self.set_rule(backout_rule, ip_version)
                    except Exception:
                        # Don't fail, so we can try to backout more
                        undo_error = True
                        warn_msg = _("Could not back out rule '%s'") % \
                                     r.format_rule()
                        warn(warn_msg)

            err_msg += _("\nError applying application rules.")
            if undo_error:
                err_msg += _(" Some rules could not be unapplied.")
            else:
                err_msg += _(" Attempted rules successfully unapplied.")

            raise UFWError(err_msg)

        return res

    def delete_rule(self, number, force=False):
        '''Delete rule'''
        try:
            n = int(number)
Пример #13
0
    def _read_rules(self):
        '''Read in rules that were added by ufw'''
        rfns = [self.files['rules']]
        if self.use_ipv6():
            rfns.append(self.files['rules6'])

        for f in rfns:
            try:
                orig = ufw.util.open_file_read(f)
            except Exception:
                err_msg = _("Couldn't open '%s' for reading") % (f)
                raise UFWError(err_msg)

            pat_tuple = re.compile(r'^### tuple ###\s*')
            for line in orig:
                if pat_tuple.match(line):
                    tupl = pat_tuple.sub('', line)
                    tmp = re.split(r'\s+', tupl.strip())
                    if len(tmp) < 6 or len(tmp) > 9:
                        wmsg = _("Skipping malformed tuple (bad length): %s") \
                                 % (tupl)
                        warn(wmsg)
                        continue
                    else:
                        # set direction to "in" to support upgrades
                        # from old format, which only had 6 or 8 fields
                        dtype = "in"
                        interface = ""
                        if len(tmp) == 7 or len(tmp) == 9:
                            if '_' in tmp[-1]:
                                (dtype, interface) = tmp[-1].split('_')
                            else:
                                dtype = tmp[-1]
                        try:
                            if len(tmp) < 8:
                                rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3],
                                               tmp[4], tmp[5], dtype)
                            else:
                                rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3],
                                               tmp[4], tmp[5], dtype)
                                # Removed leading [sd]app_ and unescape spaces
                                pat_space = re.compile('%20')
                                if tmp[6] != "-":
                                    rule.dapp = pat_space.sub(' ', tmp[6])
                                if tmp[7] != "-":
                                    rule.sapp = pat_space.sub(' ', tmp[7])
                            if interface != "":
                                rule.set_interface(dtype, interface)

                        except UFWError:
                            warn_msg = _("Skipping malformed tuple: %s") % \
                                        (tupl)
                            warn(warn_msg)
                            continue
                        if f == self.files['rules6']:
                            rule.set_v6(True)
                            self.rules6.append(rule)
                        else:
                            rule.set_v6(False)
                            self.rules.append(rule)

            orig.close()