예제 #1
0
    def _reload_user_rules(self):
        '''Reload firewall rules file'''
        err_msg = _("problem running")
        if self.dryrun:
            msg("> | iptables-restore")
            if self.use_ipv6():
                msg("> | ip6tables-restore")
        elif self.is_enabled():
            # first flush the user logging chains
            try:
                for c in self.chains['user']:
                    self._chain_cmd(c, ['-F', c])
                    self._chain_cmd(c, ['-Z', c])
            except Exception:
                raise UFWError(err_msg)

            # then restore the system rules
            (rc, out) = cmd_pipe(['cat', self.files['rules']], \
                                 [self.iptables_restore, '-n'])
            if rc != 0:
                raise UFWError(err_msg + " iptables")

            if self.use_ipv6():
                (rc, out) = cmd_pipe(['cat', self.files['rules6']], \
                                     [self.ip6tables_restore, '-n'])
                if rc != 0:
                    raise UFWError(err_msg + " ip6tables")
예제 #2
0
    def _get_defaults(self):
        '''Get all settings from defaults file'''
        self.defaults = {}
        for f in [self.files['defaults'], self.files['conf']]:
            try:
                orig = ufw.util.open_file_read(f)
            except Exception:  # pragma: no coverage
                err_msg = _("Couldn't open '%s' for reading") % (f)
                raise UFWError(err_msg)
            pat = re.compile(r'^\w+="?\w+"?')
            for line in orig:
                if pat.search(line):
                    tmp = re.split(r'=', line.strip())
                    self.defaults[tmp[0].lower()] = tmp[1].lower().strip('"\'')

            orig.close()

        # do some default policy sanity checking
        policies = ['accept', 'drop', 'reject']
        for c in ['input', 'output', 'forward']:
            if 'default_%s_policy' % (c) not in self.defaults:
                err_msg = _("Missing policy for '%s'" % (c))
                raise UFWError(err_msg)
            p = self.defaults['default_%s_policy' % (c)]
            if p not in policies:
                err_msg = _("Invalid policy '%(policy)s' for '%(chain)s'" % \
                            ({'policy': p, 'chain': c}))
                raise UFWError(err_msg)
예제 #3
0
def verify_profile(name, profile):
    '''Make sure profile has everything needed'''
    app_fields = ['title', 'description', 'ports']

    for f in app_fields:
        if not profile.has_key(f):
            err_msg = _("Profile '%(fn)s' missing required field '%(f)s'") % \
                        ({'fn': name, 'f': f})

            raise UFWError(err_msg)
        elif not profile[f]:
            err_msg = _("Profile '%(fn)s' has empty required field '%(f)s'") \
                        % ({'fn': name, 'f': f})
            raise UFWError(err_msg)

    ports = profile['ports'].split('|')
    if len(ports) < 1:
        err_msg = _("No ports found in profile '%s'") % (name)
        return False

    try:
        for p in ports:
            (port, proto) = ufw.util.parse_port_proto(p)
            # quick check if error in profile
            #if not proto:
            #    proto = "any"
            rule = ufw.common.UFWRule("ACCEPT", proto, port)
            debug(rule)
    except Exception, e:
        debug(e)
        err_msg = _("Invalid ports in profile '%s'") % (name)
        raise UFWError(err_msg)
예제 #4
0
    def start_firewall(self):
        '''Start the firewall'''
        err_msg = _("problem running")
        if self.dryrun:
            msg("> " + _("running ufw-init"))
        else:
            (rc, out) = cmd([self.files['init'], 'start'])
            if rc != 0:
                debug(out)
                raise UFWError(err_msg + " ufw-init")

            if not self.defaults.has_key('loglevel') or \
               self.defaults['loglevel'] not in self.loglevels.keys():
                # Add the loglevel if not valid
                try:
                    self.set_loglevel("low")
                except Exception:
                    err_msg = _("Could not set LOGLEVEL")
                    raise UFWError(err_msg)
            else:
                try:
                    self.update_logging(self.defaults['loglevel'])
                except Exception:
                    err_msg = _("Could not load logging rules")
                    raise UFWError(err_msg)
예제 #5
0
def verify_profile(name, profile):
    '''Make sure profile has everything needed'''
    app_fields = ['title', 'description', 'ports']

    for f in app_fields:
        if f not in profile:
            err_msg = _("Profile '%(fn)s' missing required field '%(f)s'") % \
                        ({'fn': name, 'f': f})

            raise UFWError(err_msg)
        elif not profile[f]:
            err_msg = _("Profile '%(fn)s' has empty required field '%(f)s'") \
                        % ({'fn': name, 'f': f})
            raise UFWError(err_msg)

    ports = profile['ports'].split('|')
    try:
        for p in ports:
            (port, proto) = ufw.util.parse_port_proto(p)
            # quick checks if error in profile
            if proto == "any" and (':' in port or ',' in port):
                raise UFWError(err_msg)
            rule = ufw.common.UFWRule("ACCEPT", proto, port)
            debug(rule)
    except Exception as e:
        debug(e)
        err_msg = _("Invalid ports in profile '%s'") % (name)
        raise UFWError(err_msg)

    return True
