Changeset 117

Show
Ignore:
Timestamp:
05/14/05 23:46:29 (4 years ago)
Author:
athomas
Message:
  • Implemented grouping and ordering.
  • More additions to th efwc.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • fwc/trunk/fwc

    r115 r117  
    2222firewall = Firewall("localhost", resolver) 
    2323 
    24 def to_list(value): 
    25         if type(value) is not list: return [value] 
    26         return value 
    27  
    2824def check_port_protocol(context): 
    2925        return 'protocol' in context and context['protocol'] in [ 'tcp', 'udp' ] 
     
    4238        for o in resolver.get_objects('network'): 
    4339                if o.description: 
    44                         help[o.name] = o.description + "(%s)" % o.value 
     40                        help[o.name] = "%s (%s)" % (o.description, o.value) 
    4541                else: 
    4642                        help[o.name] = o.value 
     
    8581# Commands 
    8682  
    87 def insert_rule(context, action, source = [], sport = [], destination = [], dport = [], protocol = None, description = None, where = 'bottom', index = None, state = 'new', log = None): 
     83def insert_rule(context, action, source = [], sport = [], destination = [], dport = [], protocol = None, description = None, where = 'bottom', index = None, state = 'new', log = None, reject_type = None, reject_subtype = None): 
    8884        if log: 
    8985                if log == 'log': 
     
    9288                        log = log[1] 
    9389        if index != None: index = int(index) 
    94         firewall.add(Rule(action, to_list(source), to_list(sport), to_list(destination), to_list(dport), protocol, description, state, log), where, index) 
     90        firewall.add(Rule(action, tolist(source), tolist(sport), tolist(destination), tolist(dport), protocol, description, state, log, reject_type, reject_subtype), where, index) 
    9591 
    9692def remove_rule(context, rules): 
     
    114110                        cmd += " state ^B" + rule.state + "^B" 
    115111                if rule.protocol: 
    116                         cmd += " ^B" + rule.protocol + "^B" 
     112                        try: 
     113                                cmd += ' protocol ^B%s^B' % int(rule.protocol) 
     114                        except: 
     115                                cmd += " ^B" + rule.protocol + "^B" 
    117116                if rule.source or rule.sport: 
    118117                        cmd += " from" 
     
    127126                        if rule.dport: 
    128127                                cmd += " port ^B" + ' '.join(rule.dport) + "^B" 
     128                if rule.reject_type: 
     129                        cmd += " with ^B%s^B" % rule.reject_type 
     130                        if rule.reject_subtype: 
     131                                cmd += " ^B%s^B" % rule.reject_subtype 
    129132                if rule.log: 
    130133                        cmd += " log" 
    131134                        if type(rule.log) is str: 
    132                                 cmd += " ^B'%s'^B" % rule.log 
     135                                cmd += " message ^B'%s'^B" % rule.log 
    133136                if rule.description: 
    134137                        cmd += " description ^B'%s'^B" % rule.description 
     
    143146 
    144147def list_objects(context, type = Resolver.get_object_types()): 
    145         for t in to_list(type): 
     148        for t in tolist(type): 
    146149                objects = resolver.get_objects(t) 
    147                 for o in sorted(objects): 
     150                for o in sorted(objects, lambda a, b: cmp(a.name, b.name)): 
    148151                        str = "%s ^B%s^B ^B^6%s^N" % (o.type, o.name, o.value) 
    149152                        if o.description: str += " ^2'%s^N'" % o.description 
     
    158161placement_rules = { 
    159162        'replace' : { 
    160                 GROUP : 'Rule ordering.'
     163                GROUP : 10
    161164                UNLESS_VAR : [ 'old', 'where' ], 
    162165                VAR : 'where', 
     
    169172        }, 
    170173        'top|bottom' : { 
    171                 GROUP : 'Rule ordering.'
     174                GROUP : 10
    172175                UNLESS_VAR : 'where', 
    173176                VAR : 'where', 
     
    179182        }, 
    180183        'before|after' : { 
    181                 GROUP : 'Rule ordering.'
     184                GROUP : 10
    182185                UNLESS_VAR : 'where', 
    183186                VAR : 'where', 
     
    195198 
    196199protocol_rules = { 
    197         'tcp|udp|icmp|ah|esp' : { 
    198                 GROUP : 'Packet matching.'
     200        lambda ctx, foo: [x.name for x in resolver.get_objects('protocol')] : { 
     201                GROUP : 30
    199202                UNLESS_VAR : 'protocol', 
    200203                VAR : 'protocol', 
    201                 HELP : { 
    202                         'tcp' : 'Match TCP packets.', 
    203                         'udp' : 'Match UDP packets.', 
    204                         'icmp' : 'Match ICMP packets.', 
    205                         'ah' : 'Match AH packets.', 
    206                         'esp' : 'Match ESP packets.', 
    207                 }, 
     204                HELP : lambda x: [(x.name, x.description or 'Protocol %s' % x.value) for x in resolver.get_objects('protocol')], 
    208205                JUMP : RETURN, 
     206        }, 
     207        'protocol' : { 
     208                GROUP : 30, 
     209                HELP : 'Arbitrary IP protocol number.', 
     210                UNLESS_VAR : 'protocol', 
     211                '\d+' : { 
     212                        HELP : ('<protocol>', 'IP protocol number.'), 
     213                        JUMP : RETURN, 
     214                        VAR : 'protocol', 
     215                }, 
    209216        }, 
    210217} 
     
    212219# Grammar 
    213220cli = CLI({ 
     221        'nat' : { 
     222                MERGE : [ placement_rules, protocol_rules ], 
     223                GROUP : 10, 
     224                HELP : 'NAT matching packets.', 
     225                VAR : 'action', 
     226                'to' : { 
     227                        HELP : 'Specify destination to NAT to.', 
     228                        Object.NETWORK_PATTERN : { 
     229                                VAR : 'nat_ip', 
     230                                HELP : ('<ip>', 'NAT to IP address.'), 
     231                                'if' : { 
     232                                        HELP : 'NAT on the following conditions.', 
     233                                        JUMP : 'commands', 
     234                                }, 
     235                        }, 
     236                }, 
     237        }, 
    214238        'accept|drop|reject' : { 
     239                GROUP : 10, 
    215240                IF : have_modifiable_firewall, 
    216241                GLOBAL_LABEL : 'commands', 
     242                MERGE : [ placement_rules, protocol_rules ], 
     243                VAR : 'action', 
     244                ACTION : { 
     245                        HELP : 'Add rule to firewall.', 
     246                        # Only if a source or destination have actually been specified 
     247                        #IF : lambda ctx: 'source' in ctx or 'destination' in ctx, 
     248                        ACTION : insert_rule, 
     249                }, 
     250                HELP : { 
     251                        'accept' : 'Add rule accepting matching packets.', 
     252                        'drop' : 'Add rule dropping matching packets.', 
     253                        'reject' : 'Add rule rejecting matching packets.', 
     254                }, 
     255                'with' : { 
     256                        IF : lambda ctx: 'reject_type' not in ctx and ctx['action'] == 'reject', 
     257                        HELP : 'Reject with the specified packet type.', 
     258                        'tcp' : { 
     259                                VAR : 'reject_type', 
     260                                HELP : 'Reject with tcp... packet.', 
     261                                'reset' : { 
     262                                        VAR : 'reject_subtype', 
     263                                        IF : lambda ctx: 'protocol' in ctx and ctx['protocol'] == 'tcp', 
     264                                        HELP : 'Reject with TCP reset.', 
     265                                        JUMP : 'commands', 
     266                                }, 
     267                        }, 
     268                        'network|host|protocol|admin' : { 
     269                                VAR : 'reject_type', 
     270                                HELP : { 
     271                                        'network' : 'Reject with icmp-network... packet.', 
     272                                        'host' : 'Reject with icmp-host... packet.', 
     273                                        'protocol' : 'Reject with icmp-protocol... packet.', 
     274                                        'admin' : 'Reject with icmp-admin... packet.', 
     275                                }, 
     276                                'unreachable' : { 
     277                                        HELP : lambda ctx: { 'unreachable' : 'Reject with icmp-%s-unreachable.' % ctx['reject_type'] }, 
     278                                        IF : lambda ctx: ctx['reject_type'] != 'admin', 
     279                                        VAR : 'reject_subtype', 
     280                                        JUMP : 'commands', 
     281                                }, 
     282                                'prohibited' : { 
     283                                        HELP : lambda ctx: { 'prohibited' : 'Reject with icmp-%s-prohibited.' % ctx['reject_type'] }, 
     284                                        IF : lambda ctx: ctx['reject_type'] in ('network', 'host', 'admin'), 
     285                                        VAR : 'reject_subtype', 
     286                                        JUMP : 'commands', 
     287                                }, 
     288                        }, 
     289 
     290                }, 
    217291                'state' : { 
    218                         GROUP : 'Packet matching.'
     292                        GROUP : 20
    219293                        RANGE : 1, 
    220294                        HELP : 'Match connections in this state (default: new).', 
     
    230304                        }, 
    231305                }, 
    232                 ACTION : { 
    233                         HELP : 'Add rule to firewall.', 
    234                         # Only if a source or destination have actually been specified 
    235                         #IF : lambda ctx: 'source' in ctx or 'destination' in ctx, 
    236                         ACTION : insert_rule, 
    237                 }, 
    238                 MERGE : [ placement_rules, protocol_rules ], 
    239                 VAR : 'action', 
    240                 HELP : { 
    241                         'accept' : 'Add rule accepting matching packets.', 
    242                         'drop' : 'Add rule dropping matching packets.', 
    243                         'reject' : 'Add rule rejecting matching packets.', 
    244                 }, 
    245306                'from' : { 
    246                         GROUP : 'Packet matching.'
     307                        GROUP : 20
    247308                        HELP : 'Match source network or port.', 
    248309                        IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'sport' in ctx) or not 'source' in ctx, 
     
    252313                                JUMP : 'commands', 
    253314                                JUMP_TO : 'source', 
     315                                GROUP : 20, 
    254316                                'port' : { 
     317                                        GROUP : 20, 
    255318                                        RANGE : 1, 
    256319                                        HELP : 'Match source port.', 
     
    268331                        'port' : { 
    269332                                HELP : 'Match source port.', 
     333                                GROUP : 20, 
    270334                                PORT : { 
    271335                                        HELP : help_port, 
     
    280344                }, 
    281345                'description' : { 
    282                         GROUP : 'Meta information.', 
    283346                        '.+' : { 
    284347                                VAR : 'description', 
     
    290353                }, 
    291354                'to' : { 
     355                        GROUP : 20, 
    292356                        IF : lambda ctx: ('protocol' in ctx and ctx['protocol'] in ['tcp', 'udp'] and not 'dport' in ctx) or not 'destination' in ctx, 
    293357                        UNLESS_VAR : [ 'dport', 'destination' ], 
    294358                        NETWORK : { 
     359                                GROUP : 20, 
    295360                                LABEL : 'destination', 
    296361                                VAR : 'destination', 
     
    298363                                JUMP_TO : 'destination', 
    299364                                'port' : { 
     365                                        GROUP : 20, 
    300366                                        PORT : { 
    301367                                                LABEL : 'dport', 
     
    311377                        }, 
    312378                        'port' : { 
     379                                GROUP : 20, 
    313380                                PORT : { 
    314381                                        LABEL : 'dports', 
     
    325392                'log' : { 
    326393                        VAR : 'log', 
     394                        UNLESS_VAR : 'log', 
    327395                        HELP : "Log a message when this rule matches.", 
    328                         '.+' : { 
    329                                 HELP : ('<message>', 'Custom log message.'), 
    330                                 JUMP : 'commands', 
    331                                 VAR : 'log', 
     396                        'message' : { 
     397                                HELP : 'Log with custom message.', 
     398                                '.+' : { 
     399                                        HELP : ('<message>', 'Custom log message.'), 
     400                                        JUMP : 'commands', 
     401                                        VAR : 'log', 
     402                                }, 
    332403                        }, 
    333404                        JUMP : 'commands', 
     
    342413        }, 
    343414        'object' : { 
    344                 HELP : 'Manage ruleset objects.', 
     415                HELP : 'Ruleset manipulation objects.', 
    345416                'import' : { 
    346                         HELP : 'Import system default objects.', 
     417                        LABEL : 'import', 
     418                        '|'.join(Resolver.get_object_types()) : { 
     419                                VAR : 'import_types', 
     420                                HELP : lambda ctx: [(x, 'Import system %s objects' % x) for x in Resolver.get_object_types()], 
     421                                ACTION : lambda x, import_types: resolver.populate_defaults(tolist(import_types)), 
     422                        }, 
     423                        HELP : 'Import system objects.', 
    347424                        ACTION : lambda x: resolver.populate_defaults(), 
    348425                }, 
     
    388465                'delete' : { 
    389466                        HELP : 'Delete a ruleset object.', 
    390                         'network|port' : { 
     467                        '|'.join(Resolver.get_object_types()) : { 
    391468                                VAR : 'type', 
    392469                                HELP : { 
     
    402479                                ACTION : list_objects, 
    403480                        }, 
    404                         'network|port' : { 
     481                        '|'.join(Resolver.get_object_types()) : { 
    405482                                VAR : 'type', 
    406                                 HELP : { 
    407                                         'network' : 'List network objects.', 
    408                                         'port' : 'List port objects.', 
    409                                 }, 
     483                                HELP : lambda ctx: [(x, 'List %s objects' % x) for x in Resolver.get_object_types()], 
    410484                                ACTION : list_objects, 
    411485                        }, 
     
    413487        }, 
    414488        'delete' : { 
     489                GROUP : 10, 
    415490                IF : have_modifiable_firewall, 
    416491                HELP : 'Remove rule(s) from the ruleset.', 
     
    424499        }, 
    425500        'list' : { 
     501                GROUP : 10, 
    426502                IF : have_firewall, 
    427503                HELP : 'List ruleset.', 
    428504                ACTION : list_ruleset, 
    429505        }, 
    430         HELP : "Commands to manage the ruleset.", 
    431506        'move' : { 
     507                GROUP : 10, 
    432508                IF : have_modifiable_firewall, 
    433509                HELP : 'Move rule.', 
     
    514590def format_help(help): 
    515591        length = 0 
    516         for h in help: 
    517                 if len(h[0]) > length: 
    518                         length = len(h[0]) 
    519         for h in help: 
    520                 cprint("  ^B%s^B %s" % (h[0] + (length - len(h[0])) * ' ', h[1])) 
     592        for group in help: 
     593                for h in help[group]: 
     594                        if len(h[0]) > length: 
     595                                length = len(h[0]) 
     596        count = 0 
     597        for order, group in sorted(help, lambda a, b: cmp(a[0], b[0])): 
     598                if group: 
     599                        cprint("  ^6%s^N" % group) 
     600                elif count: 
     601                        print 
     602                for h in help[(order, group)]: 
     603                        cprint("    ^B%s^B %s" % (h[0] + (length - len(h[0])) * ' ', h[1])) 
     604                count += 1 
    521605 
    522606readline.set_completer(cli_completion) 
  • fwc/trunk/Object.py

    r114 r117  
    33        NETWORK = 'network' 
    44        PORT = 'port' 
     5        PROTOCOL = 'protocol' 
    56 
    67        NETWORK_PATTERN = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?:/\d{1,2})?' 
    78        PORT_PATTERN = r'\d{1,5}' 
     9        PROTOCOL_PATTERN = r'\d{1,3}' 
    810 
    911        # Regex matching a valid object name 
  • fwc/trunk/Resolver.py

    r115 r117  
    33from util import * 
    44 
     5def get_object_types(): 
     6        return ('network', 'port', 'protocol') 
     7 
    58class Resolver: 
     9        """ 
     10                The Resolver class handles the resolution of symbolic network object 
     11                names into actual network objects. This currently includes port objects 
     12                and network objects. 
     13 
     14                The canonical name for objects is in the form: 
     15 
     16                        <type>:<name> 
     17 
     18                eg. port:ssh, network:swapoff.org, etc. 
     19 
     20        """ 
    621        class Error(Exception): pass 
    722        class InvalidObject(Error): pass 
     
    1126                def __init__(self, resolver): 
    1227                        self.__resolver = resolver 
    13                         self.__types = self.__resolver.get_object_types() 
     28                        self.__types = get_object_types() 
    1429                        self.__type = self.__types.pop() 
    1530                        self.__objectit = iter(self.__resolver.get_objects(self.__type)) 
     
    2742        def __init__(self): 
    2843                self.__objects = {} 
    29                 for t in Resolver.get_object_types(): 
     44                for t in get_object_types(): 
    3045                        self.__objects[t] = {} 
    3146 
    3247                # Set up default objects 
    3348                self.add_object(Object(Object.NETWORK, 'any', '0.0.0.0/0', 'Any network address')) 
     49                self.add_object(Object(Object.NETWORK, 'localhost', '127.0.0.1', 'Local host')) 
    3450                self.add_object(Object(Object.PORT, 'any', '0-65535', 'Any port')) 
     51                # Default protocols 
     52                self.add_object(Object(Object.PROTOCOL, 'tcp', '6', 'Transmission Control Protocol')) 
     53                self.add_object(Object(Object.PROTOCOL, 'udp', '17', 'User Datagram Protocol')) 
     54                self.add_object(Object(Object.PROTOCOL, 'icmp', '1', 'Internet Control Message Protocol')) 
    3555 
    3656        def resolve_object(self, type, name): 
     
    4262                return self.resolve_object('network', network) 
    4363 
    44         def resolve_ip(self, ip): 
    45                 return self.resolve_object('ip', ip) 
    46  
    4764        def resolve_port(self, port): 
    4865                return self.resolve_object('port', port) 
     66 
     67        def resolve_protocol(self, protocol): 
     68                return self.resolve_object('protocol', protocol) 
    4969 
    5070        def resolve(self, descriptor): 
     
    6686                        raise Resolver.InvalidObject("Object '%s:%s' not in ruleset" % (object.type, object.name)) 
    6787 
    68         def populate_defaults(self): 
     88        def populate_defaults(self, types = get_object_types()): 
    6989                """ Populate object database from system. """ 
    70                 objects = 0 
    71                 try: 
    72                         for service in open("/etc/services"): 
    73                                 comment = '' 
    74                                 comment_start = service.find('#') 
    75                                 if comment_start != -1: 
    76                                         comment = service[comment_start + 1:].strip() 
    77                                         service = service[:comment_start] 
    78                                 service = service.strip() 
    79                                 if not service: continue 
    80                                 tokens = service.split() 
    81                                 name = tokens.pop(0) 
    82                                 port, proto = tokens.pop(0).split('/') 
    83                                 tokens.insert(0, name) 
    84                                 for token in tokens: 
    85                                         try: 
    86                                                 self.add_object(Object(Object.PORT, token, port, comment)) 
    87                                                 objects += 1 
    88                                         except Resolver.DuplicateObject: 
    89                                                 eport = self.resolve_port(tokens[0]) 
    90                                                 if eport.value != port: 
    91                                                         warning("port object", tokens[0], "already exists with a different port number") 
    92                 except IOError: 
    93                         pass 
    94                 info("added", objects, "port objects") 
    95                 objects = 0 
    96                 try: 
    97                         for host in open("/etc/hosts"): 
    98                                 comment = '' 
    99                                 comment_start = host.find('#') 
    100                                 if comment_start != -1: 
    101                                         comment = host[comment_start + 1:].strip() 
    102                                         host = host[:comment_start].strip() 
    103                                 host = host.strip() 
    104                                 if not host: continue 
    105                                 ip, names = host.split(None, 1) 
    106                                 names = names.split() 
    107                                 for name in names: 
    108                                         try: 
    109                                                 self.add_object(Object(Object.NETWORK, name, ip, comment)) 
    110                                                 objects += 1 
    111                                         except Resolver.DuplicateObject: 
    112                                                 enet = self.resolve_network(name) 
    113                                                 if enet.value != ip: 
    114                                                         warning("network object", tokens[0], "already exists with a different port number") 
    115                 except IOError: 
    116                         pass 
    117                 info("added", objects, "network objects") 
     90                for type in types: 
     91                        objects = 0 
     92                        if type == Object.PORT: 
     93                                try: 
     94                                        for service in open("/etc/services"): 
     95                                                comment = '' 
     96                                                comment_start = service.find('#') 
     97                                                if comment_start != -1: 
     98                                                        comment = service[comment_start + 1:].strip() 
     99                                                        service = service[:comment_start] 
     100                                                service = service.strip() 
     101                                                if not service: continue 
     102                                                tokens = service.split() 
     103                                                name = tokens.pop(0) 
     104                                                port, proto = tokens.pop(0).split('/') 
     105                                                tokens.insert(0, name) 
     106                                                for token in tokens: 
     107                                                        try: 
     108                                                                self.add_object(Object(Object.PORT, token, port, comment)) 
     109                                                                objects += 1 
     110                                                        except Resolver.DuplicateObject: 
     111                                                                eport = self.resolve_port(tokens[0]) 
     112                                                                if eport.value != port: 
     113                                                                        warning("port object", tokens[0], "already exists with a different port number") 
     114                                except IOError: 
     115                                        pass 
     116                                info("added", objects, "port objects") 
     117                        elif type == Object.NETWORK: 
     118                                try: 
     119                                        for host in open("/etc/hosts"): 
     120                                                comment = '' 
     121                                                comment_start = host.find('#') 
     122                                                if comment_start != -1: 
     123                                                        comment = host[comment_start + 1:].strip() 
     124                                                        host = host[:comment_start].strip() 
     125                                                host = host.strip() 
     126                                                if not host: continue 
     127                                                ip, names = host.split(None, 1) 
     128                                                names = names.split() 
     129                                                for name in names: 
     130                                                        try: 
     131                                                                self.add_object(Object(Object.NETWORK, name, ip, comment)) 
     132                                                                objects += 1 
     133                                                        except Resolver.DuplicateObject: 
     134                                                                enet = self.resolve_network(name) 
     135                                                                if enet.value != ip: 
     136                                                                        warning("network object", tokens[0], "already exists with a different port number") 
     137                                except IOError: 
     138                                        pass 
     139                                info("added", objects, "network objects") 
     140                        elif type == Object.PROTOCOL: 
     141                                try: 
     142                                        for protocol in open("/etc/protocols"): 
     143                                                comment = '' 
     144                                                comment_start = protocol.find('#') 
     145                                                if comment_start != -1: 
     146                                                        comment = protocol[comment_start + 1:].strip() 
     147                                                        protocol = protocol[:comment_start].strip() 
     148                                                protocol = protocol.strip() 
     149                                                if not protocol: continue 
     150                                                name, number, aliases = protocol.split(None, 2) 
     151                                                number = int(number) 
     152                                                if number < 0 or number > 255: continue 
     153                                                names = map(str.lower, [name] + aliases.split()) 
     154                                                for name in names: 
     155                                                        try: 
     156                                                                self.add_object(Object(Object.PROTOCOL, name, number, comment)) 
     157                                                                objects += 1 
     158                                                        except Resolver.DuplicateObject: 
     159                                                                eprot = self.resolve_protocol(name) 
     160                                                                if int(eprot.value) != number: 
     161                                                                        warning("protocol object", name, "already exists with a different protocol number", number) 
     162                                except IOError: 
     163                                        pass 
     164                                info("added", objects, "protocol objects") 
    118165 
    119         @classmethod 
    120         def get_object_types(self): 
    121                 return [ 'network', 'port' ] 
     166        @staticmethod 
     167        def get_object_types(): 
     168                global get_object_types 
     169                return get_object_types() 
    122170 
    123171        def get_objects(self, type): 
  • fwc/trunk/Rule.py

    r112 r117  
    11class Rule: 
    2         def __init__(self, action = None, source = [], sport = [], destination = [], dport = [], protocol = [], description = None, state = None, log = None): 
     2        def __init__(self, action = None, source = [], sport = [], destination = [], dport = [], protocol = [], description = None, state = None, log = None, reject_type = None, reject_subtype = None): 
    33                self.action = action 
    44                self.source = source 
     
    1111                self.state = state 
    1212                self.log = log 
    13  
     13                self.reject_type = reject_type 
     14                self.reject_subtype = reject_subtype 
  • fwc/trunk/util.py

    r116 r117  
    813813        """ Print a green notice prefixed by INF. """ 
    814814        cprint("^2^BINF ^B" + ' '.join(map(str, args)) + '^N') 
     815 
     816def tolist(o): 
     817        """ Convert a scalar object into a list or simply return the list/tuple. """ 
     818        if type(o) is list or type(o) is tuple: return list(o) 
     819        return [o]