~ubuntu-branches/ubuntu/saucy/ufw/saucy-proposed

« back to all changes in this revision

Viewing changes to .pc/0003-fix-typeerror-on-error.patch/src/backend_iptables.py

  • Committer: Package Import Robot
  • Author(s): Jamie Strandboge
  • Date: 2012-09-24 08:52:57 UTC
  • Revision ID: package-import@ubuntu.com-20120924085257-uqiulz242yd4ab2a
Tags: 0.33-0ubuntu2
* debian/patches/0002-lp1044361.patch: move netfilter capabilities checking
  into initcaps(), and call initcaps() only when we need it (LP: #1044361)
* 0003-fix-typeerror-on-error.patch: fix TypeError on error when using zh_CN

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
'''backend_iptables.py: iptables backend for ufw'''
 
2
#
 
3
# Copyright 2008-2012 Canonical Ltd.
 
4
#
 
5
#    This program is free software: you can redistribute it and/or modify
 
6
#    it under the terms of the GNU General Public License version 3,
 
7
#    as published by the Free Software Foundation.
 
8
#
 
9
#    This program is distributed in the hope that it will be useful,
 
10
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
#    GNU General Public License for more details.
 
13
#
 
14
#    You should have received a copy of the GNU General Public License
 
15
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
16
#
 
17
 
 
18
import os
 
19
import re
 
20
import shutil
 
21
import stat
 
22
import sys
 
23
import time
 
24
 
 
25
from ufw.common import UFWError, UFWRule, config_dir, state_dir
 
26
from ufw.util import warn, debug, msg, cmd, cmd_pipe
 
27
import ufw.backend
 
28
 
 
29
 
 
30
class UFWBackendIptables(ufw.backend.UFWBackend):
 
31
    '''Instance class for UFWBackend'''
 
32
    def __init__(self, dryrun):
 
33
        '''UFWBackendIptables initialization'''
 
34
        self.comment_str = "# " + ufw.common.programName + "_comment #"
 
35
 
 
36
        files = {}
 
37
        files['rules'] = os.path.join(state_dir, 'user.rules')
 
38
        files['before_rules'] = os.path.join(config_dir, 'ufw/before.rules')
 
39
        files['after_rules'] = os.path.join(config_dir, 'ufw/after.rules')
 
40
        files['rules6'] = os.path.join(state_dir, 'user6.rules')
 
41
        files['before6_rules'] = os.path.join(config_dir, 'ufw/before6.rules')
 
42
        files['after6_rules'] = os.path.join(config_dir, 'ufw/after6.rules')
 
43
        files['init'] = os.path.join(state_dir, 'ufw-init')
 
44
 
 
45
        ufw.backend.UFWBackend.__init__(self, "iptables", dryrun, files)
 
46
 
 
47
        self.chains = {'before': [], 'user': [], 'after': [], 'misc': []}
 
48
        for ver in ['4', '6']:
 
49
            chain_prefix = "ufw"
 
50
            if ver == "6":
 
51
                if self.use_ipv6():
 
52
                    chain_prefix += ver
 
53
                elif ver == "6":
 
54
                    continue
 
55
 
 
56
            for loc in ['before', 'user', 'after']:
 
57
                for target in ['input', 'output', 'forward']:
 
58
                    chain = "%s-%s-logging-%s" % (chain_prefix, loc, target)
 
59
                    self.chains[loc].append(chain)
 
60
            self.chains['misc'].append(chain_prefix + "-logging-deny")
 
61
            self.chains['misc'].append(chain_prefix + "-logging-allow")
 
62
 
 
63
        # The default log rate limiting rule ('ufw[6]-user-limit chain should
 
64
        # be prepended before use)
 
65
        self.ufw_user_limit_log = ['-m', 'limit', \
 
66
                                   '--limit', '3/minute', '-j', 'LOG', \
 
67
                                   '--log-prefix']
 
68
        self.ufw_user_limit_log_text = "[UFW LIMIT BLOCK]"
 
69
 
 
70
    def get_default_application_policy(self):
 
71
        '''Get current policy'''
 
72
        rstr = _("New profiles:")
 
73
        if self.defaults['default_application_policy'] == "accept":
 
74
            rstr += " allow"
 
75
        elif self.defaults['default_application_policy'] == "drop":
 
76
            rstr += " deny"
 
77
        elif self.defaults['default_application_policy'] == "reject":
 
78
            rstr += " reject"
 
79
        else:
 
80
            rstr += " skip"
 
81
 
 
82
        return rstr
 
83
 
 
84
    def set_default_policy(self, policy, direction):
 
85
        '''Sets default policy of firewall'''
 
86
        if not self.dryrun:
 
87
            if policy != "allow" and policy != "deny" and policy != "reject":
 
88
                err_msg = _("Unsupported policy '%s'") % (policy)
 
89
                raise UFWError(err_msg)
 
90
 
 
91
            if direction != "incoming" and direction != "outgoing":
 
92
                err_msg = _("Unsupported policy for direction '%s'") % \
 
93
                            (direction)
 
94
                raise UFWError(err_msg)
 
95
 
 
96
            chain = "INPUT"
 
97
            if direction == "outgoing":
 
98
                chain = "OUTPUT"
 
99
 
 
100
            old_log_str = ''
 
101
            new_log_str = ''
 
102
            if policy == "allow":
 
103
                try:
 
104
                    self.set_default(self.files['defaults'], \
 
105
                                            "DEFAULT_%s_POLICY" % (chain), \
 
106
                                            "\"ACCEPT\"")
 
107
                except Exception:
 
108
                    raise
 
109
                old_log_str = 'UFW BLOCK'
 
110
                new_log_str = 'UFW ALLOW'
 
111
            elif policy == "reject":
 
112
                try:
 
113
                    self.set_default(self.files['defaults'], \
 
114
                                            "DEFAULT_%s_POLICY" % (chain), \
 
115
                                            "\"REJECT\"")
 
116
                except Exception:
 
117
                    raise
 
118
                old_log_str = 'UFW ALLOW'
 
119
                new_log_str = 'UFW BLOCK'
 
120
            else:
 
121
                try:
 
122
                    self.set_default(self.files['defaults'], \
 
123
                                            "DEFAULT_%s_POLICY" % (chain), \
 
124
                                            "\"DROP\"")
 
125
                except Exception:
 
126
                    raise
 
127
                old_log_str = 'UFW ALLOW'
 
128
                new_log_str = 'UFW BLOCK'
 
129
 
 
130
            # Switch logging message in catch-all rules
 
131
            pat = re.compile(r'' + old_log_str)
 
132
            for f in [self.files['after_rules'], self.files['after6_rules']]:
 
133
                try:
 
134
                    fns = ufw.util.open_files(f)
 
135
                except Exception:
 
136
                    raise
 
137
                fd = fns['tmp']
 
138
 
 
139
                for line in fns['orig']:
 
140
                    if pat.search(line):
 
141
                        ufw.util.write_to_file(fd, pat.sub(new_log_str, line))
 
142
                    else:
 
143
                        ufw.util.write_to_file(fd, line)
 
144
 
 
145
                try:
 
146
                    ufw.util.close_files(fns)
 
147
                except Exception:
 
148
                    raise
 
149
 
 
150
        rstr = _("Default %(direction)s policy changed to '%(policy)s'\n") % \
 
151
                 ({'direction': direction, 'policy': policy})
 
152
        rstr += _("(be sure to update your rules accordingly)")
 
153
 
 
154
        return rstr
 
155
 
 
156
    def get_running_raw(self, rules_type):
 
157
        '''Show current running status of firewall'''
 
158
        if self.dryrun:
 
159
            out = "> " + _("Checking raw iptables\n")
 
160
            out += "> " + _("Checking raw ip6tables\n")
 
161
            return out
 
162
 
 
163
        # Initialize the capabilities database
 
164
        self.initcaps()
 
165
 
 
166
        args = ['-n', '-v', '-x', '-L']
 
167
        items = []
 
168
        items6 = []
 
169
 
 
170
        if rules_type == "raw":
 
171
            args.append('-t')
 
172
            items = ['filter', 'nat', 'mangle', 'raw']
 
173
            items6 = ['filter', 'mangle', 'raw']
 
174
        elif rules_type == "builtins":
 
175
            for c in ['INPUT', 'FORWARD', 'OUTPUT']:
 
176
                items.append('filter:%s' % c)
 
177
                items6.append('filter:%s' % c)
 
178
            for c in ['PREROUTING', 'INPUT', 'FORWARD', 'OUTPUT', \
 
179
                      'POSTROUTING']:
 
180
                items.append('mangle:%s' % c)
 
181
                items6.append('mangle:%s' % c)
 
182
            for c in ['PREROUTING', 'OUTPUT']:
 
183
                items.append('raw:%s' % c)
 
184
                items6.append('raw:%s' % c)
 
185
            for c in ['PREROUTING', 'POSTROUTING', 'OUTPUT']:
 
186
                items.append('nat:%s' % c)
 
187
        elif rules_type == "before":
 
188
            for b in ['input', 'forward', 'output']:
 
189
                items.append('ufw-before-%s' % b)
 
190
                items6.append('ufw6-before-%s' % b)
 
191
        elif rules_type == "user":
 
192
            for b in ['input', 'forward', 'output']:
 
193
                items.append('ufw-user-%s' % b)
 
194
                items6.append('ufw6-user-%s' % b)
 
195
            if self.caps['limit']['4']:
 
196
                items.append('ufw-user-limit-accept')
 
197
                items.append('ufw-user-limit')
 
198
            if self.caps['limit']['6']:
 
199
                items6.append('ufw6-user-limit-accept')
 
200
                items6.append('ufw6-user-limit')
 
201
        elif rules_type == "after":
 
202
            for b in ['input', 'forward', 'output']:
 
203
                items.append('ufw-after-%s' % b)
 
204
                items6.append('ufw6-after-%s' % b)
 
205
        elif rules_type == "logging":
 
206
            for b in ['input', 'forward', 'output']:
 
207
                items.append('ufw-before-logging-%s' % b)
 
208
                items6.append('ufw6-before-logging-%s' % b)
 
209
                items.append('ufw-user-logging-%s' % b)
 
210
                items6.append('ufw6-user-logging-%s' % b)
 
211
                items.append('ufw-after-logging-%s' % b)
 
212
                items6.append('ufw6-after-logging-%s' % b)
 
213
            items.append('ufw-logging-allow')
 
214
            items.append('ufw-logging-deny')
 
215
            items6.append('ufw6-logging-allow')
 
216
            items6.append('ufw6-logging-deny')
 
217
 
 
218
        out = "IPV4 (%s):\n" % (rules_type)
 
219
        for i in items:
 
220
            if ':' in i:
 
221
                (t, c) = i.split(':')
 
222
                out += "(%s) " % (t)
 
223
                (rc, tmp) = cmd([self.iptables] + args + [c, '-t', t])
 
224
            else:
 
225
                (rc, tmp) = cmd([self.iptables] + args + [i])
 
226
            out += tmp
 
227
            if rules_type != "raw":
 
228
                out += "\n"
 
229
            if rc != 0:
 
230
                raise UFWError(out)
 
231
 
 
232
        if rules_type == "raw" or self.use_ipv6():
 
233
            out += "\n\nIPV6:\n"
 
234
            for i in items6:
 
235
                if ':' in i:
 
236
                    (t, c) = i.split(':')
 
237
                    out += "(%s) " % (t)
 
238
                    (rc, tmp) = cmd([self.iptables] + args + [c, '-t', t])
 
239
                else:
 
240
                    (rc, tmp) = cmd([self.ip6tables] + args + [i])
 
241
                out += tmp
 
242
                if rules_type != "raw":
 
243
                    out += "\n"
 
244
                if rc != 0:
 
245
                    raise UFWError(out)
 
246
 
 
247
        return out
 
248
 
 
249
    def get_status(self, verbose=False, show_count=False):
 
250
        '''Show ufw managed rules'''
 
251
        out = ""
 
252
        if self.dryrun:
 
253
            out = "> " + _("Checking iptables\n")
 
254
            if self.use_ipv6():
 
255
                out += "> " + _("Checking ip6tables\n")
 
256
            return out
 
257
 
 
258
        err_msg = _("problem running")
 
259
        for direction in ["input", "output"]:
 
260
            # Is the firewall loaded at all?
 
261
            (rc, out) = cmd([self.iptables, '-L', \
 
262
                            'ufw-user-%s' % (direction), '-n'])
 
263
            if rc == 1:
 
264
                return _("Status: inactive")
 
265
            elif rc != 0:
 
266
                raise UFWError(err_msg + " iptables: %s\n" % (out))
 
267
 
 
268
            if self.use_ipv6():
 
269
                (rc, out6) = cmd([self.ip6tables, '-L', \
 
270
                                 'ufw6-user-%s' % (direction), '-n'])
 
271
                if rc != 0:
 
272
                    raise UFWError(err_msg + " ip6tables")
 
273
 
 
274
        s = ""
 
275
        str_out = ""
 
276
        rules = self.rules + self.rules6
 
277
        count = 1
 
278
        app_rules = {}
 
279
        for r in rules:
 
280
            tmp_str = ""
 
281
            location = {}
 
282
            tupl = ""
 
283
            show_proto = True
 
284
            if not verbose and (r.dapp != "" or r.sapp != ""):
 
285
                show_proto = False
 
286
                tupl = r.get_app_tuple()
 
287
 
 
288
                if tupl in app_rules:
 
289
                    debug("Skipping found tuple '%s'" % (tupl))
 
290
                    continue
 
291
                else:
 
292
                    app_rules[tupl] = True
 
293
 
 
294
            for loc in [ 'dst', 'src' ]:
 
295
                location[loc] = ""
 
296
 
 
297
                port = ""
 
298
                tmp = ""
 
299
                if loc == "dst":
 
300
                    tmp = r.dst
 
301
                    if not verbose and r.dapp != "":
 
302
                        port = r.dapp
 
303
                        if r.v6 and tmp == "::/0":
 
304
                            port += " (v6)"
 
305
                    else:
 
306
                        port = r.dport
 
307
                else:
 
308
                    tmp = r.src
 
309
                    if not verbose and r.sapp != "":
 
310
                        port = r.sapp
 
311
                        if r.v6 and tmp == "::/0":
 
312
                            port += " (v6)"
 
313
                    else:
 
314
                        port = r.sport
 
315
 
 
316
                if tmp != "0.0.0.0/0" and tmp != "::/0":
 
317
                    location[loc] = tmp
 
318
 
 
319
                if port != "any":
 
320
                    if location[loc] == "":
 
321
                        location[loc] = port
 
322
                    else:
 
323
                        location[loc] += " " + port
 
324
 
 
325
                    if show_proto and r.protocol != "any":
 
326
                        location[loc] += "/" + r.protocol
 
327
 
 
328
                    if verbose:
 
329
                        if loc == "dst" and r.dapp != "":
 
330
                            location[loc] += " (%s" % (r.dapp)
 
331
                            if r.v6 and tmp == "::/0":
 
332
                                location[loc] += " (v6)"
 
333
                            location[loc] += ")"
 
334
                        if loc == "src" and r.sapp != "":
 
335
                            location[loc] += " (%s" % (r.sapp)
 
336
                            if r.v6 and tmp == "::/0":
 
337
                                location[loc] += " (v6)"
 
338
                            location[loc] += ")"
 
339
 
 
340
                if port == "any":
 
341
                    if tmp == "0.0.0.0/0" or tmp == "::/0":
 
342
                        location[loc] = "Anywhere"
 
343
 
 
344
                        # Show the protocol if Anywhere to Anwhere, have
 
345
                        # protocol and source and dest ports are any
 
346
                        if show_proto and r.protocol != "any" and \
 
347
                           r.dst == r.src and r.dport == r.sport:
 
348
                            location[loc] += "/" + r.protocol
 
349
 
 
350
                        if tmp == "::/0":
 
351
                            location[loc] += " (v6)"
 
352
                    else:
 
353
                        # Show the protocol if have protocol, and source
 
354
                        # and dest ports are any
 
355
                        if show_proto and r.protocol != "any" and \
 
356
                           r.dport == r.sport:
 
357
                            location[loc] += "/" + r.protocol
 
358
                if loc == 'dst' and r.interface_in != "":
 
359
                    location[loc] += " on %s" % (r.interface_in)
 
360
                if loc == 'src' and r.interface_out != "":
 
361
                    location[loc] += " on %s" % (r.interface_out)
 
362
 
 
363
            attribs = []
 
364
            attrib_str = ""
 
365
            if r.logtype or r.direction.lower() == "out":
 
366
                if r.logtype:
 
367
                    attribs.append(r.logtype.lower())
 
368
                if show_count and r.direction == "out":
 
369
                    attribs.append(r.direction)
 
370
                if len(attribs) > 0:
 
371
                    attrib_str = " (%s)" % (', '.join(attribs))
 
372
 
 
373
            # now construct the rule output string
 
374
            if show_count:
 
375
                tmp_str += "[%2d] " % (count)
 
376
 
 
377
            dir_str = r.direction.upper()
 
378
            if r.direction == "in" and not verbose and not show_count:
 
379
                dir_str = ""
 
380
            tmp_str += "%-26s %-12s%s%s\n" % (location['dst'], \
 
381
                                             " ".join([r.action.upper(), \
 
382
                                                       dir_str]), \
 
383
                                             location['src'], attrib_str)
 
384
 
 
385
            # Show the list in the order given if a numbered list, otherwise
 
386
            # split incoming and outgoing rules
 
387
            if show_count:
 
388
                s += tmp_str
 
389
            else:
 
390
                if r.direction == "out":
 
391
                    str_out += tmp_str
 
392
                else:
 
393
                    s += tmp_str
 
394
            count += 1
 
395
 
 
396
        if s != "" or str_out != "":
 
397
            full_str = "\n\n"
 
398
            if show_count:
 
399
                full_str += "     "
 
400
            str_to = _("To")
 
401
            str_from = _("From")
 
402
            str_action = _("Action")
 
403
            rules_header_fmt = "%-26s %-12s%s\n"
 
404
 
 
405
            rules_header = rules_header_fmt % (str_to, str_action, str_from)
 
406
            if show_count:
 
407
                rules_header += "     "
 
408
            rules_header += rules_header_fmt % \
 
409
                            ("-" * len(str_to), \
 
410
                             "-" * len(str_action), \
 
411
                             "-" * len(str_from))
 
412
 
 
413
            full_str += rules_header
 
414
 
 
415
            if s != "":
 
416
                full_str += s
 
417
            if s != "" and str_out != "":
 
418
                full_str += _("\n")
 
419
            if str_out != "":
 
420
                full_str += str_out
 
421
 
 
422
            s = full_str
 
423
 
 
424
        if verbose:
 
425
            (level, logging_str) = self.get_loglevel()
 
426
            policy_str = _("Default: %(in)s (incoming), %(out)s (outgoing)") \
 
427
                           % ({'in': self._get_default_policy(), \
 
428
                               'out': self._get_default_policy("output")})
 
429
            app_policy_str = self.get_default_application_policy()
 
430
            return _("Status: active\n%(log)s\n%(pol)s\n%(app)s%(status)s") % \
 
431
                     ({'log': logging_str, 'pol': policy_str, \
 
432
                       'app': app_policy_str, 'status': s})
 
433
        else:
 
434
            return _("Status: active%s") % (s)
 
435
 
 
436
    def stop_firewall(self):
 
437
        '''Stop the firewall'''
 
438
        if self.dryrun:
 
439
            msg("> " + _("running ufw-init"))
 
440
        else:
 
441
            (rc, out) = cmd([self.files['init'], 'force-stop'])
 
442
            if rc != 0:
 
443
                err_msg = _("problem running ufw-init\n%s" % out)
 
444
                raise UFWError(err_msg)
 
445
 
 
446
    def start_firewall(self):
 
447
        '''Start the firewall'''
 
448
        if self.dryrun:
 
449
            msg("> " + _("running ufw-init"))
 
450
        else:
 
451
            (rc, out) = cmd([self.files['init'], 'start'])
 
452
            if rc != 0:
 
453
                err_msg = _("problem running ufw-init\n%s" % out)
 
454
                raise UFWError(err_msg)
 
455
 
 
456
            if 'loglevel' not in self.defaults or \
 
457
               self.defaults['loglevel'] not in list(self.loglevels.keys()):
 
458
                # Add the loglevel if not valid
 
459
                try:
 
460
                    self.set_loglevel("low")
 
461
                except Exception:
 
462
                    err_msg = _("Could not set LOGLEVEL")
 
463
                    raise UFWError(err_msg)
 
464
            else:
 
465
                try:
 
466
                    self.update_logging(self.defaults['loglevel'])
 
467
                except Exception:
 
468
                    err_msg = _("Could not load logging rules")
 
469
                    raise UFWError(err_msg)
 
470
 
 
471
    def _need_reload(self, v6):
 
472
        '''Check if all chains exist'''
 
473
        if self.dryrun:
 
474
            return False
 
475
 
 
476
        # Initialize the capabilities database
 
477
        self.initcaps()
 
478
 
 
479
        prefix = "ufw"
 
480
        exe = self.iptables
 
481
        if v6:
 
482
            prefix = "ufw6"
 
483
            exe = self.ip6tables
 
484
 
 
485
        for chain in [ 'input', 'output', 'forward', 'limit', 'limit-accept' ]:
 
486
            if chain == "limit" or chain == "limit-accept":
 
487
                if v6 and not self.caps['limit']['6']:
 
488
                    continue
 
489
                elif not v6 and not self.caps['limit']['4']:
 
490
                    continue
 
491
 
 
492
            (rc, out) = cmd([exe, '-n', '-L', prefix + "-user-" + chain])
 
493
            if rc != 0:
 
494
                debug("_need_reload: forcing reload")
 
495
                return True
 
496
 
 
497
        return False
 
498
 
 
499
    def _reload_user_rules(self):
 
500
        '''Reload firewall rules file'''
 
501
        err_msg = _("problem running")
 
502
        if self.dryrun:
 
503
            msg("> | iptables-restore")
 
504
            if self.use_ipv6():
 
505
                msg("> | ip6tables-restore")
 
506
        elif self.is_enabled():
 
507
            # first flush the user logging chains
 
508
            try:
 
509
                for c in self.chains['user']:
 
510
                    self._chain_cmd(c, ['-F', c])
 
511
                    self._chain_cmd(c, ['-Z', c])
 
512
            except Exception:
 
513
                raise UFWError(err_msg)
 
514
 
 
515
            # then restore the system rules
 
516
            (rc, out) = cmd_pipe(['cat', self.files['rules']], \
 
517
                                 [self.iptables_restore, '-n'])
 
518
            if rc != 0:
 
519
                raise UFWError(err_msg + " iptables")
 
520
 
 
521
            if self.use_ipv6():
 
522
                (rc, out) = cmd_pipe(['cat', self.files['rules6']], \
 
523
                                     [self.ip6tables_restore, '-n'])
 
524
                if rc != 0:
 
525
                    raise UFWError(err_msg + " ip6tables")
 
526
 
 
527
    def _get_rules_from_formatted(self, frule, prefix, suffix):
 
528
        '''Return list of iptables rules appropriate for sending'''
 
529
        snippets = []
 
530
 
 
531
        # adjust reject and protocol 'all'
 
532
        pat_proto = re.compile(r'-p all ')
 
533
        pat_port = re.compile(r'port ')
 
534
        pat_reject = re.compile(r'-j (REJECT(_log(-all)?)?)')
 
535
        if pat_proto.search(frule):
 
536
            if pat_port.search(frule):
 
537
                if pat_reject.search(frule):
 
538
                    snippets.append(pat_proto.sub('-p tcp ', \
 
539
                        pat_reject.sub(r'-j \1 --reject-with tcp-reset', \
 
540
                        frule)))
 
541
                else:
 
542
                    snippets.append(pat_proto.sub('-p tcp ', frule))
 
543
                snippets.append(pat_proto.sub('-p udp ', frule))
 
544
            else:
 
545
                snippets.append(pat_proto.sub('', frule))
 
546
        else:
 
547
            snippets.append(frule)
 
548
 
 
549
        # adjust for logging rules
 
550
        pat_log = re.compile(r'(.*)-j ([A-Z]+)_log(-all)?(.*)')
 
551
        pat_logall = re.compile(r'-j [A-Z]+_log-all')
 
552
        pat_chain = re.compile(r'(-A|-D) ([a-zA-Z0-9\-]+)')
 
553
        limit_args = '-m limit --limit 3/min --limit-burst 10'
 
554
        for i, s in enumerate(snippets):
 
555
            if pat_log.search(s):
 
556
                policy = pat_log.sub(r'\2', s).strip()
 
557
                if policy.lower() == "accept":
 
558
                    policy = "ALLOW"
 
559
                elif policy.lower() == "limit":
 
560
                    policy = "LIMIT"
 
561
                else:
 
562
                    policy = "BLOCK"
 
563
 
 
564
                lstr = '%s -j LOG --log-prefix "[UFW %s] "' % (limit_args, \
 
565
                       policy)
 
566
                if not pat_logall.search(s):
 
567
                    lstr = '-m state --state NEW ' + lstr
 
568
                snippets[i] = pat_log.sub(r'\1-j \2\4', s)
 
569
                snippets.insert(i, pat_log.sub(r'\1-j ' + prefix + \
 
570
                                               '-user-logging-' + suffix, s))
 
571
                snippets.insert(i, pat_chain.sub(r'\1 ' + prefix + \
 
572
                                                 '-user-logging-' + suffix,
 
573
                                                 pat_log.sub(r'\1-j RETURN', \
 
574
                                                 s)))
 
575
                snippets.insert(i, pat_chain.sub(r'\1 ' + prefix + \
 
576
                                                 '-user-logging-' + suffix,
 
577
                                                 pat_log.sub(r'\1' + lstr, s)))
 
578
 
 
579
        # adjust for limit
 
580
        pat_limit = re.compile(r' -j LIMIT')
 
581
        for i, s in enumerate(snippets):
 
582
            if pat_limit.search(s):
 
583
                tmp1 = pat_limit.sub(' -m state --state NEW -m recent --set', \
 
584
                                     s)
 
585
                tmp2 = pat_limit.sub(' -m state --state NEW -m recent' + \
 
586
                                     ' --update --seconds 30 --hitcount 6' + \
 
587
                                     ' -j ' + prefix + '-user-limit', s)
 
588
                tmp3 = pat_limit.sub(' -j ' + prefix + '-user-limit-accept', s)
 
589
                snippets[i] = tmp3
 
590
                snippets.insert(i, tmp2)
 
591
                snippets.insert(i, tmp1)
 
592
 
 
593
        return snippets
 
594
 
 
595
    def _get_lists_from_formatted(self, frule, prefix, suffix):
 
596
        '''Return list of iptables rules appropriate for sending as arguments
 
597
           to cmd()
 
598
        '''
 
599
        snippets = []
 
600
        str_snippets = self._get_rules_from_formatted(frule, prefix, suffix)
 
601
 
 
602
        # split the string such that the log prefix can contain spaces
 
603
        pat = re.compile(r'(.*) --log-prefix (".* ")(.*)')
 
604
        for i, s in enumerate(str_snippets):
 
605
            snippets.append(pat.sub(r'\1', s).split())
 
606
            if pat.match(s):
 
607
                snippets[i].append("--log-prefix")
 
608
                snippets[i].append(pat.sub(r'\2', s).replace('"', ''))
 
609
                snippets[i] += pat.sub(r'\3', s).split()
 
610
 
 
611
        return snippets
 
612
 
 
613
    def _read_rules(self):
 
614
        '''Read in rules that were added by ufw'''
 
615
        rfns = [self.files['rules']]
 
616
        if self.use_ipv6():
 
617
            rfns.append(self.files['rules6'])
 
618
 
 
619
        for f in rfns:
 
620
            try:
 
621
                orig = ufw.util.open_file_read(f)
 
622
            except Exception:
 
623
                err_msg = _("Couldn't open '%s' for reading") % (f)
 
624
                raise UFWError(err_msg)
 
625
 
 
626
            pat_tuple = re.compile(r'^### tuple ###\s*')
 
627
            for line in orig:
 
628
                if pat_tuple.match(line):
 
629
                    tupl = pat_tuple.sub('', line)
 
630
                    tmp = re.split(r'\s+', tupl.strip())
 
631
                    if len(tmp) < 6 or len(tmp) > 9:
 
632
                        wmsg = _("Skipping malformed tuple (bad length): %s") \
 
633
                                 % (tupl)
 
634
                        warn(wmsg)
 
635
                        continue
 
636
                    else:
 
637
                        # set direction to "in" to support upgrades
 
638
                        # from old format, which only had 6 or 8 fields
 
639
                        dtype = "in"
 
640
                        interface = ""
 
641
                        if len(tmp) == 7 or len(tmp) == 9:
 
642
                            if '_' in tmp[-1]:
 
643
                                (dtype, interface) = tmp[-1].split('_')
 
644
                            else:
 
645
                                dtype = tmp[-1]
 
646
                        try:
 
647
                            if len(tmp) < 8:
 
648
                                rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3],
 
