def verify(self): """ Run some basic verifications on the rule This will e.g. verify that the hosts referred to do exist, services are properly defined and so on. """ # verify server name given if self.server != "": if not Firewall.hosts.has_key(self.server): raise PyromanException("Rule refers to unknown host as server: '%s' at %s" \ % (self.server, self.loginfo)) # verify client name given if self.client != "": if not Firewall.hosts.has_key(self.client): raise PyromanException("Rule refers to unknown host as client: '%s' at %s" \ % (self.client, self.loginfo)) # for services not yet defined, try to autocreate them if self.service != "" and self.service not in Firewall.services: try: s = Service(name=self.service, sports="", dports=self.service, include=None, loginfo=self.loginfo) Firewall.services[self.service] = s except PortInvalidSpec: raise PyromanException("Rule refers to unknown service: '%s' at %s" \ % (self.service, self.loginfo))
def __init__(self, name, sports="", dports="", include="", loginfo=""): """ Create new service object with string parameters sports -- source port specifications like "www/tcp dns/udp" dports -- destination port specifications include -- include rules for other services to be included/aliased loginfo -- reference line number for user error messages """ if name == "" or not Util.verify_name(name, servicename=True): raise PyromanException("service lacking a proper name: '%s' at %s" \ % (name, loginfo)) if Firewall.services.has_key(name): raise PyromanException( "Duplicate service specification: '%s' at %s" % (name, loginfo)) if sports == "" and dports == "" and include == "" and (name != "ANY"): raise PyromanException( "service specification invalid: '%s' at %s" % (name, loginfo)) self.name = name self.loginfo = loginfo try: self.sports = map(lambda p: Port(p), Util.splitter.split(sports)) self.dports = map(lambda p: Port(p), Util.splitter.split(dports)) except PortInvalidSpec, p: raise PyromanException("Service '%s' contains invalid port spec '%s' at %s: %s" \ % (name, p.spec, loginfo, p.err))
def make_chain_name(inface, outface, client, server): """ Generate chain name from interface and host names The maximum length for iptables is 32 chars, this will be checked and an error reported if the name becomes too long. """ if client and client.islocalhost(): if not outface: outface = inface inface = None if server and server.islocalhost(): if not inface: inface = outface outface = None # get ascii names ifname, ofname, cname, sname = ("","","","") if inface: ifname = inface.name if outface: ofname = outface.name if client: cname = client.name if server: sname = server.name chain = "%s_%s_%s_%s" % (ifname, ofname, cname, sname) if len(chain) >= 32: raise PyromanException("Chain name length too long, use shorter nicknames: %s" % chain) return chain
def verify(self): """ Verify that the host is properly specified. Verifies that the interface given was properly defined. """ if not Firewall.interfaces.has_key(self.iface): raise PyromanException("Host '%s' is assigned interface '%s' which is not defined at %s" \ % (self.name, self.iface, self.loginfo))
def verify(self): """ Verify that the service doesn't try to include a service which is not defined. Future versions might want to add a loop detection. """ for i in self.include: if not i == "" and not Firewall.services.has_key(i): raise PyromanException("Service '%s' tries to include undefined '%s' at %s" \ % (self.name, i, self.loginfo))
def generate(self): for c in self.client: for s in self.server: client = Firewall.hosts[c] server = Firewall.hosts[s] # sanity checks, that should be moved to "verify" if not client or not server: raise PyromanException( "Client or server not found for NAT defined at %s" % self.loginfo) if client.iface == server.iface: raise PyromanException( "client interface and server interface match (i.e. cannot NAT!) for NAT defined at %s" % self.loginfo) if self.dir in ["in", "both"]: self.gen_dnat(client, server) if self.dir in ["out", "both"]: self.gen_snat(client, server)
def __init__(self, name, ip, iface, hostname="", loginfo=""): """ Create a new host object, with the given name, IP specification and interface name -- Nickname for the host ip -- IP specification for the host or subnet (e.g. "127.0.0.1 10.0.0.0/24") iface -- Interface nickname this is connected to (only one!) """ # verify and store name if name == "" and not Util.verify_name(name): raise PyromanException("Host '%s' lacking a valid name at %s" \ % (name, iface, loginfo)) if Firewall.hosts.has_key(name): raise PyromanException("Duplicate host specification: '%s' at %s" % (name, loginfo)) self.name = name # verify and store IPs if ip == "": raise PyromanException( "Host '%s' definition lacking IP address at %s" % (name, loginfo)) self.ip = Util.splitter.split(ip) for i in self.ip: if not Util.verify_ipnet(i): raise PyromanException("IP specification '%s' invalid for host '%s' at %s" \ % (i, name, loginfo)) # verify and store interface self.iface = iface if iface == "": raise PyromanException("Host definition '%s' lacking kernel interfaces at %s" \ % (name, loginfo)) # store "real" hostname (which may be longer than nick) # this is used for "localhost detection" self.hostname = hostname # store loginfo self.loginfo = loginfo # register with firewall Firewall.hosts[name] = self
def __init__(self, name, iface, loginfo): """ Create a new interface name -- name for this interface (-group) iface -- kernel interface names, e.g. "eth0 eth1" """ if name == "" or not Util.verify_name(name): raise PyromanException("Interface lacking a valid name (name: %s, iface: %s) at %s" \ % (name, iface, loginfo)) if Firewall.interfaces.has_key(name): raise PyromanException( "Duplicate interface specification: %s at %s" % (name, loginfo)) if iface == "": raise PyromanException("Interface definition lacking kernel interfaces: %s at %s" \ % (name, loginfo)) self.name = name self.iface = Util.splitter.split(iface) self.loginfo = loginfo # register with firewall Firewall.interfaces[name] = self
def __init__(self, target, server, client, service, loginfo): """ Create a new rule, with given action, source, destination and service target -- the action if the rule matches, e.g. do_accept server -- host the packets are addressed to client -- host the packets originate from service -- service (i.e. ports) the packages use loginfo -- user information about origin of this rule for errors """ # store parameters for later use self.target = target self.server = server self.client = client self.service = service self.loginfo = loginfo if not server and not client: raise PyromanException( "Rules need at least a server or a client at %s" % loginfo)
def get_filter(self, dir, v4v6): """ Generate filter rules for this service by generating a list of filter rules for all source and destination port combinations dir -- either "d" or "s" for destination filter or source filter v4v6 -- either 4 or 6 for IPv4 or IPv6 """ # set 1 and 2 to source/dest filter characters if dir == "d": dir1 = "s" dir2 = "d" elif dir == "s": dir1 = "d" dir2 = "s" else: raise PyromanException("Invalid direction specified: %s" % dir) result = [] for sp in self.sports: if v4v6 == 4 and not sp.forIPv4(): continue if v4v6 == 6 and not sp.forIPv6(): continue for dp in self.dports: if v4v6 == 4 and not dp.forIPv4(): continue if v4v6 == 6 and not dp.forIPv6(): continue # only generate rules when source and destination protocol match if not sp.proto or not dp.proto or sp.proto == dp.proto: f1 = "" if sp.proto: f1 = sp.get_filter_proto() + " " elif dp.proto: f1 = dp.get_filter_proto() + " " f2 = sp.get_filter_port(dir1) f3 = dp.get_filter_port(dir2) if f2 != "" or f3 != "": result.append(f1 + " " + f2 + " " + f3) for i in self.include: result.extend(i.get_filter(dir, v4v6)) return result
def get_filter(self, dir): """ Generate filter rules for this interface by generating a list of filter rules for all source specifications dir -- either "d" or "s" for destination filter or source filter """ idir = None if dir == "d": idir = "o" elif dir == "s": idir = "i" else: raise PyromanException("Unknown direction specified: %s" % dir) # when necessary, turn around filter directions result = [] for i in self.iface: if i == "": continue if i == "*": result.append("") else: result.append("-%s %s" % (idir, i)) return result
def get_chain(inface, outface, client, server, loginfo): """ Make a chain for the given hosts and add it to the interface chain """ chain = Chain.make_chain_name(inface, outface, client, server) if not Firewall.chains.has_key(chain): c = Chain(chain, loginfo) parent = Firewall.forward # if we are talking about localhost, things are different... if client and client.islocalhost(): parent = Firewall.output if not outface: outface = inface inface = None if server and server.islocalhost(): parent = Firewall.input if not inface: inface = outface outface = None if not Firewall.chains.has_key(parent): raise PyromanException("Unknown chain specified: %s" % parent) p = Firewall.chains[parent] # this is localhost talking to localhost... if server and server.islocalhost() and client and client.islocalhost(): raise PyromanException("Localhost talking to localhost?") crules4 = [""] crules6 = [""] srules4 = [""] srules6 = [""] ifrules = [""] ofrules = [""] if client: crules4 = client.get_filter4("s") crules6 = client.get_filter6("s") if server: srules4 = server.get_filter4("d") srules6 = server.get_filter6("d") if inface: ifrules = inface.get_filter("s") if outface: ofrules = outface.get_filter("d") for cr in crules4: for sr in srules4: for infi in ifrules: for outfi in ofrules: filter = "%s %s %s %s -j %s" % (infi, outfi, cr, sr, chain) p.append4(filter, loginfo) for cr in crules6: for sr in srules6: for infi in ifrules: for outfi in ofrules: filter = "%s %s %s %s -j %s" % (infi, outfi, cr, sr, chain) p.append6(filter, loginfo) Firewall.chains[chain]=c return c else: return Firewall.chains[chain]
def __init__(self, client, server, ip, port, dport, dir, loginfo): """ Create a new NAT rule client -- clients allowed to access this NAT rule server -- host nick the NAT is applied to ip -- IP that is used in NAT port -- Ports that are used in NAT dport -- Destination port for single port redirections dir -- incoming, outgoing or bidirecitonal NAT Note that the NAT is always applied to the "server" host, the UI accessible function is responsible to eventually exchange client and server for "outgoing" NATs (where the naming of client, server makes more sense the other way, think of workstations accessing web server via a NAT) """ if server == "": raise PyromanException( "Nat lacking a server host (client: %s, server: %s, ip: %s) at %s" % (client, server, ip, loginfo)) if ip == "": raise PyromanException( "Nat lacking IP address: (client: %s, server: %s) at %s" % (client, server, loginfo)) if dir not in ["in", "out", "both"]: raise PyromanException( "Nat with invalid direction: (client: %s, server: %s, ip: %s, dir: %s) at %s" % (client, server, ip, dir, loginfo)) if not Util.verify_ip4(ip): raise PyromanException( "Nat with invalid IP address: (client: %s, server: %s, ip: %s) at %s" % (client, server, ip, loginfo)) if port: try: self.port = Port(port) except PortInvalidSpec: raise PyromanException( "Nat port specification invalid: (client: %s, server: %s, ip: %s, port: %s) at %s " % (client, server, ip, port, loginfo)) if not self.port.forIPv4(): raise PyromanException("Non-IPv4 port specified: " + port) else: self.port = None if dport: try: self.dport = Port(dport) except PortInvalidSpec: raise PyromanException( "Nat dport specification invalid: (client: %s, server: %s, ip: %s, port: %s, dport: %s) at %s " % (client, server, ip, port, dport, loginfo)) if not self.dport.forIPv4(): raise PyromanException("Non-IPv4 port specified: " + dport) else: self.dport = None if self.dport and not (self.port.proto == self.dport.proto): raise PyromanException( "Nat ports have different protocols: (client: %s, server: %s, ip: %s, port: %s, dport: %s) at %s" % (client, server, ip, port, dport, loginfo)) if dport and not port: raise PyromanException( "Nat with destination port, but no source port: (client: %s, server: %s, ip: %s, dport: %s) at %s" % (client, server, ip, dport, loginfo)) self.client = Util.splitter.split(client) self.server = Util.splitter.split(server) self.ip = ip # port, dport are set above self.dir = dir self.loginfo = loginfo
def __init__(self): """ Dummy initialization function, will raise PyromanException(an exception!) """ raise PyromanException("Instanciation not supported!")