예제 #6
0
    def set_default(self, fn, opt, value):
        '''Sets option in defaults file'''
        if not re.match(r'^[\w_]+$', opt):
            err_msg = _("Invalid option")
            raise UFWError(err_msg)

        # Perform this here so we can present a nice error to the user rather
        # than a traceback
        if not os.access(fn, os.W_OK):
            err_msg = _("'%s' is not writable" % (fn))
            raise UFWError(err_msg)

        fns = ufw.util.open_files(fn)
        fd = fns['tmp']

        found = False
        pat = re.compile(r'^' + opt + '=')
        for line in fns['orig']:
            if pat.search(line):
                ufw.util.write_to_file(fd, opt + "=" + value + "\n")
                found = True
            else:
                ufw.util.write_to_file(fd, line)

        # Add the entry if not found
        if not found:
            ufw.util.write_to_file(fd, opt + "=" + value + "\n")

        try:
            ufw.util.close_files(fns)
        except Exception:  # pragma: no coverage
            raise

        # Now that the files are written out, update value in memory
        self.defaults[opt.lower()] = value.lower().strip('"\'')
예제 #7
0
    def parse(self, argv):
        assert (argv[0] == "route")

        # 'ufw delete NUM' is the correct usage, not 'ufw route delete NUM'
        if 'delete' in argv:
            idx = argv.index('delete')
            err_msg = ""
            if len(argv) > idx:
                try:
                    # 'route delete NUM' is unsupported
                    int(argv[idx + 1])
                    err_msg = _(
                        "'route delete NUM' unsupported. Use 'delete NUM' instead."
                    )
                    raise UFWError(err_msg)
                except ValueError:
                    # 'route delete RULE' is supported
                    pass

        # Let's use as much as UFWCommandRule.parse() as possible. The only
        # difference with our rules is that argv[0] is 'route' and we support
        # both 'in on <interface>' and 'out on <interface>' in our rules.
        # Because UFWCommandRule.parse() expects that the interface clause is
        # specified first, strip out the second clause and add it later
        rule_argv = None
        interface = None
        strip = None

        # eg: ['route', 'allow', 'in', 'on', 'eth0', 'out', 'on', 'eth1']
        s = " ".join(argv)
        if " in on " in s and " out on " in s:
            strip = "out"
            if argv.index("in") > argv.index("out"):
                strip = "in"
            # Remove 2nd interface clause from argv and add it to the rule
            # later. Because we searched for " <strip> on " in our joined
            # string we are guaranteed to have argv[argv.index(<strip>) + 2]
            # exist.
            interface = argv[argv.index(strip) + 2]
            rule_argv = argv[0:argv.index(strip)] + argv[argv.index(strip) +
                                                         3:]
        elif not re.search(r' (in|out) on ', s) and \
             not re.search(r' app (in|out) ', s) and \
             (" in " in s or " out " in s):
            # Specifying a direction without an interface doesn't make any
            # sense with route rules. application names could be 'in' or 'out'
            # so don't artificially limit those names.
            err_msg = _("Invalid interface clause for route rule")
            raise UFWError(err_msg)
        else:
            rule_argv = argv

        rule_argv[0] = "rule"
        r = UFWCommandRule.parse(self, rule_argv)
        if 'rule' in r.data:
            r.data['rule'].forward = True
            if strip and interface:
                r.data['rule'].set_interface(strip, interface)

        return r
예제 #8
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
예제 #9
0
    def delete_rule(self, number, force=False):
        '''Delete rule'''
        try:
            n = int(number)
        except Exception:
            err_msg = _("Could not find rule '%s'") % number
            raise UFWError(err_msg)

        rules = self.backend.get_rules()
        if n <= 0 or n > len(rules):
            err_msg = _("Could not find rule '%d'") % n
            raise UFWError(err_msg)

        rule = self.backend.get_rule_by_number(n)
        if not rule:
            err_msg = _("Could not find rule '%d'") % n
            raise UFWError(err_msg)

        rule.remove = True

        ip_version = "v4"
        if rule.v6:
            ip_version = "v6"

        proceed = True
        if not force:
            if rule.forward:
                rstr = "route %s" % \
                        ufw.parser.UFWCommandRouteRule.get_command(rule)
            else:
                rstr = ufw.parser.UFWCommandRule.get_command(rule)
            prompt = _("Deleting:\n %(rule)s\nProceed with operation " \
                       "(%(yes)s|%(no)s)? ") % ({'rule': rstr, \
                                                 'yes': self.yes, \
                                                 'no': self.no})
            msg(prompt, output=sys.stdout, newline=False)
            ans = sys.stdin.readline().lower().strip()
            if ans != "y" and ans != self.yes.lower() and \
               ans != self.yes_full.lower():
                proceed = False

        res = ""
        if proceed:
            res = self.set_rule(rule, ip_version)
        else:
            res = _("Aborted")

        return res