649
                                               tmp[4], tmp[5], dtype)
 
650
                            else:
 
651
                                rule = UFWRule(tmp[0], tmp[1], tmp[2], tmp[3],
 
652
                                               tmp[4], tmp[5], dtype)
 
653
                                # Removed leading [sd]app_ and unescape spaces
 
654
                                pat_space = re.compile('%20')
 
655
                                if tmp[6] != "-":
 
656
                                    rule.dapp = pat_space.sub(' ', tmp[6])
 
657
                                if tmp[7] != "-":
 
658
                                    rule.sapp = pat_space.sub(' ', tmp[7])
 
659
                            if interface != "":
 
660
                                rule.set_interface(dtype, interface)
 
661
 
 
662
                        except UFWError:
 
663
                            warn_msg = _("Skipping malformed tuple: %s") % \
 
664
                                        (tupl)
 
665
                            warn(warn_msg)
 
666
                            continue
 
667
                        if f == self.files['rules6']:
 
668
                            rule.set_v6(True)
 
669
                            self.rules6.append(rule)
 
670
                        else:
 
671
                            rule.set_v6(False)
 
672
                            self.rules.append(rule)
 
673
 
 
674
            orig.close()
 
675
 
 
676
    def _write_rules(self, v6=False):
 
677
        '''Write out new rules to file to user chain file'''
 
