/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos-ctl

  • Committer: Teddy Hogeborn
  • Date: 2017-08-20 14:41:20 UTC
  • Revision ID: teddy@recompile.se-20170820144120-ee0hsyhvo1geg8ms
Handle multiple lines better in cryptroot file.

* initramfs-tools-script: Avoid running plugin-runner more than once
  if the root file system device is specially marked in the cryptroot
  file.  Also never run plugin-runner for a resume (usually swap)
  device.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
 
# -*- mode: python; coding: utf-8; after-save-hook: (lambda () (let ((command (if (and (boundp 'tramp-file-name-structure) (string-match (car tramp-file-name-structure) (buffer-file-name))) (tramp-file-name-localname (tramp-dissect-file-name (buffer-file-name))) (buffer-file-name)))) (if (= (shell-command (format "%s --check" (shell-quote-argument command)) "*Test*") 0) (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)) (kill-buffer "*Test*")) (display-buffer "*Test*")))); -*-
 
2
# -*- mode: python; coding: utf-8 -*-
3
3
#
4
4
# Mandos Monitor - Control and monitor the Mandos server
5
5
#
6
 
# Copyright © 2008-2019 Teddy Hogeborn
7
 
# Copyright © 2008-2019 Björn Påhlsson
8
 
#
9
 
# This file is part of Mandos.
10
 
#
11
 
# Mandos is free software: you can redistribute it and/or modify it
12
 
# under the terms of the GNU General Public License as published by
 
6
# Copyright © 2008-2017 Teddy Hogeborn
 
7
# Copyright © 2008-2017 Björn Påhlsson
 
8
#
 
9
# This program is free software: you can redistribute it and/or modify
 
10
# it under the terms of the GNU General Public License as published by
13
11
# the Free Software Foundation, either version 3 of the License, or
14
12
# (at your option) any later version.
15
13
#
16
 
#     Mandos is distributed in the hope that it will be useful, but
17
 
#     WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
#     This program is distributed in the hope that it will be useful,
 
15
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
18
16
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
17
#     GNU General Public License for more details.
20
18
#
21
19
# You should have received a copy of the GNU General Public License
22
 
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
20
# along with this program.  If not, see
 
21
# <http://www.gnu.org/licenses/>.
23
22
#
24
23
# Contact the authors at <mandos@recompile.se>.
25
24
#
40
39
import os
41
40
import collections
42
41
import json
43
 
import unittest
44
 
import logging
45
42
 
46
43
import dbus
47
44
 
48
 
# Show warnings by default
49
 
if not sys.warnoptions:
50
 
    import warnings
51
 
    warnings.simplefilter("default")
52
 
 
53
 
log = logging.getLogger(sys.argv[0])
54
 
logging.basicConfig(level="INFO", # Show info level messages
55
 
                    format="%(message)s") # Show basic log messages
56
 
 
57
 
logging.captureWarnings(True)   # Show warnings via the logging system
58
 
 
59
45
if sys.version_info.major == 2:
60
46
    str = unicode
61
47
 
71
57
    "Interval": "Interval",
72
58
    "Host": "Host",
73
59
    "Fingerprint": "Fingerprint",
74
 
    "KeyID": "Key ID",
75
60
    "CheckerRunning": "Check Is Running",
76
61
    "LastEnabled": "Last Enabled",
77
62
    "ApprovalPending": "Approval Is Pending",
89
74
server_path = "/"
90
75
server_interface = domain + ".Mandos"
91
76
client_interface = domain + ".Mandos.Client"
92
 
version = "1.8.3"
 
77
version = "1.7.15"
93
78
 
94
79
 
95
80
try:
116
101
    datetime.timedelta(0, 60)
117
102
    >>> rfc3339_duration_to_delta("PT60M")
118
103
    datetime.timedelta(0, 3600)
119
 
    >>> rfc3339_duration_to_delta("P60M")
120
 
    datetime.timedelta(1680)
121
104
    >>> rfc3339_duration_to_delta("PT24H")
122
105
    datetime.timedelta(1)
123
106
    >>> rfc3339_duration_to_delta("P1W")
126
109
    datetime.timedelta(0, 330)
127
110
    >>> rfc3339_duration_to_delta("P1DT3M20S")
128
111
    datetime.timedelta(1, 200)
129
 
    >>> # Can not be empty:
130
 
    >>> rfc3339_duration_to_delta("")
131
 
    Traceback (most recent call last):
132
 
    ...
133
 
    ValueError: Invalid RFC 3339 duration: u''
134
 
    >>> # Must start with "P":
135
 
    >>> rfc3339_duration_to_delta("1D")
136
 
    Traceback (most recent call last):