예제 #10
0
    def do_application_action(self, action, profile):
        '''Perform action on profile. action and profile are usually based on
           return values from parse_command().
        '''
        res = ""
        if action == "default-allow":
            res = self.set_default_application_policy("allow")
        elif action == "default-deny":
            res = self.set_default_application_policy("deny")
        elif action == "default-reject":
            res = self.set_default_application_policy("reject")
        elif action == "default-skip":
            res = self.set_default_application_policy("skip")
        elif action == "list":
            res = self.get_application_list()
        elif action == "info":
            res = self.get_application_info(profile)
        elif action == "update" or action == "update-with-new":
            str1 = self.application_update(profile)
            str2 = ""
            if action == "update-with-new":
                str2 = self.application_add(profile)

            if str1 != "" and str2 != "":
                str1 += "\n"
            res = str1 + str2
        else:
            err_msg = _("Unsupported action '%s'") % (action)
            raise UFWError(err_msg)

        return res
예제 #11
0
    def set_default_application_policy(self, policy):
        '''Sets default application policy of firewall'''
        if not self.dryrun:
            if policy == "allow":
                self.set_default(self.files['defaults'], \
                                        "DEFAULT_APPLICATION_POLICY", \
                                        "\"ACCEPT\"")
            elif policy == "deny":
                self.set_default(self.files['defaults'], \
                                        "DEFAULT_APPLICATION_POLICY", \
                                        "\"DROP\"")
            elif policy == "reject":
                self.set_default(self.files['defaults'], \
                                        "DEFAULT_APPLICATION_POLICY", \
                                        "\"REJECT\"")
            elif policy == "skip":
                self.set_default(self.files['defaults'], \
                                        "DEFAULT_APPLICATION_POLICY", \
                                        "\"SKIP\"")
            else:
                err_msg = _("Unsupported policy '%s'") % (policy)
                raise UFWError(err_msg)

        rstr = _("Default application policy changed to '%s'") % (policy)

        return rstr
예제 #12
0
    def get_application_info(self, pname):
        '''Display information on profile'''
        names = []
        if pname == "all":
            names = list(self.backend.profiles.keys())
            names.sort()
        else:
            if not ufw.applications.valid_profile_name(pname):
                err_msg = _("Invalid profile name")
                raise UFWError(err_msg)
            names.append(pname)

        rstr = ""
        for name in names:
            if name not in self.backend.profiles or \
               not self.backend.profiles[name]:
                err_msg = _("Could not find profile '%s'") % (name)
                raise UFWError(err_msg)

            if not ufw.applications.verify_profile(name, \
               self.backend.profiles[name]):
                err_msg = _("Invalid profile")
                raise UFWError(err_msg)

            rstr += _("Profile: %s\n") % (name)
            rstr += _("Title: %s\n") % (ufw.applications.get_title(\
                                        self.backend.profiles[name]))

            rstr += _("Description: %s\n\n") % \
                                            (ufw.applications.get_description(\
                                             self.backend.profiles[name]))

            ports = ufw.applications.get_ports(self.backend.profiles[name])
            if len(ports) > 1 or ',' in ports[0]:
                rstr += _("Ports:")
            else:
                rstr += _("Port:")

            for p in ports:
                rstr += "\n  %s" % (p)

            if name != names[len(names) - 1]:
                rstr += "\n\n--\n\n"

        return ufw.util.wrap_text(rstr)
예제 #13
0
 def stop_firewall(self):
     '''Stop the firewall'''
     err_msg = _("problem running")
     if self.dryrun:
         msg("> " + _("running ufw-init"))
     else:
         (rc, out) = cmd([self.files['init'], 'force-stop'])
         if rc != 0:
             debug(out)
             raise UFWError(err_msg + " ufw-init")
예제 #14
0
    def application_add(self, profile):
        '''Refresh application profile'''
        rstr = ""
        policy = ""

        if profile == "all":
            err_msg = _("Cannot specify 'all' with '--add-new'")
            raise UFWError(err_msg)

        default = self.backend.defaults['default_application_policy']
        if default == "skip":
            ufw.util.debug("Policy is '%s', not adding profile '%s'" % \
                           (policy, profile))
            return rstr
        elif default == "accept":
            policy = "allow"
        elif default == "drop":
            policy = "deny"
        elif default == "reject":
            policy = "reject"
        else:
            err_msg = _("Unknown policy '%s'") % (default)
            raise UFWError(err_msg)

        args = ['ufw']
        if self.backend.dryrun:
            args.append("--dry-run")

        args += [policy, profile]
        try:
            pr = parse_command(args)
        except Exception:  # pragma: no cover
            raise

        if 'rule' in pr.data:
            rstr = self.do_action(pr.action, pr.data['rule'], \
                                  pr.data['iptype'])
        else:
            rstr = self.do_action(pr.action, "", "")

        return rstr