678
        rules_file = self.files['rules']
 
679
        if v6:
 
680
            rules_file = self.files['rules6']
 
681
 
 
682
        # Perform this here so we can present a nice error to the user rather
 
683
        # than a traceback
 
684
        if not os.access(rules_file, os.W_OK):
 
685
            err_msg = _("'%s' is not writable" % (rules_file))
 
686
            raise UFWError(err_msg)
 
687
 
 
688
        try:
 
689
            fns = ufw.util.open_files(rules_file)
 
690
        except Exception:
 
691
            raise
 
692
 
 
693
        # Initialize the capabilities database
 
694
        self.initcaps()
 
695
 
 
696
        chain_prefix = "ufw"
 
697
        rules = self.rules
 
698
        if v6:
 
699
            chain_prefix = "ufw6"
 
700
            rules = self.rules6
 
701
 
 
702
        if self.dryrun:
 
703
            fd = sys.stdout.fileno()
 
704
        else:
 
705
            fd = fns['tmp']
 
706
 
 
707
        # Write header
 
708
        ufw.util.write_to_file(fd, "*filter\n")
 
709
        ufw.util.write_to_file(fd, ":" + chain_prefix + "-user-input - [0:0]\n")
 
710
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
711
                                         "-user-output - [0:0]\n")
 