137
 
    ...
138
 
    ValueError: Invalid RFC 3339 duration: u'1D'
139
 
    >>> # Must use correct order
140
 
    >>> rfc3339_duration_to_delta("PT1S2M")
141
 
    Traceback (most recent call last):
142
 
    ...
143
 
    ValueError: Invalid RFC 3339 duration: u'PT1S2M'
144
 
    >>> # Time needs time marker
145
 
    >>> rfc3339_duration_to_delta("P1H2S")
146
 
    Traceback (most recent call last):
147
 
    ...
148
 
    ValueError: Invalid RFC 3339 duration: u'P1H2S'
149
 
    >>> # Weeks can not be combined with anything else
150
 
    >>> rfc3339_duration_to_delta("P1D2W")
151
 
    Traceback (most recent call last):
152
 
    ...
153
 
    ValueError: Invalid RFC 3339 duration: u'P1D2W'
154
 
    >>> rfc3339_duration_to_delta("P2W2H")
155
 
    Traceback (most recent call last):
156
 
    ...
157
 
    ValueError: Invalid RFC 3339 duration: u'P2W2H'
158
112
    """
159
113
 
160
114
    # Parsing an RFC 3339 duration with regular expressions is not
239
193
 
240
194
def string_to_delta(interval):
241
195
    """Parse a string and return a datetime.timedelta
 
196
 
 
197
    >>> string_to_delta('7d')
 
198
    datetime.timedelta(7)
 
199
    >>> string_to_delta('60s')
 
200
    datetime.timedelta(0, 60)
 
201
    >>> string_to_delta('60m')
 
202
    datetime.timedelta(0, 3600)
 
203
    >>> string_to_delta('24h')
 
204
    datetime.timedelta(1)
 
205
    >>> string_to_delta('1w')
 
206
    datetime.timedelta(7)
 
207
    >>> string_to_delta('5m 30s')
 
208
    datetime.timedelta(0, 330)
242
209
    """
243
210
 
244
211
    try:
245
212
        return rfc3339_duration_to_delta(interval)
246
 
    except ValueError as e:
247
 
        log.warning("%s - Parsing as pre-1.6.1 interval instead",
248
 
                    ' '.join(e.args))
249
 
    return parse_pre_1_6_1_interval(interval)
250
 
 
251
 
 
252
 
def parse_pre_1_6_1_interval(interval):
253
 
    """Parse an interval string as documented by Mandos before 1.6.1, and
254
 
    return a datetime.timedelta
255
 
    >>> parse_pre_1_6_1_interval('7d')
256
 
    datetime.timedelta(7)
257
 
    >>> parse_pre_1_6_1_interval('60s')
258
 
    datetime.timedelta(0, 60)
259
 
    >>> parse_pre_1_6_1_interval('60m')
260
 
    datetime.timedelta(0, 3600)
261
 
    >>> parse_pre_1_6_1_interval('24h')
262
 
    datetime.timedelta(1)
263
 
    >>> parse_pre_1_6_1_interval('1w')
264
 
    datetime.timedelta(7)
265
 
    >>> parse_pre_1_6_1_interval('5m 30s')
266
 
    datetime.timedelta(0, 330)
267
 
    >>> parse_pre_1_6_1_interval('')
268
 
    datetime.timedelta(0)
269
 
    >>> # Ignore unknown characters, allow any order and repetitions
270
 
    >>> parse_pre_1_6_1_interval('2dxy7zz11y3m5m')
271
 
    datetime.timedelta(2, 480, 18000)