예제 #15
0
 def _chain_cmd(self, chain, args, fail_ok=False):
     '''Perform command on chain'''
     exe = self.iptables
     if chain.startswith("ufw6"):
         exe = self.ip6tables
     (rc, out) = cmd([exe] + args)
     if rc != 0:
         err_msg = _("Could not perform '%s'") % (args)
         if fail_ok:
             debug("FAILOK: " + err_msg)
         else:
             raise UFWError(err_msg)
예제 #16
0
    def __init__(self,
                 name,
                 dryrun,
                 extra_files=None,
                 rootdir=None,
                 datadir=None):
        self.defaults = None
        self.name = name
        self.dryrun = dryrun
        self.rules = []
        self.rules6 = []

        p = _findpath(ufw.common.config_dir, datadir)
        self.files = {
            'defaults': os.path.join(p, 'default/ufw'),
            'conf': os.path.join(p, 'ufw/ufw.conf'),
            'apps': os.path.join(p, 'ufw/applications.d')
        }
        if extra_files is not None:
            self.files.update(extra_files)

        self.loglevels = {
            'off': 0,
            'low': 100,
            'medium': 200,
            'high': 300,
            'full': 400
        }

        self.do_checks = ufw.common.do_checks
        self._do_checks()
        self._get_defaults()
        self._read_rules()

        self.profiles = ufw.applications.get_profiles(self.files['apps'])

        self.iptables = os.path.join(ufw.common.iptables_dir, "iptables")
        self.iptables_restore = os.path.join(ufw.common.iptables_dir, \
                                             "iptables-restore")
        self.ip6tables = os.path.join(ufw.common.iptables_dir, "ip6tables")
        self.ip6tables_restore = os.path.join(ufw.common.iptables_dir, \
                                              "ip6tables-restore")

        try:
            self.iptables_version = ufw.util.get_iptables_version(
                self.iptables)
        except OSError:  # pragma: no coverage
            err_msg = _("Couldn't determine iptables version")
            raise UFWError(err_msg)

        # Initialize via initcaps only when we need it (LP: #1044361)
        self.caps = None
예제 #17
0
    def __init__(self, dryrun, backend_type="iptables"):
        if backend_type == "iptables":
            try:
                self.backend = UFWBackendIptables(dryrun)
            except Exception:  # pragma: no cover
                raise
        else:
            raise UFWError("Unsupported backend type '%s'" % (backend_type))

        # Initialize input strings for translations
        self.no = _("n")
        self.yes = _("y")
        self.yes_full = _("yes")
예제 #18
0
    def register_command(self, c):
        '''Register a command with the parser'''
        if c.command is None or c.command == '':
            # If the command is empty, then use 'type' as command
            key = "%s" % (c.type)
        else:
            key = "%s" % (c.command)

        if c.type not in self.commands:
            self.commands[c.type] = {}
        if key in self.commands[c.type]:
            err_msg = _("Command '%s' already exists") % (key)
            raise UFWError(err_msg)
        self.commands[c.type][key] = c
예제 #19
0
    def set_loglevel(self, level):
        '''Sets log level of firewall'''
        if level not in list(self.loglevels.keys()) + ['on']:
            err_msg = _("Invalid log level '%s'") % (level)
            raise UFWError(err_msg)

        new_level = level
        if level == "on":
            if 'loglevel' not in self.defaults or \
               self.defaults['loglevel'] == "off":
                new_level = "low"
            else:
                new_level = self.defaults['loglevel']

        self.set_default(self.files['conf'], "LOGLEVEL", new_level)
        self.update_logging(new_level)

        if new_level == "off":
            return _("Logging disabled")
        else:
            return _("Logging enabled")
예제 #20
0
    def find_application_name(self, profile_name):
        '''Find the application profile name for profile_name'''
        if self.profiles.has_key(profile_name):
            return profile_name

        match = ""
        matches = 0
        for n in self.profiles.keys():
            if n.lower() == profile_name.lower():
                match = n
                matches += 1

        debug_msg = "'%d' matches for '%s'" % (matches, profile_name)
        debug(debug_msg)
        if matches == 1:
            return match
        elif matches > 1:
            err_msg = _("Found multiple matches for '%s'. Please use exact profile name") % \
                        (profile_name)
        err_msg = _("Could not find a profile matching '%s'") % (profile_name)
        raise UFWError(err_msg)