712
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
713
                                         "-user-forward - [0:0]\n")
 
714
 
 
715
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
716
                                         "-before-logging-input - [0:0]\n")
 
717
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
718
                                         "-before-logging-output - [0:0]\n")
 
719
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
720
                                         "-before-logging-forward - [0:0]\n")
 
721
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
722
                                         "-user-logging-input - [0:0]\n")
 
723
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
724
                                         "-user-logging-output - [0:0]\n")
 
725
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
726
                                         "-user-logging-forward - [0:0]\n")
 
727
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
728
                                         "-after-logging-input - [0:0]\n")
 
729
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
730
                                         "-after-logging-output - [0:0]\n")
 
731
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
732
                                         "-after-logging-forward - [0:0]\n")
 
733
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
734
                                         "-logging-deny - [0:0]\n")
 
735
        ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
736
                                         "-logging-allow - [0:0]\n")
 
737
 
 
738
        # Rate limiting is runtime supported
 
739
        if (chain_prefix == "ufw" and self.caps['limit']['4']) or \
 
740
           (chain_prefix == "ufw6" and self.caps['limit']['6']):
 
741
            ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
742
                                             "-user-limit - [0:0]\n")
 
743
            ufw.util.write_to_file(fd, ":" + chain_prefix + \
 
744
                                             "-user-limit-accept - [0:0]\n")
 