272
 
 
273
 
    """
 
213
    except ValueError:
 
214
        pass
274
215
 
275
216
    value = datetime.timedelta(0)
276
217
    regexp = re.compile(r"(\d+)([dsmhw]?)")
293
234
 
294
235
def print_clients(clients, keywords):
295
236
    def valuetostring(value, keyword):
296
 
        if isinstance(value, dbus.Boolean):
 
237
        if type(value) is dbus.Boolean:
297
238
            return "Yes" if value else "No"
298
239
        if keyword in ("Timeout", "Interval", "ApprovalDelay",
299
240
                       "ApprovalDuration", "ExtendedTimeout"):
402
343
    if options.all and not has_actions(options):
403
344
        parser.error("--all requires an action.")
404
345
 
 
346
    if options.check:
 
347
        import doctest
 
348
        fail_count, test_count = doctest.testmod()
 
349
        sys.exit(os.EX_OK if fail_count == 0 else 1)
 
350
 
405
351
    try:
406
352
        bus = dbus.SystemBus()
407
353
        mandos_dbus_objc = bus.get_object(busname, server_path)
408
354
    except dbus.exceptions.DBusException:
409
 
        log.critical("Could not connect to Mandos server")
 
355
        print("Could not connect to Mandos server", file=sys.stderr)
410
356
        sys.exit(1)
411
357
 
412
358
    mandos_serv = dbus.Interface(mandos_dbus_objc,
431
377
            os.dup2(stderrcopy, sys.stderr.fileno())
432
378
            os.close(stderrcopy)
433
379
    except dbus.exceptions.DBusException as e:
434
 
        log.critical("Failed to access Mandos server through D-Bus:"
435
 
                     "\n%s", e)
 
380
        print("Access denied: "
 
381
              "Accessing mandos server through D-Bus: {}".format(e),
 
382
              file=sys.stderr)
436
383
        sys.exit(1)
437
384
 
438
385
    # Compile dict of (clients: properties) to process
449
396
                    clients[client_objc] = client
450
397
                    break
451
398
            else:
452
 
                log.critical("Client not found on server: %r", name)
 
399
                print("Client not found on server: {!r}"
 
400
                      .format(name), file=sys.stderr)
453
401
                sys.exit(1)
454
402
 
455
403
    if not has_actions(options) and clients:
456
404
        if options.verbose or options.dump_json:
457
405
            keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
458
 
                        "Created", "Interval", "Host", "KeyID",
459
 
                        "Fingerprint", "CheckerRunning",
460
 
                        "LastEnabled", "ApprovalPending",
461
 
                        "ApprovedByDefault", "LastApprovalRequest",
462
 
                        "ApprovalDelay", "ApprovalDuration",
463
 
                        "Checker", "ExtendedTimeout", "Expires",
 
406
                        "Created", "Interval", "Host", "Fingerprint",
 
407
                        "CheckerRunning", "LastEnabled",
 
408
                        "ApprovalPending", "ApprovedByDefault",
 
409
                        "LastApprovalRequest", "ApprovalDelay",
 
410
                        "ApprovalDuration", "Checker",
 
411
                        "ExtendedTimeout", "Expires",
464
412
                        "LastCheckerStatus")
465
413
        else:
466
414
            keywords = defaultkeywords
543
491
                client.Approve(dbus.Boolean(False),
544
492
                               dbus_interface=client_interface)
545
493
 
546
 
 
547
 
class Test_milliseconds_to_string(unittest.TestCase):
548
 
    def test_all(self):
549
 
        self.assertEqual(milliseconds_to_string(93785000),
550
 
                         "1T02:03:05")
551
 
    def test_no_days(self):
552
 
        self.assertEqual(milliseconds_to_string(7385000), "02:03:05")
553
 
    def test_all_zero(self):
554
 
        self.assertEqual(milliseconds_to_string(0), "00:00:00")
555
 
    def test_no_fractional_seconds(self):
556
 
        self.assertEqual(milliseconds_to_string(400), "00:00:00")
557
 
        self.assertEqual(milliseconds_to_string(900), "00:00:00")
558
 
        self.assertEqual(milliseconds_to_string(1900), "00:00:01")
559
 
 
560
 
class Test_string_to_delta(unittest.TestCase):
561
 
    def test_handles_basic_rfc3339(self):
562
 
        self.assertEqual(string_to_delta("PT2H"),
563
 
                         datetime.timedelta(0, 7200))
564
 
    def test_falls_back_to_pre_1_6_1_with_warning(self):
565
 
        # assertLogs only exists in Python 3.4
566
 
        if hasattr(self, "assertLogs"):
567
 
            with self.assertLogs(log, logging.WARNING):
568
 
                value = string_to_delta("2h")
569
 
        else:
570
 
            value = string_to_delta("2h")
571
 
        self.assertEqual(value, datetime.timedelta(0, 7200))
572
 
 
573
 
 
574
 
def should_only_run_tests():
575
 
    parser = argparse.ArgumentParser(add_help=False)
576
 
    parser.add_argument("--check", action='store_true')
577
 
    args, unknown_args = parser.parse_known_args()
578
 
    run_tests = args.check
579
 
    if run_tests:
580
 
        # Remove --check argument from sys.argv
581
 
        sys.argv[1:] = unknown_args
582
 
    return run_tests
583
 
 
584
 
# Add all tests from doctest strings
585
 
def load_tests(loader, tests, none):
586
 
    import doctest
587
 
    tests.addTests(doctest.DocTestSuite())
588
 
    return tests
589
494
 
590
495
if __name__ == "__main__":
591
 
    if should_only_run_tests():
592
 
        # Call using ./tdd-python-script --check [--verbose]
593
 
        unittest.main()
594
 
    else:
595
 
        main()
 
496
    main()