예제 #21
0
    def _get_default_policy(self, primary="input", check_forward=False):
        '''Get default policy for specified primary chain'''
        policy = "default_" + primary + "_policy"

        rstr = ""
        if self.defaults[policy] == "accept":
            rstr = "allow"
        elif self.defaults[policy] == "reject":
            rstr = "reject"
        else:
            rstr = "deny"

        if check_forward and primary == "forward":
            enabled = False
            err_msg = _("problem running sysctl")

            (rc, out) = ufw.util.cmd(['sysctl', 'net.ipv4.ip_forward'])
            if rc != 0:  # pragma: no cover
                raise UFWError(err_msg)
            if '1' in out:
                enabled = True

            # IPv6 may be disabled, so ignore sysctl output
            if self.use_ipv6():
                (rc, out) = ufw.util.cmd(
                    ['sysctl', 'net.ipv6.conf.default.forwarding'])
                if rc == 0 and '1' in out:
                    enabled = True

                (rc, out) = ufw.util.cmd(
                    ['sysctl', 'net.ipv6.conf.all.forwarding'])
                if rc == 0 and '1' in out:
                    enabled = True

            if not enabled:
                rstr = "disabled"

        return rstr
예제 #22
0
 def reset(self):  # pragma: no coverage
     '''Reset the firewall'''
     raise UFWError("UFWBackend.reset: need to override")
예제 #23
0
 def get_app_rules_from_system(self, template, v6):  # pragma: no coverage
     '''Get a list if rules based on template'''
     raise UFWError("UFWBackend.get_app_rules_from_system: need to " + \
                    "override")
예제 #24
0
 def update_logging(self, level):  # pragma: no coverage
     '''Update loglevel of running firewall'''
     raise UFWError("UFWBackend.update_logging: need to override")
예제 #25
0
    def do_action(self, action, rule, ip_version, force=False):
        '''Perform action on rule. action, rule and ip_version are usually
           based on return values from parse_command().
        '''
        res = ""
        if action.startswith("logging-on"):
            tmp = action.split('_')
            if len(tmp) > 1:
                res = self.set_loglevel(tmp[1])
            else:
                res = self.set_loglevel("on")
        elif action == "logging-off":
            res = self.set_loglevel("off")
        elif action.startswith("default-"):
            err_msg = _("Unsupported default policy")
            tmp = action.split('-')
            if len(tmp) != 3:
                raise UFWError(err_msg)
            res = self.set_default_policy(tmp[1], tmp[2])
        elif action == "reset":
            res = self.reset(force)
        elif action == "status":
            res = self.get_status()
        elif action == "status-verbose":
            res = self.get_status(True)
        elif action.startswith("show"):
            tmp = action.split('-')[1]
            if tmp == "listening":
                res = self.get_show_listening()
            elif tmp == "added":
                res = self.get_show_added()
            else:
                res = self.get_show_raw(tmp)
        elif action == "status-numbered":
            res = self.get_status(False, True)
        elif action == "enable":
            res = self.set_enabled(True)
        elif action == "disable":
            res = self.set_enabled(False)
        elif action == "reload":
            if self.backend.is_enabled():
                self.set_enabled(False)
                self.set_enabled(True)
                res = _("Firewall reloaded")
            else:
                res = _("Firewall not enabled (skipping reload)")
        elif action.startswith("delete-"):
            res = self.delete_rule(action.split('-')[1], force)
        elif action == "allow" or action == "deny" or action == "reject" or \
             action == "limit":
            # allow case insensitive matches for application rules
            if rule.dapp != "":
                try:
                    tmp = self.backend.find_application_name(rule.dapp)
                    if tmp != rule.dapp:
                        rule.dapp = tmp
                        rule.set_port(tmp, "dst")
                except UFWError as e:
                    # allow for the profile being deleted (LP: #407810)
                    if not rule.remove:  # pragma: no cover
                        error(e.value)
                    if not ufw.applications.valid_profile_name(rule.dapp):
                        err_msg = _("Invalid profile name")
                        raise UFWError(err_msg)

            if rule.sapp != "":
                try:
                    tmp = self.backend.find_application_name(rule.sapp)
                    if tmp != rule.sapp:
                        rule.sapp = tmp
                        rule.set_port(tmp, "dst")
                except UFWError as e:
                    # allow for the profile being deleted (LP: #407810)
                    if not rule.remove:  # pragma: no cover
                        error(e.value)
                    if not ufw.applications.valid_profile_name(rule.sapp):
                        err_msg = _("Invalid profile name")
                        raise UFWError(err_msg)

            res = self.set_rule(rule, ip_version)
        else:
            err_msg = _("Unsupported action '%s'") % (action)
            raise UFWError(err_msg)

        return res