745
 
 
746
        ufw.util.write_to_file(fd, "### RULES ###\n")
 
747
 
 
748
        # Write rules
 
749
        for r in rules:
 
750
            action = r.action
 
751
            if r.logtype != "":
 
752
                action += "_" + r.logtype
 
753
 
 
754
            if r.dapp == "" and r.sapp == "":
 
755
                tstr = "\n### tuple ### %s %s %s %s %s %s %s" % \
 
756
                     (action, r.protocol, r.dport, r.dst, r.sport, r.src, \
 
757
                      r.direction)
 
758
                if r.interface_in != "":
 
759
                    tstr += "_%s" % (r.interface_in)
 
760
                if r.interface_out != "":
 
761
                    tstr += "_%s" % (r.interface_out)
 
762
                ufw.util.write_to_file(fd, tstr + "\n")
 
763
            else:
 
764
                pat_space = re.compile(' ')
 
765
                dapp = "-"
 
766
                if r.dapp:
 
767
                    dapp = pat_space.sub('%20', r.dapp)
 
768
                sapp = "-"
 
769
                if r.sapp:
 
770
                    sapp = pat_space.sub('%20', r.sapp)
 
771
                tstr = "\n### tuple ### %s %s %s %s %s %s %s %s %s" % \
 
772
                       (action, r.protocol, r.dport, r.dst, r.sport, r.src, \
 
773
                        dapp, sapp, r.direction)
 
774
 
 
775
                if r.interface_in != "":
 
776
                    tstr += "_%s" % (r.interface_in)
 
777
                if r.interface_out != "":
 
778
                    tstr += "_%s" % (r.interface_out)
 
779
                ufw.util.write_to_file(fd, tstr + "\n")
 
780
 
 
781
            chain_suffix = "input"
 
782
            if r.direction == "out":
 
783
                chain_suffix = "output"
 
784
            chain = "%s-user-%s" % (chain_prefix, chain_suffix)
 
785
            rule_str = "-A %s %s\n" % (chain, r.format_rule())
 
786
 
 
787
            for s in self._get_rules_from_formatted(rule_str, chain_prefix, \
 
788
                                                    chain_suffix):
 
789
                ufw.util.write_to_file(fd, s)
 
790
 
 
791
        # Write footer
 
792
        ufw.util.write_to_file(fd, "\n### END RULES ###\n")
 
793
 
 
794
        # Add logging rules, skipping any delete ('-D') rules
 
795
        ufw.util.write_to_file(fd, "\n### LOGGING ###\n")
 
796
        try:
 
797
            lrules_t = self._get_logging_rules(self.defaults['loglevel'])
 
798
        except Exception:
 
799
            raise
 
800
        for c, r, q in lrules_t:
 
801
            if len(r) > 0 and r[0] == '-D':
 
802
                continue
 
803
            if c.startswith(chain_prefix + "-"):
 
804
                ufw.util.write_to_file(fd,
 
805
                    " ".join(r).replace('[', '"[').replace('] ', '] "') + \
 
806
                    "\n")
 
807
        ufw.util.write_to_file(fd, "### END LOGGING ###\n")
 
808
 
 
809
        # Rate limiting is runtime supported
 
810
        if (chain_prefix == "ufw" and self.caps['limit']['4']) or \
 
