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

« back to all changes in this revision

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