Пример #1
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)
Пример #2
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('|')
    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 as e:
        debug(e)
        err_msg = _("Invalid ports in profile '%s'") % (name)
        raise UFWError(err_msg)

    return True
Пример #3
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")
Пример #4
0
 def stop_firewall(self):
     """Stops 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")
Пример #5
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)
Пример #6
0
    def find_application_name(self, str):
        '''Find the application profile name for str'''
        if self.profiles.has_key(str):
            return str

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

        debug_msg = "'%d' matches for '%s'" % (matches, str)
        debug(debug_msg)
        if matches == 1:
            return match
        elif matches > 1:
            err_msg = _("Found multiple matches for '%s'. Please use exact profile name") % (str)
        err_msg = _("Could not find a profile matching '%s'") % (str)
        raise UFWError(err_msg)
Пример #7
0
    def _need_reload(self, v6):
        """Check if all chains exist"""
        if self.dryrun:
            return False

        prefix = "ufw"
        exe = self.iptables
        if v6:
            prefix = "ufw6"
            exe = self.ip6tables

        for chain in ["input", "output", "forward", "limit", "limit-accept"]:
            if v6 and (chain == "limit" or chain == "limit-accept"):
                continue

            (rc, out) = cmd([exe, "-n", "-L", prefix + "-user-" + chain])
            if rc != 0:
                debug("_need_reload: forcing reload")
                return True

        return False
Пример #8
0
    def find_application_name(self, profile_name):
        '''Find the application profile name for profile_name'''
        if profile_name in self.profiles:
            return profile_name

        match = ""
        matches = 0
        for n in list(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)
Пример #9
0
    def get_rule_by_number(self, n):
        '''Return rule specified by number seen via "status numbered"'''
        rules = self.get_rules()

        count = 1
        app_rules = {}
        for r in rules:
            tupl = ""
            if r.dapp != "" or r.sapp != "":
                tupl = r.get_app_tuple()

                if app_rules.has_key(tupl):
                    debug("Skipping found tuple '%s'" % (tupl))
                    continue
                else:
                    app_rules[tupl] = True
            if count == int(n):
                return r
            count += 1
        
        return None
Пример #10
0
    def _need_reload(self, v6):
        '''Check if all chains exist'''
        if self.dryrun:
            return False

        prefix = "ufw"
        exe = self.iptables
        if v6:
            prefix = "ufw6"
            exe = self.ip6tables

        for chain in [ 'input', 'output', 'forward', 'limit', 'limit-accept' ]:
            if v6 and (chain == "limit" or chain == "limit-accept"):
                continue

            (rc, out) = cmd([exe, '-n', '-L', prefix + "-user-" + chain])
            if rc != 0:
                debug("_need_reload: forcing reload")
                return True

        return False
Пример #11
0
    def find_application_name(self, profile_name):
        '''Find the application profile name for profile_name'''
        if profile_name in self.profiles:
            return profile_name

        match = ""
        matches = 0
        for n in list(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)
        else:
            err_msg = _("Could not find a profile matching '%s'") % \
                        (profile_name)
        raise UFWError(err_msg)
Пример #12
0
    def get_rules_count(self, v6):
        '''Return number of ufw rules (not iptables rules)'''
        rules = []
        if v6:
            rules = self.rules6
        else:
            rules = self.rules

        count = 0
        app_rules = {}
        for r in rules:
            tupl = ""
            if r.dapp != "" or r.sapp != "":
                tupl = r.get_app_tuple()

                if app_rules.has_key(tupl):
                    debug("Skipping found tuple '%s'" % (tupl))
                    continue
                else:
                    app_rules[tupl] = True
            count += 1

        return count
Пример #13
0
    def get_rules_count(self, v6):
        '''Return number of ufw rules (not iptables rules)'''
        rules = []
        if v6:
            rules = self.rules6
        else:
            rules = self.rules

        count = 0
        app_rules = {}
        for r in rules:
            tupl = ""
            if r.dapp != "" or r.sapp != "":
                tupl = r.get_app_tuple()

                if app_rules.has_key(tupl):
                    debug("Skipping found tuple '%s'" % (tupl))
                    continue
                else:
                    app_rules[tupl] = True
            count += 1

        return count
Пример #14
0
    def match(x, y):
        '''Check if rules match
        Return codes:
          0  match
          1  no match
         -1  match all but action, log-type and/or comment
         -2  match all but comment
        '''
        if not x or not y:
            raise ValueError()

        dbg_msg = "No match '%s' '%s'" % (x, y)
        if x.dport != y.dport:
            debug(dbg_msg)
            return 1
        if x.sport != y.sport:
            debug(dbg_msg)
            return 1
        if x.protocol != y.protocol:
            debug(dbg_msg)
            return 1
        if x.src != y.src:
            debug(dbg_msg)
            return 1
        if x.dst != y.dst:
            debug(dbg_msg)
            return 1
        if x.v6 != y.v6:
            debug(dbg_msg)
            return 1
        if x.dapp != y.dapp:
            debug(dbg_msg)
            return 1
        if x.sapp != y.sapp:
            debug(dbg_msg)
            return 1
        if x.interface_in != y.interface_in:
            debug(dbg_msg)
            return 1
        if x.interface_out != y.interface_out:
            debug(dbg_msg)
            return 1
        if x.direction != y.direction:
            debug(dbg_msg)
            return 1
        if x.forward != y.forward:
            debug(dbg_msg)
            return 1
        if x.action == y.action and x.logtype == y.logtype and \
                x.comment == y.comment:
            dbg_msg = _("Found exact match")
            debug(dbg_msg)
            return 0
        if x.action == y.action and x.logtype == y.logtype and \
                x.comment != y.comment:
            dbg_msg = _("Found exact match, excepting comment")
            debug(dbg_msg)
            return -2

        dbg_msg = _("Found non-action/non-logtype/comment match " \
                    "(%(xa)s/%(ya)s/'%(xc)s' %(xl)s/%(yl)s/'%(yc)s')") % \
                    ({'xa': x.action, 'ya': y.action,
                      'xl': x.logtype, 'yl': y.logtype,
                      'xc': x.comment, 'yc': y.comment})
        debug(dbg_msg)
        return -1
Пример #15
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))
Пример #16
0
    def set_rule(self, rule, allow_reload=True):
        '''Updates firewall with rule by:
        * appending the rule to the chain if new rule and firewall enabled
        * deleting the rule from the chain if found and firewall enabled
        * inserting the rule if possible and firewall enabled
        * updating user rules file
        * reloading the user rules file if rule is modified
        '''
        rstr = ""

        if rule.v6:
            if not self.use_ipv6():
                err_msg = _("Adding IPv6 rule failed: IPv6 not enabled")
                raise UFWError(err_msg)
            if rule.action == 'limit':
                # Netfilter doesn't have ip6t_recent yet, so skip
                return _("Skipping unsupported IPv6 '%s' rule") % (rule.action)

        if rule.multi and rule.protocol != "udp" and rule.protocol != "tcp":
            err_msg = _("Must specify 'tcp' or 'udp' with multiple ports")
            raise UFWError(err_msg)

        newrules = []
        found = False
        modified = False

        rules = self.rules
        position = rule.position
        if rule.v6:
            if self.iptables_version < "1.4" and (rule.dapp != "" or \
                                                  rule.sapp != ""):
                return _(
                    "Skipping IPv6 application rule. Need at least iptables 1.4"
                )
            rules = self.rules6

        # bail if we have a bad position
        if position < 0 or position > len(rules):
            err_msg = _("Invalid position '%d'") % (position)
            raise UFWError(err_msg)

        if position > 0 and rule.remove:
            err_msg = _("Cannot specify insert and delete")
            raise UFWError(err_msg)
        if position > len(rules):
            err_msg = _("Cannot insert rule at position '%d'") % position
            raise UFWError(err_msg)

        # First construct the new rules list
        try:
            rule.normalize()
        except Exception:
            raise

        count = 1
        inserted = False
        matches = 0
        last = ('', '', '', '')
        for r in rules:
            try:
                r.normalize()
            except Exception:
                raise

            current = (r.dst, r.src, r.dapp, r.sapp)
            if count == position:
                # insert the rule if:
                # 1. the last rule was not an application rule
                # 2. the current rule is not an application rule
                # 3. the last application rule is different than the current
                #    while the new rule is different than the current one
                if (last[2] == '' and last[3] == '' and count > 1) or \
                   (current[2] == '' and current[3] == '') or \
                   last != current:
                    inserted = True
                    newrules.append(rule.dup_rule())
                    last = ('', '', '', '')
                else:
                    position += 1
            last = current
            count += 1

            ret = UFWRule.match(r, rule)
            if ret < 1:
                matches += 1

            if ret == 0 and not found and not inserted:
                # If find the rule, add it if it's not to be removed, otherwise
                # skip it.
                found = True
                if not rule.remove:
                    newrules.append(rule.dup_rule())
            elif ret < 0 and not rule.remove and not inserted:
                # If only the action is different, replace the rule if it's not
                # to be removed.
                found = True
                modified = True
                newrules.append(rule.dup_rule())
            else:
                newrules.append(r)

        if inserted:
            if matches > 0:
                rstr = _("Skipping inserting existing rule")
                if rule.v6:
                    rstr += " (v6)"
                return rstr
        else:
            # Add rule to the end if it was not already added.
            if not found and not rule.remove:
                newrules.append(rule.dup_rule())

            # Don't process non-existing or unchanged pre-exisiting rules
            if not found and rule.remove and not self.dryrun:
                rstr = _("Could not delete non-existent rule")
                if rule.v6:
                    rstr += " (v6)"
                return rstr
            elif found and not rule.remove and not modified:
                rstr = _("Skipping adding existing rule")
                if rule.v6:
                    rstr += " (v6)"
                return rstr

        if rule.v6:
            self.rules6 = newrules
        else:
            self.rules = newrules

        # Update the user rules file
        try:
            self._write_rules(rule.v6)
        except UFWError:
            raise
        except Exception:
            err_msg = _("Couldn't update rules file")
            UFWError(err_msg)

        # We wrote out the rules, so set reasonable string. We will change
        # this below when operating on the live firewall.
        rstr = _("Rules updated")
        if rule.v6:
            rstr = _("Rules updated (v6)")

        # Operate on the chains
        if self.is_enabled() and not self.dryrun:
            flag = ""
            if modified or self._need_reload(rule.v6) or inserted:
                rstr = ""
                if inserted:
                    rstr += _("Rule inserted")
                else:
                    rstr += _("Rule updated")
                if rule.v6:
                    rstr += " (v6)"
                if allow_reload:
                    # Reload the chain
                    try:
                        self._reload_user_rules()
                    except Exception:
                        raise
                else:
                    rstr += _(" (skipped reloading firewall)")
            elif found and rule.remove:
                flag = '-D'
                rstr = _("Rule deleted")
            elif not found and not modified and not rule.remove:
                flag = '-A'
                rstr = _("Rule added")

            if flag != "":
                exe = self.iptables
                chain_prefix = "ufw"
                if rule.v6:
                    exe = self.ip6tables
                    chain_prefix = "ufw6"
                    rstr += " (v6)"
                chain_suffix = "input"
                if rule.direction == "out":
                    chain_suffix = "output"
                chain = "%s-user-%s" % (chain_prefix, chain_suffix)

                # Is the firewall running?
                err_msg = _("Could not update running firewall")
                (rc, out) = cmd([exe, '-L', chain, '-n'])
                if rc != 0:
                    raise UFWError(err_msg)

                rule_str = "%s %s %s" % (flag, chain, rule.format_rule())
                pat_log = re.compile(r'(-A +)(ufw6?-user-[a-z\-]+)(.*)')
                for s in self._get_lists_from_formatted(rule_str, \
                                                        chain_prefix, \
                                                        chain_suffix):
                    (rc, out) = cmd([exe] + s)
                    if rc != 0:
                        msg(out, sys.stderr)
                        UFWError(err_msg)

                    # delete any lingering RETURN rules (needed for upgrades)
                    if flag == "-A" and pat_log.search(" ".join(s)):
                        c = pat_log.sub(r'\2', " ".join(s))
                        (rc, out) = cmd([exe, '-D', c, '-j', 'RETURN'])
                        if rc != 0:
                            debug("FAILOK: -D %s -j RETURN" % (c))

        return rstr
Пример #17
0
    def fuzzy_dst_match(x, y):
	'''This will match if x is more specific than y. Eg, for protocol if x
	   is tcp and y is all or for address if y is a network and x is a
           subset of y (where x is either an address or network). Returns:

            0  match
            1  no match
           -1  fuzzy match

           This is a fuzzy destination match, so source ports or addresses
           are not considered, and (currently) only incoming.
        '''
        def _match_ports(p, to_match):
            '''Returns True if p is an exact match or within a multi rule'''
            for port in to_match.split(','):
                if p == port:
                    return True
                if ':' in port:
                    (low, high) = port.split(':')
                    if int(p) >= int(low) and int(p) <= int(high):
                        return True

            return False

        if not x or not y:
            raise ValueError()

        # Ok if exact match
        if x.match(y) == 0:
            return 0

        dbg_msg = "No fuzzy match '%s (v6=%s)' '%s (v6=%s)'" % \
                   (x, x.v6, y, y.v6)

        # Direction must match
        if y.direction != "in":
            debug("(direction) " + dbg_msg + " (not incoming)")
            return 1

        # Protocols must match or y 'any'
        if x.protocol != y.protocol and y.protocol != "any":
            debug("(protocol) " + dbg_msg)
            return 1

        # Destination ports must match or y 'any'
        if y.dport != "any" and not _match_ports(x.dport, y.dport):
            debug("(dport) " + dbg_msg)
            return 1

        if y.interface_in == "":
	    # If destination interface is not specified, destination addresses
            # must match or x must be contained in y

            if x.interface_in == "" and x._is_anywhere(x.dst):
		# if x and y interfaces are not specified, and x.dst is
                # anywhere then ok
                pass
            elif x.dst != y.dst and '/' not in y.dst:
                debug("(dst) " + dbg_msg)
                return 1
            elif x.dst != y.dst and '/' in y.dst and x.v6 == y.v6 and \
               not ufw.util.in_network(x.dst, y.dst, x.v6):
                debug("(dst) " + dbg_msg + " ('%s' not in network '%s')" % \
                      (x.dst, y.dst))
                return 1
        else:
	    # If destination interface is specified, then:
            #  if specified, both interfaces must match or
            #  the IP of the interface must match the IP of y or
	    #  the IP of the interface must be contained in y
            if x.interface_in != "" and x.interface_in != y.interface_in:
                debug("(interface) " + dbg_msg + " (%s != %s)" % \
                      (x.interface_in, y.interface_in))
                return 1

            if_ip = ufw.util.get_ip_from_if(y.interface_in, x.v6)
            if y.dst != if_ip and '/' not in y.dst:
                debug("(interface) " + dbg_msg + " (%s != %s)" % \
                      (y.dst, if_ip))
                return 1
            elif y.dst != if_ip and '/' in y.dst and x.v6 == y.v6 and \
               not ufw.util.in_network(if_ip, y.dst, x.v6):
                debug("(interface) " + dbg_msg + \
                      " ('%s' not in network '%s')" % (if_ip, y.dst))
                return 1

        if x.v6 != y.v6:
            debug("(v6) " + dbg_msg + " (%s != %s)" % (x.dst, y.dst))
            return 1

        # if we made it here, it is a fuzzy match
        debug("(fuzzy match) '%s (v6=%s)' '%s (v6=%s)'" % (x, x.v6, y, y.v6))
        return -1
Пример #18
0
    def get_status(self, verbose=False, show_count=False):
        """Show ufw managed rules"""
        out = ""
        out6 = ""
        if self.dryrun:
            out = "> " + _("Checking iptables\n")
            if self.use_ipv6():
                out += "> " + _("Checking ip6tables\n")
            return out

        err_msg = _("problem running")
        for direction in ["input", "output"]:
            # Is the firewall loaded at all?
            (rc, out) = cmd([self.iptables, "-L", "ufw-user-%s" % (direction), "-n"])
            if rc == 1:
                return _("Status: inactive")
            elif rc != 0:
                raise UFWError(err_msg + " iptables: %s\n" % (out))

            if self.use_ipv6():
                (rc, out6) = cmd([self.ip6tables, "-L", "ufw6-user-%s" % (direction), "-n"])
                if rc != 0:
                    raise UFWError(err_msg + " ip6tables")

        s = ""
        str_out = ""
        rules = self.rules + self.rules6
        count = 1
        app_rules = {}
        for r in rules:
            tmp_str = ""
            location = {}
            tupl = ""
            show_proto = True
            if not verbose and (r.dapp != "" or r.sapp != ""):
                show_proto = False
                tupl = r.get_app_tuple()

                if app_rules.has_key(tupl):
                    debug("Skipping found tuple '%s'" % (tupl))
                    continue
                else:
                    app_rules[tupl] = True

            for loc in ["dst", "src"]:
                location[loc] = ""

                port = ""
                tmp = ""
                if loc == "dst":
                    tmp = r.dst
                    if not verbose and r.dapp != "":
                        port = r.dapp
                        if r.v6 and tmp == "::/0":
                            port += " (v6)"
                    else:
                        port = r.dport
                else:
                    tmp = r.src
                    if not verbose and r.sapp != "":
                        port = r.sapp
                        if r.v6 and tmp == "::/0":
                            port += " (v6)"
                    else:
                        port = r.sport

                if tmp != "0.0.0.0/0" and tmp != "::/0":
                    location[loc] = tmp

                if port != "any":
                    if location[loc] == "":
                        location[loc] = port
                    else:
                        location[loc] += " " + port

                    if show_proto and r.protocol != "any":
                        location[loc] += "/" + r.protocol

                    if verbose:
                        if loc == "dst" and r.dapp != "":
                            location[loc] += " (%s" % (r.dapp)
                            if r.v6 and tmp == "::/0":
                                location[loc] += " (v6)"
                            location[loc] += ")"
                        if loc == "src" and r.sapp != "":
                            location[loc] += " (%s" % (r.sapp)
                            if r.v6 and tmp == "::/0":
                                location[loc] += " (v6)"
                            location[loc] += ")"

                if port == "any":
                    if tmp == "0.0.0.0/0" or tmp == "::/0":
                        location[loc] = "Anywhere"

                        # Show the protocol if Anywhere to Anwhere, have
                        # protocol and source and dest ports are any
                        if show_proto and r.protocol != "any" and r.dst == r.src and r.dport == r.sport:
                            location[loc] += "/" + r.protocol

                        if tmp == "::/0":
                            location[loc] += " (v6)"
                    else:
                        # Show the protocol if have protocol, and source
                        # and dest ports are any
                        if show_proto and r.protocol != "any" and r.dport == r.sport:
                            location[loc] += "/" + r.protocol
                if loc == "dst" and r.interface_in != "":
                    location[loc] += " on %s" % (r.interface_in)
                if loc == "src" and r.interface_out != "":
                    location[loc] += " on %s" % (r.interface_out)

            attribs = []
            attrib_str = ""
            if r.logtype or r.direction.lower() == "out":
                if r.logtype:
                    attribs.append(r.logtype.lower())
                if show_count and r.direction == "out":
                    attribs.append(r.direction)
                if len(attribs) > 0:
                    attrib_str = " (%s)" % (", ".join(attribs))

            # now construct the rule output string
            if show_count:
                tmp_str += "[%2d] " % (count)

            dir_str = r.direction.upper()
            if r.direction == "in" and not verbose and not show_count:
                dir_str = ""
            tmp_str += "%-26s %-12s%s%s\n" % (
                location["dst"],
                " ".join([r.action.upper(), dir_str]),
                location["src"],
                attrib_str,
            )

            # Show the list in the order given if a numbered list, otherwise
            # split incoming and outgoing rules
            if show_count:
                s += tmp_str
            else:
                if r.direction == "out":
                    str_out += tmp_str
                else:
                    s += tmp_str
            count += 1

        if s != "" or str_out != "":
            full_str = "\n\n"
            if show_count:
                full_str += "     "
            str_to = _("To")
            str_from = _("From")
            str_action = _("Action")
            rules_header = "%-26s %-12s%s\n" % (
                str_to.decode("utf-8", "ignore"),
                str_action.decode("utf-8", "ignore"),
                str_from.decode("utf-8", "ignore"),
            )
            if show_count:
                rules_header += "     "
            rules_header += "%-26s %-12s%s\n" % (
                "-" * len(str_to.decode("utf-8", "ignore")),
                "-" * len(str_action.decode("utf-8", "ignore")),
                "-" * len(str_from.decode("utf-8", "ignore")),
            )
            full_str += rules_header.encode("utf-8", "ignore")
            if s != "":
                full_str += s
            if s != "" and str_out != "":
                full_str += _("\n")
            if str_out != "":
                full_str += str_out

            s = full_str

        if verbose:
            (level, logging_str) = self.get_loglevel()
            policy_str = _("Default: %(in)s (incoming), %(out)s (outgoing)") % (
                {"in": self.get_default_policy(), "out": self.get_default_policy("output")}
            )
            app_policy_str = self.get_default_application_policy()
            return _("Status: active\n%(log)s\n%(pol)s\n%(app)s%(status)s") % (
                {"log": logging_str, "pol": policy_str, "app": app_policy_str, "status": s}
            )
        else:
            return _("Status: active%s") % (s)
Пример #19
0
Файл: parser.py Проект: jbq/ufw
    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 != 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' adds rule at end, which is potentially
	        # confusing for the end user
                if insert_pos == "0":
                    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]

            action = argv[0]

        if action == "":
            action = self.command
            argv.insert(0, action)

        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)

        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)
        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 UFWError:
                    err_msg = _("Bad port")
                    raise UFWError(err_msg)

                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)

            # Don't specify a port with ipv6, esp or ah protocols
            if rule.protocol in [ 'ipv6', 'esp', 'ah' ]:
                err_msg = _("Invalid port with protocol '%s'") % \
                            (rule.protocol)
                raise UFWError(err_msg)
        elif (nargs + 1) % 2 != 0:
            err_msg = _("Wrong number of arguments")
            raise UFWError(err_msg)
        elif not 'from' in argv and not 'to' in argv and not 'in' in argv and \
             not 'out' 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:
                        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:
                        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:
                        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:
                        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:
                        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:
                    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:
                        err_msg = _("Could not find protocol")
                        raise UFWError(err_msg)
                else:
                    try:
                        tmp = ufw.util.get_services_proto(from_service)
                    except Exception:
                        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)

        # Verify protocol not specified with application rule
        if rule and rule.protocol != "any" and \
           (rule.sapp != "" or rule.dapp != ""):
            err_msg = _("Improper rule syntax ('%s' specified with app rule)") \
                        % (rule.protocol)
            raise UFWError(err_msg)

        if rule.protocol == 'ipv6':
            if type == "v6":
                # Can't use protocol ipv6 with v6 addresses
                err_msg = _("Invalid IPv6 address with protocol '%s'") % \
                            (rule.protocol)
                raise UFWError(err_msg)
            elif type == "both":
                debug("Adjusting iptype to 'v4' for protocol '%s'" % \
                      (rule.protocol))
                type = "v4"

            if rule.dport != "any" or rule.sport != "any":
                # Don't specify a port with ipv6, esp, or ah protocol
                err_msg = _("Invalid port with protocol '%s'") % \
                            (rule.protocol)
                raise UFWError(err_msg)

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

        return r
Пример #20
0
    def match(x, y):
        '''Check if rules match
        Return codes:
          0  match
          1  no match
         -1  match all but action
        '''
        if not x or not y:
            raise ValueError()

        dbg_msg = "No match '%s' '%s'" % (x, y)
        if x.dport != y.dport:
            debug(dbg_msg)
            return 1
        if x.sport != y.sport:
            debug(dbg_msg)
            return 1
        if x.protocol != y.protocol:
            debug(dbg_msg)
            return 1
        if x.src != y.src:
            debug(dbg_msg)
            return 1
        if x.dst != y.dst:
            debug(dbg_msg)
            return 1
        if x.v6 != y.v6:
            debug(dbg_msg)
            return 1
        if x.dapp != y.dapp:
            debug(dbg_msg)
            return 1
        if x.sapp != y.sapp:
            debug(dbg_msg)
            return 1
        if x.interface_in != y.interface_in:
            debug(dbg_msg)
            return 1
        if x.interface_out != y.interface_out:
            debug(dbg_msg)
            return 1
        if x.direction != y.direction:
            debug(dbg_msg)
            return 1
        if x.action == y.action and x.logtype == y.logtype:
            dbg_msg = _("Found exact match")
            debug(dbg_msg)
            return 0

        dbg_msg = _("Found non-action/non-logtype match " \
                    "(%(xa)s/%(ya)s %(xl)s/%(yl)s)") % \
                    ({'xa': x.action, 'ya': y.action, \
                      'xl': x.logtype, 'yl': y.logtype})
        debug(dbg_msg)
        return -1
Пример #21
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 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 != 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' adds rule at end, which is potentially
                # confusing for the end user
                if insert_pos == "0":
                    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]

            action = argv[0]

        if action == "":
            action = self.command
            argv.insert(0, action)

        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)

        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)
        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 UFWError:
                    err_msg = _("Bad port")
                    raise UFWError(err_msg)

                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)

            # Don't specify a port with ipv6, esp or ah protocols
            if rule.protocol in ['ipv6', 'esp', 'ah']:
                err_msg = _("Invalid port with protocol '%s'") % \
                            (rule.protocol)
                raise UFWError(err_msg)
        elif (nargs + 1) % 2 != 0:
            err_msg = _("Wrong number of arguments")
            raise UFWError(err_msg)
        elif not 'from' in argv and not 'to' in argv and not 'in' in argv and \
             not 'out' 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:
                        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:
                        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:
                        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:
                        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:
                        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:
                    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:
                        err_msg = _("Could not find protocol")
                        raise UFWError(err_msg)
                else:
                    try:
                        tmp = ufw.util.get_services_proto(from_service)
                    except Exception:
                        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)

        # Verify protocol not specified with application rule
        if rule and rule.protocol != "any" and \
           (rule.sapp != "" or rule.dapp != ""):
            err_msg = _("Improper rule syntax ('%s' specified with app rule)") \
                        % (rule.protocol)
            raise UFWError(err_msg)

        if rule.protocol == 'ipv6':
            if type == "v6":
                # Can't use protocol ipv6 with v6 addresses
                err_msg = _("Invalid IPv6 address with protocol '%s'") % \
                            (rule.protocol)
                raise UFWError(err_msg)
            elif type == "both":
                debug("Adjusting iptype to 'v4' for protocol '%s'" % \
                      (rule.protocol))
                type = "v4"

            if rule.dport != "any" or rule.sport != "any":
                # Don't specify a port with ipv6, esp, or ah protocol
                err_msg = _("Invalid port with protocol '%s'") % \
                            (rule.protocol)
                raise UFWError(err_msg)

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

        return r
Пример #23
0
    def fuzzy_dst_match(x, y):
        '''This will match if x is more specific than y. Eg, for protocol if x
	   is tcp and y is all or for address if y is a network and x is a
           subset of y (where x is either an address or network). Returns:

            0  match
            1  no match
           -1  fuzzy match

           This is a fuzzy destination match, so source ports or addresses
           are not considered, and (currently) only incoming.
        '''
        def _match_ports(test_p, to_match):
            '''Returns True if p is an exact match or within a multi rule'''
            if ',' in test_p or ':' in test_p:
                if test_p == to_match:
                    return True
                return False

            for port in to_match.split(','):
                if test_p == port:
                    return True
                if ':' in port:
                    (low, high) = port.split(':')
                    if int(test_p) >= int(low) and int(test_p) <= int(high):
                        return True

            return False

        if not x or not y:
            raise ValueError()

        # Ok if exact match
        if x.match(y) == 0:
            return 0

        dbg_msg = "No fuzzy match '%s (v6=%s)' '%s (v6=%s)'" % \
                   (x, x.v6, y, y.v6)

        # Direction must match
        if y.direction != "in":
            debug("(direction) " + dbg_msg + " (not incoming)")
            return 1

        # forward must match
        if y.forward != x.forward:
            debug(dbg_msg + " (forward does not match)")
            return 1

        # Protocols must match or y 'any'
        if x.protocol != y.protocol and y.protocol != "any":
            debug("(protocol) " + dbg_msg)
            return 1

        # Destination ports must match or y 'any'
        if y.dport != "any" and not _match_ports(x.dport, y.dport):
            debug("(dport) " + dbg_msg)
            return 1

        if y.interface_in == "":
	    # If destination interface is not specified, destination addresses
            # must match or x must be contained in y

            if x.interface_in == "" and x._is_anywhere(x.dst):
		# if x and y interfaces are not specified, and x.dst is
                # anywhere then ok
                pass
            elif x.dst != y.dst and '/' not in y.dst:
                debug("(dst) " + dbg_msg)
                return 1
            elif x.dst != y.dst and '/' in y.dst and x.v6 == y.v6 and \
               not ufw.util.in_network(x.dst, y.dst, x.v6):
                debug("(dst) " + dbg_msg + " ('%s' not in network '%s')" % \
                      (x.dst, y.dst))
                return 1
        else:
	    # If destination interface is specified, then:
            #  if specified, both interfaces must match or
            #  the IP of the interface must match the IP of y or
	    #  the IP of the interface must be contained in y
            if x.interface_in != "" and x.interface_in != y.interface_in:
                debug("(interface) " + dbg_msg + " (%s != %s)" % \
                      (x.interface_in, y.interface_in))
                return 1

            try:
                if_ip = ufw.util.get_ip_from_if(y.interface_in, x.v6)
            except IOError:
                debug("(interface) " + dbg_msg + " %s does not exist" % \
                      (y.interface_in))
                return 1

            if y.dst != if_ip and '/' not in y.dst:
                debug("(interface) " + dbg_msg + " (%s != %s)" % \
                      (y.dst, if_ip))
                return 1
            elif y.dst != if_ip and '/' in y.dst and x.v6 == y.v6 and \
               not ufw.util.in_network(if_ip, y.dst, x.v6):
                debug("(interface) " + dbg_msg + \
                      " ('%s' not in network '%s')" % (if_ip, y.dst))
                return 1

        if x.v6 != y.v6:
            debug("(v6) " + dbg_msg + " (%s != %s)" % (x.dst, y.dst))
            return 1

        # if we made it here, it is a fuzzy match
        debug("(fuzzy match) '%s (v6=%s)' '%s (v6=%s)'" % (x, x.v6, y, y.v6))
        return -1
Пример #24
0
    def get_status(self, verbose=False, show_count=False):
        '''Show ufw managed rules'''
        out = ""
        if self.dryrun:
            out = "> " + _("Checking iptables\n")
            if self.use_ipv6():
                out += "> " + _("Checking ip6tables\n")
            return out

        err_msg = _("problem running")
        for direction in ["input", "output"]:
            # Is the firewall loaded at all?
            (rc, out) = cmd([self.iptables, '-L', \
                            'ufw-user-%s' % (direction), '-n'])
            if rc == 1:
                return _("Status: inactive")
            elif rc != 0:
                raise UFWError(err_msg + " iptables: %s\n" % (out))

            if self.use_ipv6():
                (rc, out6) = cmd([self.ip6tables, '-L', \
                                 'ufw6-user-%s' % (direction), '-n'])
                if rc != 0:
                    raise UFWError(err_msg + " ip6tables")

        s = ""
        str_out = ""
        rules = self.rules + self.rules6
        count = 1
        app_rules = {}
        for r in rules:
            tmp_str = ""
            location = {}
            tupl = ""
            show_proto = True
            if not verbose and (r.dapp != "" or r.sapp != ""):
                show_proto = False
                tupl = r.get_app_tuple()

                if app_rules.has_key(tupl):
                    debug("Skipping found tuple '%s'" % (tupl))
                    continue
                else:
                    app_rules[tupl] = True

            for loc in ['dst', 'src']:
                location[loc] = ""

                port = ""
                tmp = ""
                if loc == "dst":
                    tmp = r.dst
                    if not verbose and r.dapp != "":
                        port = r.dapp
                        if r.v6 and tmp == "::/0":
                            port += " (v6)"
                    else:
                        port = r.dport
                else:
                    tmp = r.src
                    if not verbose and r.sapp != "":
                        port = r.sapp
                        if r.v6 and tmp == "::/0":
                            port += " (v6)"
                    else:
                        port = r.sport

                if tmp != "0.0.0.0/0" and tmp != "::/0":
                    location[loc] = tmp

                if port != "any":
                    if location[loc] == "":
                        location[loc] = port
                    else:
                        location[loc] += " " + port

                    if show_proto and r.protocol != "any":
                        location[loc] += "/" + r.protocol

                    if verbose:
                        if loc == "dst" and r.dapp != "":
                            location[loc] += " (%s" % (r.dapp)
                            if r.v6 and tmp == "::/0":
                                location[loc] += " (v6)"
                            location[loc] += ")"
                        if loc == "src" and r.sapp != "":
                            location[loc] += " (%s" % (r.sapp)
                            if r.v6 and tmp == "::/0":
                                location[loc] += " (v6)"
                            location[loc] += ")"

                if port == "any":
                    if tmp == "0.0.0.0/0" or tmp == "::/0":
                        location[loc] = "Anywhere"

                        # Show the protocol if Anywhere to Anwhere, have
                        # protocol and source and dest ports are any
                        if show_proto and r.protocol != "any" and \
                           r.dst == r.src and r.dport == r.sport:
                            location[loc] += "/" + r.protocol

                        if tmp == "::/0":
                            location[loc] += " (v6)"
                    else:
                        # Show the protocol if have protocol, and source
                        # and dest ports are any
                        if show_proto and r.protocol != "any" and \
                           r.dport == r.sport:
                            location[loc] += "/" + r.protocol
                if loc == 'dst' and r.interface_in != "":
                    location[loc] += " on %s" % (r.interface_in)
                if loc == 'src' and r.interface_out != "":
                    location[loc] += " on %s" % (r.interface_out)

            attribs = []
            attrib_str = ""
            if r.logtype or r.direction.lower() == "out":
                if r.logtype:
                    attribs.append(r.logtype.lower())
                if show_count and r.direction == "out":
                    attribs.append(r.direction)
                if len(attribs) > 0:
                    attrib_str = " (%s)" % (', '.join(attribs))

            # now construct the rule output string
            if show_count:
                tmp_str += "[%2d] " % (count)

            dir_str = r.direction.upper()
            if r.direction == "in" and not verbose and not show_count:
                dir_str = ""
            tmp_str += "%-26s %-12s%s%s\n" % (location['dst'], \
                                             " ".join([r.action.upper(), \
                                                       dir_str]), \
                                             location['src'], attrib_str)

            # Show the list in the order given if a numbered list, otherwise
            # split incoming and outgoing rules
            if show_count:
                s += tmp_str
            else:
                if r.direction == "out":
                    str_out += tmp_str
                else:
                    s += tmp_str
            count += 1

        if s != "" or str_out != "":
            full_str = "\n\n"
            if show_count:
                full_str += "     "
            str_to = _("To")
            str_from = _("From")
            str_action = _("Action")
            rules_header = "%-26s %-12s%s\n" % \
                            (str_to.decode("utf-8", 'ignore'), \
                             str_action.decode("utf-8", 'ignore'), \
                             str_from.decode("utf-8", 'ignore'))
            if show_count:
                rules_header += "     "
            rules_header += "%-26s %-12s%s\n" % \
                            ("-" * len(str_to.decode("utf-8", 'ignore')), \
                             "-" * len(str_action.decode("utf-8", 'ignore')), \
                             "-" * len(str_from.decode("utf-8", 'ignore')))
            full_str += rules_header.encode('utf-8', 'ignore')
            if s != "":
                full_str += s
            if s != "" and str_out != "":
                full_str += _("\n")
            if str_out != "":
                full_str += str_out

            s = full_str

        if verbose:
            (level, logging_str) = self.get_loglevel()
            policy_str = _("Default: %(in)s (incoming), %(out)s (outgoing)") \
                           % ({'in': self._get_default_policy(), \
                               'out': self._get_default_policy("output")})
            app_policy_str = self.get_default_application_policy()
            return _("Status: active\n%(log)s\n%(pol)s\n%(app)s%(status)s") % \
                     ({'log': logging_str, 'pol': policy_str, \
                       'app': app_policy_str, 'status': s})
        else:
            return _("Status: active%s") % (s)
Пример #25
0
    def set_rule(self, rule, allow_reload=True):
        """Updates firewall with rule by:
        * appending the rule to the chain if new rule and firewall enabled
        * deleting the rule from the chain if found and firewall enabled
        * inserting the rule if possible and firewall enabled
        * updating user rules file
        * reloading the user rules file if rule is modified
        """
        rstr = ""

        if rule.v6:
            if not self.use_ipv6():
                err_msg = _("Adding IPv6 rule failed: IPv6 not enabled")
                raise UFWError(err_msg)
            if rule.action == "limit":
                # Netfilter doesn't have ip6t_recent yet, so skip
                return _("Skipping unsupported IPv6 '%s' rule") % (rule.action)

        if rule.multi and rule.protocol != "udp" and rule.protocol != "tcp":
            err_msg = _("Must specify 'tcp' or 'udp' with multiple ports")
            raise UFWError(err_msg)

        newrules = []
        found = False
        modified = False
        delete = False

        rules = self.rules
        position = rule.position
        if rule.v6:
            if self.iptables_version < "1.4" and (rule.dapp != "" or rule.sapp != ""):
                return _("Skipping IPv6 application rule. Need at least iptables 1.4")
            rules = self.rules6

        # bail if we have a bad position
        if position < 0 or position > len(rules):
            err_msg = _("Invalid position '%d'") % (position)
            raise UFWError(err_msg)

        if position > 0 and rule.remove:
            err_msg = _("Cannot specify insert and delete")
            raise UFWError(err_msg)
        if position > len(rules):
            err_msg = _("Cannot insert rule at position '%d'") % position
            raise UFWError(err_msg)

        # First construct the new rules list
        try:
            rule.normalize()
        except Exception:
            raise

        count = 1
        inserted = False
        matches = 0
        last = ("", "", "", "")
        for r in rules:
            try:
                r.normalize()
            except Exception:
                raise

            current = (r.dst, r.src, r.dapp, r.sapp)
            if count == position:
                # insert the rule if:
                # 1. the last rule was not an application rule
                # 2. the current rule is not an application rule
                # 3. the last application rule is different than the current
                #    while the new rule is different than the current one
                if (
                    (last[2] == "" and last[3] == "" and count > 1)
                    or (current[2] == "" and current[3] == "")
                    or last != current
                ):
                    inserted = True
                    newrules.append(rule.dup_rule())
                    last = ("", "", "", "")
                else:
                    position += 1
            last = current
            count += 1

            ret = UFWRule.match(r, rule)
            if ret < 1:
                matches += 1

            if ret == 0 and not found and not inserted:
                # If find the rule, add it if it's not to be removed, otherwise
                # skip it.
                found = True
                if not rule.remove:
                    newrules.append(rule.dup_rule())
            elif ret < 0 and not rule.remove and not inserted:
                # If only the action is different, replace the rule if it's not
                # to be removed.
                found = True
                modified = True
                newrules.append(rule.dup_rule())
            else:
                newrules.append(r)

        if inserted:
            if matches > 0:
                rstr = _("Skipping inserting existing rule")
                if rule.v6:
                    rstr += " (v6)"
                return rstr
        else:
            # Add rule to the end if it was not already added.
            if not found and not rule.remove:
                newrules.append(rule.dup_rule())

            # Don't process non-existing or unchanged pre-exisiting rules
            if not found and rule.remove and not self.dryrun:
                rstr = _("Could not delete non-existent rule")
                if rule.v6:
                    rstr += " (v6)"
                return rstr
            elif found and not rule.remove and not modified:
                rstr = _("Skipping adding existing rule")
                if rule.v6:
                    rstr += " (v6)"
                return rstr

        if rule.v6:
            self.rules6 = newrules
        else:
            self.rules = newrules

        # Update the user rules file
        try:
            self._write_rules(rule.v6)
        except UFWError:
            raise
        except Exception:
            err_msg = _("Couldn't update rules file")
            UFWError(err_msg)

        # We wrote out the rules, so set reasonable string. We will change
        # this below when operating on the live firewall.
        rstr = _("Rules updated")
        if rule.v6:
            rstr = _("Rules updated (v6)")

        # Operate on the chains
        if self._is_enabled() and not self.dryrun:
            flag = ""
            if modified or self._need_reload(rule.v6) or inserted:
                rstr = ""
                if inserted:
                    rstr += _("Rule inserted")
                else:
                    rstr += _("Rule updated")
                if rule.v6:
                    rstr += " (v6)"
                if allow_reload:
                    # Reload the chain
                    try:
                        self._reload_user_rules()
                    except Exception:
                        raise
                else:
                    rstr += _(" (skipped reloading firewall)")
            elif found and rule.remove:
                flag = "-D"
                rstr = _("Rule deleted")
            elif not found and not modified and not rule.remove:
                flag = "-A"
                rstr = _("Rule added")

            if flag != "":
                exe = self.iptables
                chain_prefix = "ufw"
                if rule.v6:
                    exe = self.ip6tables
                    chain_prefix = "ufw6"
                    rstr += " (v6)"
                chain_suffix = "input"
                if rule.direction == "out":
                    chain_suffix = "output"
                chain = "%s-user-%s" % (chain_prefix, chain_suffix)

                # Is the firewall running?
                err_msg = _("Could not update running firewall")
                (rc, out) = cmd([exe, "-L", chain, "-n"])
                if rc != 0:
                    raise UFWError(err_msg)

                rule_str = "%s %s %s" % (flag, chain, rule.format_rule())
                pat_log = re.compile(r"(-A +)(ufw6?-user-[a-z\-]+)(.*)")
                for s in self._get_lists_from_formatted(rule_str, chain_prefix, chain_suffix):
                    (rc, out) = cmd([exe] + s)
                    if rc != 0:
                        msg(out, sys.stderr)
                        UFWError(err_msg)

                    # delete any lingering RETURN rules (needed for upgrades)
                    if flag == "-A" and pat_log.search(" ".join(s)):
                        c = pat_log.sub(r"\2", " ".join(s))
                        (rc, out) = cmd([exe, "-D", c, "-j", "RETURN"])
                        if rc != 0:
                            debug("FAILOK: -D %s -j RETURN" % (c))

        return rstr
Пример #26
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
Пример #27
0
    def match(x, y):
        '''Check if rules match
        Return codes:
          0  match
          1  no match
         -1  match all but action
        '''
        if not x or not y:
            raise ValueError()

        dbg_msg = "No match '%s' '%s'" % (x, y)
        if x.dport != y.dport:
            debug(dbg_msg)
            return 1
        if x.sport != y.sport:
            debug(dbg_msg)
            return 1
        if x.protocol != y.protocol:
            debug(dbg_msg)
            return 1
        if x.src != y.src:
            debug(dbg_msg)
            return 1
        if x.dst != y.dst:
            debug(dbg_msg)
            return 1
        if x.v6 != y.v6:
            debug(dbg_msg)
            return 1
        if x.dapp != y.dapp:
            debug(dbg_msg)
            return 1
        if x.sapp != y.sapp:
            debug(dbg_msg)
            return 1
        if x.interface_in != y.interface_in:
            debug(dbg_msg)
            return 1
        if x.interface_out != y.interface_out:
            debug(dbg_msg)
            return 1
        if x.direction != y.direction:
            debug(dbg_msg)
            return 1
        if x.forward != y.forward:
            debug(dbg_msg)
            return 1
        if x.action == y.action and x.logtype == y.logtype:
            dbg_msg = _("Found exact match")
            debug(dbg_msg)
            return 0

        dbg_msg = _("Found non-action/non-logtype match " \
                    "(%(xa)s/%(ya)s %(xl)s/%(yl)s)") % \
                    ({'xa': x.action, 'ya': y.action, \
                      'xl': x.logtype, 'yl': y.logtype})
        debug(dbg_msg)
        return -1
Пример #28
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
Пример #29
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)