811
           (chain_prefix == "ufw6" and self.caps['limit']['6']):
 
812
            ufw.util.write_to_file(fd, "\n### RATE LIMITING ###\n")
 
813
            if self.defaults['loglevel'] != "off":
 
814
                ufw.util.write_to_file(fd, "-A " + \
 
815
                         chain_prefix + "-user-limit " + \
 
816
                         " ".join(self.ufw_user_limit_log) + \
 
817
                         " \"" + self.ufw_user_limit_log_text + " \"\n")
 
818
            ufw.util.write_to_file(fd, "-A " + chain_prefix + \
 
819
                         "-user-limit -j REJECT\n")
 
820
            ufw.util.write_to_file(fd, "-A " + chain_prefix + \
 
821
                         "-user-limit-accept -j ACCEPT\n")
 
822
            ufw.util.write_to_file(fd, "### END RATE LIMITING ###\n")
 
823
 
 
824
        ufw.util.write_to_file(fd, "COMMIT\n")
 
825
 
 
826
        try:
 
827
            if self.dryrun:
 
828
                ufw.util.close_files(fns, False)
 
829
            else:
 
830
                ufw.util.close_files(fns)
 
831
        except Exception:
 
832
            raise
 
833
 
 
834
    def set_rule(self, rule, allow_reload=True):
 
835
        '''Updates firewall with rule by:
 
836
        * appending the rule to the chain if new rule and firewall enabled
 
837
        * deleting the rule from the chain if found and firewall enabled
 
838
        * inserting the rule if possible and firewall enabled
 
839
        * updating user rules file
 
840
        * reloading the user rules file if rule is modified
 
841
        '''
 
842
 
 
843
        # Initialize the capabilities database
 
844
        self.initcaps()
 
845
 
 
846
        rstr = ""
 
847
 
 
848
        if rule.v6:
 
849
            if not self.use_ipv6():
 
850
                err_msg = _("Adding IPv6 rule failed: IPv6 not enabled")
 
851
                raise UFWError(err_msg)
 
852
            if rule.action == 'limit' and not self.caps['limit']['6']:
 
853
                # Rate limiting is runtime supported
 
854
                return _("Skipping unsupported IPv6 '%s' rule") % (rule.action)
 
855
        else:
 
856
            if rule.action == 'limit' and not self.caps['limit']['4']:
 
857
                # Rate limiting is runtime supported
 
858
                return _("Skipping unsupported IPv4 '%s' rule") % (rule.action)
 
859
 
 
860
        if rule.multi and rule.protocol != "udp" and rule.protocol != "tcp":
 
861
            err_msg = _("Must specify 'tcp' or 'udp' with multiple ports")
 
862
            raise UFWError(err_msg)
 
863
 
 
864
        newrules = []
 
865
        found = False
 
866
        modified = False
 
867
 
 
868
        rules = self.rules
 
869
        position = rule.position
 
870
        if rule.v6:
 
871
            if self.iptables_version < "1.4" and (rule.dapp != "" or \
 
872
                                                  rule.sapp != ""):
 
873
                return _("Skipping IPv6 application rule. Need at least iptables 1.4")
 
874
            rules = self.rules6
 
875
 
 
876
        # bail if we have a bad position
 
877
        if position < 0 or position > len(rules):
 
878
            err_msg = _("Invalid position '%d'") % (position)
 
879
            raise UFWError(err_msg)
 
880
 
 
881
        if position > 0 and rule.remove:
 
882
            err_msg = _("Cannot specify insert and delete")
 
883
            raise UFWError(err_msg)
 
884
        if position > len(rules):
 
885
            err_msg = _("Cannot insert rule at position '%d'") % position
 
886
            raise UFWError(err_msg)
 
887
 
 
888
        # First construct the new rules list
 
889
        try:
 
890
            rule.normalize()
 
891
        except Exception:
 
892
            raise
 
893
 
 
894
        count = 1
 
895
        inserted = False
 
896
        matches = 0
 
897
        last = ('', '', '', '')
 
898
        for r in rules:
 
899
            try:
 
900
                r.normalize()
 
901
            except Exception:
 
902
                raise
 
903
 
 
904
            current = (r.dst, r.src, r.dapp, r.sapp)
 
905
            if count == position:
 
906
                # insert the rule if:
 
907
                # 1. the last rule was not an application rule
 
908
                # 2. the current rule is not an application rule
 
909
                # 3. the last application rule is different than the current
 
910
                #    while the new rule is different than the current one
 
911
                if (last[2] == '' and last[3] == '' and count > 1) or \
 
912
                   (current[2] == '' and current[3] == '') or \
 
913
                   last != current:
 
914
                    inserted = True
 
915
                    newrules.append(rule.dup_rule())
 
916
                    last = ('', '', '', '')
 
917
                else:
 
918
                    position += 1
 
919
            last = current
 
920
            count += 1
 
921
 
 
922
            ret = UFWRule.match(r, rule)
 
923
            if ret < 1:
 
924
                matches += 1
 
925
 
 
926
            if ret == 0 and not found and not inserted:
 
927
                # If find the rule, add it if it's not to be removed, otherwise
 
928
                # skip it.
 
929
                found = True
 
930
                if not rule.remove:
 
931
                    newrules.append(rule.dup_rule())
 
932
            elif ret < 0 and not rule.remove and not inserted:
 
933
                # If only the action is different, replace the rule if it's not
 
934
                # to be removed.
 
935
                found = True
 
936
                modified = True
 
937
                newrules.append(rule.dup_rule())
 
938
            else:
 
939
                newrules.append(r)
 
940
 
 
941
        if inserted:
 
942
            if matches > 0:
 
943
                rstr = _("Skipping inserting existing rule")
 
944
                if rule.v6:
 
945
                    rstr += " (v6)"
 
946
                return rstr
 
947
        else:
 
948
            # Add rule to the end if it was not already added.
 
949
            if not found and not rule.remove:
 
950
                newrules.append(rule.dup_rule())
 
951
 
 
952
            # Don't process non-existing or unchanged pre-exisiting rules
 
953
            if not found and rule.remove and not self.dryrun:
 
954
                rstr = _("Could not delete non-existent rule")
 
955
                if rule.v6:
 
956
                    rstr += " (v6)"
 
957
                return rstr
 
958
            elif found and not rule.remove and not modified:
 
959
                rstr = _("Skipping adding existing rule")
 
960
                if rule.v6:
 
961
                    rstr += " (v6)"
 
962
                return rstr
 
963
 
 
964
        if rule.v6:
 
965
            self.rules6 = newrules
 
966
        else:
 
967
            self.rules = newrules
 
968
 
 
969
        # Update the user rules file
 
970
        try:
 
971
            self._write_rules(rule.v6)
 
972
        except UFWError:
 
973
            raise
 
974
        except Exception:
 
975
            err_msg = _("Couldn't update rules file")
 
976
            UFWError(err_msg)
 
977
 
 
978
        # We wrote out the rules, so set reasonable string. We will change
 
979
        # this below when operating on the live firewall.
 
980
        rstr = _("Rules updated")
 
981
        if rule.v6:
 
982
            rstr = _("Rules updated (v6)")
 
983
 
 
984
        # Operate on the chains
 
985
        if self.is_enabled() and not self.dryrun:
 
986
            flag = ""
 
987
            if modified or self._need_reload(rule.v6) or inserted:
 
988
                rstr = ""
 