예제 #26
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
예제 #27
0
    def get_show_listening(self):
        '''Shows listening services and incoming rules that might affect
           them'''
        res = ""
        try:
            d = ufw.util.parse_netstat_output(self.backend.use_ipv6())
        except Exception:  # pragma: no cover
            err_msg = _("Could not get listening status")
            raise UFWError(err_msg)

        rules = self.backend.get_rules()

        protocols = list(d.keys())
        protocols.sort()
        for proto in protocols:
            if not self.backend.use_ipv6() and proto in ['tcp6', 'udp6']:
                continue  # pragma: no cover
            res += "%s:\n" % (proto)
            ports = list(d[proto].keys())
            ports.sort()
            for port in ports:
                for item in d[proto][port]:
                    addr = item['laddr']
                    if not addr.startswith("127.") and \
                       not addr.startswith("::1"):
                        ifname = ""

                        res += "  %s " % port
                        if addr == "0.0.0.0" or addr == "::":
                            res += "* "
                            addr = "%s/0" % (item['laddr'])
                        else:
                            res += "%s " % addr
                            ifname = ufw.util.get_if_from_ip(addr)
                        res += "(%s)" % os.path.basename(item['exe'])

                        # Create an incoming rule since matching outgoing and
                        # forward rules doesn't make sense for this report.
                        rule = ufw.common.UFWRule(action="allow", \
                                                  protocol=proto[:3], \
                                                  dport=port, \
                                                  dst=addr,
                                                  direction="in", \
                                                  forward=False
                                                 )
                        rule.set_v6(proto.endswith("6"))

                        if ifname != "":
                            rule.set_interface("in", ifname)

                        rule.normalize()

                        # Get the non-tuple rule from get_matching(), and then
                        # add its corresponding CLI command.
                        matching = self.backend.get_matching(rule)
                        if len(matching) > 0:
                            res += "\n"
                            for i in matching:
                                if i > 0 and i - 1 < len(rules):
                                    res += "   [%2d] %s\n" % (i, \
                                        # Don't need UFWCommandRule here either

                                        ufw.parser.UFWCommandRule.get_command(\
                                          rules[i-1])
                                    )

                        res += "\n"

        if not self.backend.use_ipv6():
            ufw.util.debug("Skipping tcp6 and udp6 (IPv6 is disabled)")

        return res
예제 #28
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
예제 #29
0
 def help(self, args):
     raise UFWError("UFWCommand.help: need to override")