989
                if inserted:
 
990
                    rstr += _("Rule inserted")
 
991
                else:
 
992
                    rstr += _("Rule updated")
 
993
                if rule.v6:
 
994
                    rstr += " (v6)"
 
995
                if allow_reload:
 
996
                    # Reload the chain
 
997
                    try:
 
998
                        self._reload_user_rules()
 
999
                    except Exception:
 
1000
                        raise
 
1001
                else:
 
1002
                    rstr += _(" (skipped reloading firewall)")
 
1003
            elif found and rule.remove:
 
1004
                flag = '-D'
 
1005
                rstr = _("Rule deleted")
 
1006
            elif not found and not modified and not rule.remove:
 
1007
                flag = '-A'
 
1008
                rstr = _("Rule added")
 
1009
 
 
1010
            if flag != "":
 
1011
                exe = self.iptables
 
1012
                chain_prefix = "ufw"
 
1013
                if rule.v6:
 
1014
                    exe = self.ip6tables
 
1015
                    chain_prefix = "ufw6"
 
1016
                    rstr += " (v6)"
 
1017
                chain_suffix = "input"
 
1018
                if rule.direction == "out":
 
1019
                    chain_suffix = "output"
 
1020
                chain = "%s-user-%s" % (chain_prefix, chain_suffix)
 
1021
 
 
1022
                # Is the firewall running?
 
1023
                err_msg = _("Could not update running firewall")
 
1024
                (rc, out) = cmd([exe, '-L', chain, '-n'])
 
1025
                if rc != 0:
 
1026
                    raise UFWError(err_msg)
 
1027
 
 
1028
                rule_str = "%s %s %s" % (flag, chain, rule.format_rule())
 
1029
                pat_log = re.compile(r'(-A +)(ufw6?-user-[a-z\-]+)(.*)')
 
1030
                for s in self._get_lists_from_formatted(rule_str, \
 
1031
                                                        chain_prefix, \
 
1032
                                                        chain_suffix):
 
1033
                    (rc, out) = cmd([exe] + s)
 
1034
                    if rc != 0:
 
1035
                        msg(out, sys.stderr)
 
1036
                        UFWError(err_msg)
 
1037
 
 
1038
                    # delete any lingering RETURN rules (needed for upgrades)
 
1039
                    if flag == "-A" and pat_log.search(" ".join(s)):
 
1040
                        c = pat_log.sub(r'\2', " ".join(s))
 
1041
                        (rc, out) = cmd([exe, '-D', c, '-j', 'RETURN'])
 
1042
                        if rc != 0:
 
1043
                            debug("FAILOK: -D %s -j RETURN" % (c))
 
1044
 
 
1045
        return rstr
 
1046
 
 
1047
    def get_app_rules_from_system(self, template, v6):
 
1048
        '''Return a list of UFWRules from the system based on template rule'''
 
1049
        rules = []
 
1050
        app_rules = []
 
1051
 
 
1052
        if v6:
 
1053
            rules = self.rules6
 
1054
        else:
 
1055
            rules = self.rules
 
1056
 
 
1057
        norm = template.dup_rule()
 
1058
        norm.set_v6(v6)
 
1059
        norm.normalize()
 
1060
        tupl = norm.get_app_tuple()
 
1061
 
 
1062
        for r in rules:
 
1063
            tmp = r.dup_rule()
 
1064
            tmp.normalize()
 
1065
            tmp_tuple = tmp.get_app_tuple()
 
1066
            if tmp_tuple == tupl:
 
1067
                app_rules.append(tmp)
 
1068
 
 
1069
        return app_rules
 
1070
 
 
1071
    def _chain_cmd(self, chain, args, fail_ok=False):
 
1072
        '''Perform command on chain'''
 
1073
        exe = self.iptables
 
1074
        if chain.startswith("ufw6"):
 
1075
            exe = self.ip6tables
 
1076
        (rc, out) = cmd([exe] + args)
 
1077
        if rc != 0:
 
1078
            err_msg = _("Could not perform '%s'") % (args)
 
1079
            if fail_ok:
 
1080
                debug("FAILOK: " + err_msg)
 
1081
            else:
 
1082
                raise UFWError(err_msg)
 
1083
 
 
1084
    def update_logging(self, level):
 
1085
        '''Update loglevel of running firewall'''
 
1086
        if self.dryrun:
 
1087
            return
 
1088
 
 
1089
        # Initialize the capabilities database
 
1090
        self.initcaps()
 
1091
 
 
1092
        rules_t = []
 
1093
        try:
 
1094
            rules_t = self._get_logging_rules(level)
 
1095
        except Exception:
 
1096
            raise
 
1097
 
 
1098
        # Update the user rules file
 
1099
        try:
 
1100
            self._write_rules(v6=False)
 
1101
            self._write_rules(v6=True)
 
1102
        except UFWError:
 
1103
            raise
 
1104
        except Exception:
 
1105
            err_msg = _("Couldn't update rules file for logging")
 
1106
            UFWError(err_msg)
 
1107
 
 
1108
        # Don't update the running firewall if not enabled
 
1109
        if not self.is_enabled():
 
1110
            return
 
1111
 
 
1112
        # make sure all the chains are here, it's redundant but helps make
 
1113
        # sure the chains are in a consistent state
 
1114
        err_msg = _("Could not update running firewall")
 
1115
        for c in self.chains['before'] + self.chains['user'] + \
 
1116
           self.chains['after'] + self.chains['misc']:
 
1117
            try:
 
1118
                self._chain_cmd(c, ['-L', c, '-n'])
 
1119
            except Exception:
 
1120
                raise UFWError(err_msg)
 
1121
 
 
1122
        # Flush all the logging chains except 'user'
 
1123
        try:
 
1124
            for c in self.chains['before'] + self.chains['after'] + \
 
1125
               self.chains['misc']:
 
1126
                self._chain_cmd(c, ['-F', c])
 
1127
                self._chain_cmd(c, ['-Z', c])
 
1128
        except Exception:
 
1129
            raise UFWError(err_msg)
 
1130
 
 
1131
        # Add logging rules to running firewall
 
1132
        for c, r, q in rules_t:
 
1133
            fail_ok = False
 
1134
            if len(r) > 0 and r[0] == '-D':
 
1135
                fail_ok = True
 
1136
            try:
 
1137
                if q == 'delete_first' and len(r) > 1:
 
1138
                    self._chain_cmd(c, ['-D'] + r[1:], fail_ok=True)
 
1139
                self._chain_cmd(c, r, fail_ok)
 
1140
            except Exception:
 
1141
                raise UFWError(err_msg)
 
1142
 
 
1143
        # Rate limiting is runtime supported
 
1144
        # Always delete these and re-add them so that we don't have extras
 
1145
        for chain in ['ufw-user-limit', 'ufw6-user-limit']:
 
1146
            if (self.caps['limit']['4'] and chain == 'ufw-user-limit') or \
 
1147
               (self.caps['limit']['6'] and chain == 'ufw6-user-limit'):
 
1148
                self._chain_cmd(chain, ['-D', chain] + \
 
1149
                                self.ufw_user_limit_log + \
 
1150
                                [self.ufw_user_limit_log_text + " "], \
 
1151
                                fail_ok=True)
 
1152
                if self.defaults["loglevel"] != "off":
 
1153
                    self._chain_cmd(chain, ['-I', chain] + \
 
1154
                                    self.ufw_user_limit_log + \
 
1155
                                    [self.ufw_user_limit_log_text + " "], \
 
1156
                                    fail_ok=True)
 
1157
 
 
1158
    def _get_logging_rules(self, level):
 
1159
        '''Get rules for specified logging level'''
 
1160
        rules_t = []
 
1161
 
 
1162
        if level not in list(self.loglevels.keys()):
 
1163
            err_msg = _("Invalid log level '%s'") % (level)
 
1164
            raise UFWError(err_msg)
 
1165
 
 
1166
        if level == "off":
 
1167
            # when off, insert a RETURN rule at the top of user rules, thus
 
1168
            # preserving the rules
 
1169
            for c in self.chains['user']:
 
1170
                rules_t.append([c, ['-I', c, '-j', 'RETURN'], 'delete_first'])
 
1171
            return rules_t
 
1172
        else:
 
1173
            # when on, remove the RETURN rule at the top of user rules, thus
 
1174
            # honoring the log rules
 
1175
            for c in self.chains['user']:
 
1176
                rules_t.append([c, ['-D', c, '-j', 'RETURN'], ''])
 
1177
 
 
1178
        limit_args = ['-m', 'limit', '--limit', '3/min', '--limit-burst', '10']
 
1179
 
 
1180
        # log levels of low and higher log blocked packets
 
1181
        if self.loglevels[level] >= self.loglevels["low"]:
 
1182
            # Setup the policy violation logging chains
 
1183
            largs = []
 
1184
            # log levels under high use limit
 
1185
            if self.loglevels[level] < self.loglevels["high"]:
 
1186
                largs = limit_args
 
1187
            for c in self.chains['after']:
 
1188
                for t in ['input', 'output', 'forward']:
 
1189
                    if c.endswith(t):
 
1190
                        if self._get_default_policy(t) == "reject" or \
 
1191
                           self._get_default_policy(t) == "deny":
 
1192
                            prefix = "[UFW BLOCK] "
 
1193
                            rules_t.append([c, ['-A', c, '-j', 'LOG', \
 
1194
                                                '--log-prefix', prefix] +
 
1195
                                                largs, ''])
 
1196
                        elif self.loglevels[level] >= self.loglevels["medium"]:
 
1197
                            prefix = "[UFW ALLOW] "
 
1198
                            rules_t.append([c, ['-A', c, '-j', 'LOG', \
 
1199
                                                '--log-prefix', prefix] + \
 
1200
                                                largs, ''])
 
1201
 
 
1202
            # Setup the miscellaneous logging chains
 
1203
            largs = []
 
1204
            # log levels under high use limit
 
1205
            if self.loglevels[level] < self.loglevels["high"]:
 
1206
                largs = limit_args
 
1207
 
 
1208
            for c in self.chains['misc']:
 
1209
                if c.endswith("allow"):
 
1210
                    prefix = "[UFW ALLOW] "
 
1211
                elif c.endswith("deny"):
 
1212
                    prefix = "[UFW BLOCK] "
 
1213
                    if self.loglevels[level] < self.loglevels["medium"]:
 
1214
                        # only log INVALID in medium and higher
 
1215
                        rules_t.append([c, ['-I', c, '-m', 'state', \
 
1216
                                            '--state', 'INVALID', \
 
1217
                                            '-j', 'RETURN'] + largs, ''])
 
1218
                    else:
 
1219
                        rules_t.append([c, ['-A', c, '-m', 'state', \
 
1220
                                            '--state', 'INVALID', \
 
1221
                                            '-j', 'LOG', \
 
1222
                                            '--log-prefix', \
 
1223
                                            "[UFW AUDIT INVALID] "] + \
 
1224
                                        largs, ''])
 
1225
                rules_t.append([c, ['-A', c, '-j', 'LOG', \
 
1226
                                    '--log-prefix', prefix] + largs, ''])
 
1227
 
 
1228
        # Setup the audit logging chains
 
1229
        if self.loglevels[level] >= self.loglevels["medium"]:
 
1230
            # loglevel full logs all packets without limit
 
1231
            largs = []
 
1232
 
 
1233
            # loglevel high logs all packets with limit
 
1234
            if self.loglevels[level] < self.loglevels["full"]:
 
1235
                largs = limit_args
 
1236
 
 
1237
            # loglevel medium logs all new packets with limit
 
1238
            if self.loglevels[level] < self.loglevels["high"]:
 
1239
                largs = ['-m', 'state', '--state', 'NEW'] + limit_args
 
1240
 
 
1241
            prefix = "[UFW AUDIT] "
 
1242
            for c in self.chains['before']:
 
1243
                rules_t.append([c, ['-I', c, '-j', 'LOG', \
 
1244
                                    '--log-prefix', prefix] + largs, ''])
 
1245
 
 
1246
        return rules_t
 
1247
 
 
1248
    def reset(self):
 
1249
        '''Reset the firewall'''
 
1250
        res = ""
 
1251
        # First make sure we have all the original files
 
1252
        allfiles = []
 
1253
        for i in self.files:
 
1254
            if not self.files[i].endswith('.rules'):
 
1255
                continue
 
1256
            allfiles.append(self.files[i])
 
1257
            fn = os.path.join(ufw.common.share_dir, "iptables", \
 
1258
                              os.path.basename(self.files[i]))
 
1259
            if not os.path.isfile(fn):
 
1260
                err_msg = _("Could not find '%s'. Aborting") % (fn)
 
1261
                raise UFWError(err_msg)
 
1262
 
 
1263
        ext = time.strftime("%Y%m%d_%H%M%S")
 
1264
 
 
1265
        # This implementation will intentionally traceback if someone tries to
 
1266
        # do something to take advantage of the race conditions here.
 
1267
 
 
1268
        # Don't do anything if the files already exist
 
1269
        for i in allfiles:
 
1270
            fn = "%s.%s" % (i, ext)
 
1271
            if os.path.exists(fn):
 
1272
                err_msg = _("'%s' already exists. Aborting") % (fn)
 
1273
                raise UFWError(err_msg)
 
1274
 
 
1275
        # Move the old to the new
 
1276
        for i in allfiles:
 
1277
            fn = "%s.%s" % (i, ext)
 
1278
            res += _("Backing up '%(old)s' to '%(new)s'\n") % (\
 
1279
                     {'old': os.path.basename(i), 'new': fn})
 
1280
            os.rename(i, fn)
 
1281
 
 
1282
        # Copy files into place
 
1283
        for i in allfiles:
 
1284
            old = "%s.%s" % (i, ext)
 
1285
            shutil.copy(os.path.join(ufw.common.share_dir, "iptables", \
 
1286
                                     os.path.basename(i)), \
 
1287
                        os.path.dirname(i))
 
1288
            shutil.copymode(old, i)
 
1289
 
 
1290
            try:
 
1291
                statinfo = os.stat(i)
 
1292
                mode = statinfo[stat.ST_MODE]
 
1293
            except Exception:
 
1294
                warn_msg = _("Couldn't stat '%s'") % (i)
 
1295
                warn(warn_msg)
 
1296
                continue
 
1297
 
 
1298
            if mode & stat.S_IWOTH:
 
1299
                res += _("WARN: '%s' is world writable") % (i)
 
1300
            elif mode & stat.S_IROTH:
 
1301
                res += _("WARN: '%s' is world readable") % (i)
 
1302
 
 
1303
        return res
 
1304