예제 #30
0
    def parse(self, argv):
        action = ""
        rule = ""
        type = ""
        from_type = "any"
        to_type = "any"
        from_service = ""
        to_service = ""
        insert_pos = ""
        logtype = ""
        remove = False

        if len(argv) > 0 and argv[0].lower() == "rule":
            argv.remove(argv[0])

        # TODO: break this out
        if len(argv) > 0:
            if argv[0].lower() == "delete" and len(argv) > 1:
                remove = True
                argv.remove(argv[0])
                rule_num = None
                try:
                    rule_num = int(argv[0])
                except Exception:
                    action = argv[0]

                # return quickly if deleting by rule number
                if rule_num is not None:
                    r = UFWParserResponse('delete-%d' % rule_num)
                    return r

            elif argv[0].lower() == "insert":
                if len(argv) < 4:
                    raise ValueError()
                insert_pos = argv[1]

                # Using position '0' appends the rule while '-1' prepends,
                # which is potentially confusing for the end user
                if insert_pos == "0" or insert_pos == "-1":
                    err_msg = _("Cannot insert rule at position '%s'") % \
                                (insert_pos)
                    raise UFWError(err_msg)

                # strip out 'insert NUM' and parse as normal
                del argv[1]
                del argv[0]

            elif argv[0].lower() == "prepend":
                insert_pos = -1
                del argv[0]

            action = argv[0]

        if action != "allow" and action != "deny" and action != "reject" and \
           action != "limit":
            raise ValueError()

        nargs = len(argv)
        if nargs < 2:
            raise ValueError()

        # set/strip
        rule_direction = "in"
        if nargs > 1 and (argv[1].lower() == "in" or \
                          argv[1].lower() == "out"):
            rule_direction = argv[1].lower()

        # strip out direction if not an interface rule
        if nargs > 2 and argv[2] != "on" and (argv[1].lower() == "in" or \
                                              argv[1].lower() == "out"):
            rule_direction = argv[1].lower()
            del argv[1]
            nargs = len(argv)

        # strip out 'on' as in 'allow in on eth0 ...'
        has_interface = False
        if nargs > 1 and (argv.count('in') > 0 or argv.count('out') > 0):
            err_msg = _("Invalid interface clause")

            if argv[1].lower() != "in" and argv[1].lower() != "out":
                raise UFWError(err_msg)
            if nargs < 3 or argv[2].lower() != "on":
                raise UFWError(err_msg)

            del argv[2]
            nargs = len(argv)
            has_interface = True

        log_idx = 0
        if has_interface and nargs > 3 and (argv[3].lower() == "log" or \
                                            argv[3].lower() == 'log-all'):
            log_idx = 3
        elif nargs > 2 and (argv[1].lower() == "log" or \
                           argv[1].lower() == 'log-all'):
            log_idx = 1

        if log_idx > 0:
            logtype = argv[log_idx].lower()
            # strip out 'log' or 'log-all' and parse as normal
            del argv[log_idx]
            nargs = len(argv)

        if "log" in argv:
            err_msg = _("Option 'log' not allowed here")
            raise UFWError(err_msg)

        if "log-all" in argv:
            err_msg = _("Option 'log-all' not allowed here")
            raise UFWError(err_msg)

        comment = ""
        if 'comment' in argv:
            comment_idx = argv.index("comment")
            if comment_idx == len(argv) - 1:
                err_msg = _("Option 'comment' missing required argument")
                raise UFWError(err_msg)
            comment = argv[comment_idx + 1]
            # TODO: properly support "'" in the comment string. See r949 for
            # details
            if "'" in comment:
                err_msg = _("Comment may not contain \"'\"")
                raise ValueError(err_msg)

            del argv[comment_idx + 1]
            del argv[comment_idx]
            nargs = len(argv)

        if nargs < 2 or nargs > 13:
            raise ValueError()

        rule_action = action
        if logtype != "":
            rule_action += "_" + logtype
        rule = ufw.common.UFWRule(rule_action, "any", "any", \
                                  direction=rule_direction,
                                  comment=ufw.util.hex_encode(comment))
        if remove:
            rule.remove = remove
        elif insert_pos != "":
            try:
                rule.set_position(insert_pos)
            except Exception:
                raise
        if nargs == 2:
            # Short form where only app or port/proto is given
            if ufw.applications.valid_profile_name(argv[1]):
                # Check if name collision with /etc/services. If so, use
                # /etc/services instead of application profile
                try:
                    ufw.util.get_services_proto(argv[1])
                except Exception:
                    type = "both"
                    rule.dapp = argv[1]
                    rule.set_port(argv[1], "dst")
            if rule.dapp == "":
                try:
                    (port, proto) = ufw.util.parse_port_proto(argv[1])
                except ValueError as e:
                    raise UFWError(e)

                if not re.match('^\d([0-9,:]*\d+)*$', port):
                    if ',' in port or ':' in port:
                        err_msg = _("Port ranges must be numeric")
                        raise UFWError(err_msg)
                    to_service = port

                try:
                    rule.set_protocol(proto)
                    rule.set_port(port, "dst")
                    type = "both"
                except UFWError:
                    err_msg = _("Bad port")
                    raise UFWError(err_msg)
        elif (nargs + 1) % 2 != 0:
            err_msg = _("Wrong number of arguments")
            raise UFWError(err_msg)
        elif 'from' not in argv and 'to' not in argv and 'in' not in argv and \
             'out' not in argv:
            err_msg = _("Need 'to' or 'from' clause")
            raise UFWError(err_msg)
        else:
            # Full form with PF-style syntax
            keys = ['proto', 'from', 'to', 'port', 'app', 'in', 'out']

            # quick check
            if argv.count("to") > 1 or \
               argv.count("from") > 1 or \
               argv.count("proto") > 1 or \
               argv.count("port") > 2 or \
               argv.count("in") > 1 or \
               argv.count("out") > 1 or \
               argv.count("app") > 2 or \
               argv.count("app") > 0 and argv.count("proto") > 0:
                err_msg = _("Improper rule syntax")
                raise UFWError(err_msg)

            i = 0
            loc = ""
            for arg in argv:
                if i % 2 != 0 and argv[i] not in keys:
                    err_msg = _("Invalid token '%s'") % (argv[i])
                    raise UFWError(err_msg)
                if arg == "proto":
                    if i + 1 < nargs:
                        try:
                            rule.set_protocol(argv[i + 1])
                        except Exception:
                            raise
                    else:  # pragma: no cover
                        # This can't normally be reached because of nargs
                        # checks above, but leave it here in case our parsing
                        # changes
                        err_msg = _("Invalid 'proto' clause")
                        raise UFWError(err_msg)
                elif arg == "in" or arg == "out":
                    if i + 1 < nargs:
                        try:
                            if arg == "in":
                                rule.set_interface("in", argv[i + 1])
                            elif arg == "out":
                                rule.set_interface("out", argv[i + 1])
                        except Exception:
                            raise
                    else:  # pragma: no cover
                        # This can't normally be reached because of nargs
                        # checks above, but leave it here in case our parsing
                        # changes
                        err_msg = _("Invalid '%s' clause") % (arg)
                        raise UFWError(err_msg)
                elif arg == "from":
                    if i + 1 < nargs:
                        try:
                            faddr = argv[i + 1].lower()
                            if faddr == "any":
                                faddr = "0.0.0.0/0"
                                from_type = "any"
                            else:
                                if ufw.util.valid_address(faddr, "6"):
                                    from_type = "v6"
                                else:
                                    from_type = "v4"
                            rule.set_src(faddr)
                        except Exception:
                            raise
                        loc = "src"
                    else:  # pragma: no cover
                        # This can't normally be reached because of nargs
                        # checks above, but leave it here in case our parsing
                        # changes
                        err_msg = _("Invalid 'from' clause")
                        raise UFWError(err_msg)
                elif arg == "to":
                    if i + 1 < nargs:
                        try:
                            saddr = argv[i + 1].lower()
                            if saddr == "any":
                                saddr = "0.0.0.0/0"
                                to_type = "any"
                            else:
                                if ufw.util.valid_address(saddr, "6"):
                                    to_type = "v6"
                                else:
                                    to_type = "v4"
                            rule.set_dst(saddr)
                        except Exception:
                            raise
                        loc = "dst"
                    else:  # pragma: no cover
                        # This can't normally be reached because of nargs
                        # checks above, but leave it here in case our parsing
                        # changes
                        err_msg = _("Invalid 'to' clause")
                        raise UFWError(err_msg)
                elif arg == "port" or arg == "app":
                    if i + 1 < nargs:
                        if loc == "":
                            err_msg = _("Need 'from' or 'to' with '%s'") % \
                                        (arg)
                            raise UFWError(err_msg)

                        tmp = argv[i + 1]
                        if arg == "app":
                            if loc == "src":
                                rule.sapp = tmp
                            else:
                                rule.dapp = tmp
                        elif not re.match('^\d([0-9,:]*\d+)*$', tmp):
                            if ',' in tmp or ':' in tmp:
                                err_msg = _("Port ranges must be numeric")
                                raise UFWError(err_msg)

                            if loc == "src":
                                from_service = tmp
                            else:
                                to_service = tmp
                        try:
                            rule.set_port(tmp, loc)
                        except Exception:
                            raise
                    else:  # pragma: no cover
                        # This can't normally be reached because of nargs
                        # checks above, but leave it here in case our parsing
                        # changes
                        err_msg = _("Invalid 'port' clause")
                        raise UFWError(err_msg)
                i += 1

            # Figure out the type of rule (IPv4, IPv6, or both) this is
            if from_type == "any" and to_type == "any":
                type = "both"
            elif from_type != "any" and to_type != "any" and \
                 from_type != to_type:
                err_msg = _("Mixed IP versions for 'from' and 'to'")
                raise UFWError(err_msg)
            elif from_type != "any":
                type = from_type
            elif to_type != "any":
                type = to_type

        # Adjust protocol
        if to_service != "" or from_service != "":
            proto = ""
            if to_service != "":
                try:
                    proto = ufw.util.get_services_proto(to_service)
                except Exception:  # pragma: no cover
                    # This can't normally be reached because of set_port()
                    # checks above, but leave it here in case our parsing
                    # changes
                    err_msg = _("Could not find protocol")
                    raise UFWError(err_msg)
            if from_service != "":
                if proto == "any" or proto == "":
                    try:
                        proto = ufw.util.get_services_proto(from_service)
                    except Exception:  # pragma: no cover
                        # This can't normally be reached because of set_port()
                        # checks above, but leave it here in case our parsing
                        # changes
                        err_msg = _("Could not find protocol")
                        raise UFWError(err_msg)
                else:
                    try:
                        tmp = ufw.util.get_services_proto(from_service)
                    except Exception:  # pragma: no cover
                        # This can't normally be reached because of set_port()
                        # checks above, but leave it here in case our parsing
                        # changes
                        err_msg = _("Could not find protocol")
                        raise UFWError(err_msg)
                    if proto == "any" or proto == tmp:
                        proto = tmp
                    elif tmp == "any":
                        pass
                    else:
                        err_msg = _("Protocol mismatch (from/to)")
                        raise UFWError(err_msg)

            # Verify found proto with specified proto
            if rule.protocol == "any":
                rule.set_protocol(proto)
            elif proto != "any" and rule.protocol != proto:
                err_msg = _("Protocol mismatch with specified protocol %s") % \
                            (rule.protocol)
                raise UFWError(err_msg)

        # adjust type as needed
        if rule:
            if rule.protocol in ufw.util.ipv4_only_protocols and \
               type == "both":
                debug("Adjusting iptype to 'v4' for protocol '%s'" % \
                      (rule.protocol))
                type = "v4"

            # Now verify the rule
            rule.verify(type)

        r = UFWParserResponse(action)
        r.data['type'] = self.type
        r.data['rule'] = rule
        r.data['iptype'] = type

        return r