/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: 2014-07-25 22:44:20 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: teddy@recompile.se-20140725224420-4a5ct2ptt0hsc92z
Require Python 2.7.

This is in preparation for the eventual move to Python 3, which will
happen as soon as all Python modules required by Mandos are available.
The mandos-ctl and mandos-monitor programs are already portable
between Python 2.6 and Python 3 without changes; this change will
bring the requirement up to Python 2.7.

* INSTALL (Prerequisites/Libraries/Mandos Server): Document
                                                   requirement of
                                                   Python 2.7; remove
                                                   Python-argparse
                                                   which is in the
                                                   Python 2.7 standard
                                                   library.
* debian/control (Source: mandos/Build-Depends-Indep): Depend on
                                                       exactly the
                                                       python2.7
                                                       package and all
                                                       the Python 2.7
                                                       versions of the
                                                       python modules.
  (Package: mandos/Depends): - '' - but still depend on python (<=2.7)
                            and the generic versions of the Python
                            modules; this is for mandos-ctl and
                            mandos-monitor, both of which are
                            compatible with Python 3, and use
                            #!/usr/bin/python.
* mandos: Use #!/usr/bin/python2.7 instead of #!/usr/bin/python.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
4
4
# Mandos Monitor - Control and monitor the Mandos server
5
5
6
 
# Copyright © 2008-2015 Teddy Hogeborn
7
 
# Copyright © 2008-2015 Björn Påhlsson
 
6
# Copyright © 2008-2014 Teddy Hogeborn
 
7
# Copyright © 2008-2014 Björn Påhlsson
8
8
9
9
# This program is free software: you can redistribute it and/or modify
10
10
# it under the terms of the GNU General Public License as published by
42
42
 
43
43
import dbus
44
44
 
45
 
if sys.version_info.major == 2:
 
45
if sys.version_info[0] == 2:
46
46
    str = unicode
47
47
 
48
48
locale.setlocale(locale.LC_ALL, "")
64
64
    "ApprovalDelay": "Approval Delay",
65
65
    "ApprovalDuration": "Approval Duration",
66
66
    "Checker": "Checker",
67
 
    "ExtendedTimeout": "Extended Timeout"
68
 
}
 
67
    "ExtendedTimeout" : "Extended Timeout"
 
68
    }
69
69
defaultkeywords = ("Name", "Enabled", "Timeout", "LastCheckedOK")
70
70
domain = "se.recompile"
71
71
busname = domain + ".Mandos"
72
72
server_path = "/"
73
73
server_interface = domain + ".Mandos"
74
74
client_interface = domain + ".Mandos.Client"
75
 
version = "1.6.9"
76
 
 
77
 
 
78
 
try:
79
 
    dbus.OBJECT_MANAGER_IFACE
80
 
except AttributeError:
81
 
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
 
75
version = "1.6.7"
 
76
 
 
77
def timedelta_to_milliseconds(td):
 
78
    """Convert a datetime.timedelta object to milliseconds"""
 
79
    return ((td.days * 24 * 60 * 60 * 1000)
 
80
            + (td.seconds * 1000)
 
81
            + (td.microseconds // 1000))
82
82
 
83
83
def milliseconds_to_string(ms):
84
84
    td = datetime.timedelta(0, 0, 0, ms)
85
 
    return ("{days}{hours:02}:{minutes:02}:{seconds:02}".format(
86
 
        days = "{}T".format(td.days) if td.days else "",
87
 
        hours = td.seconds // 3600,
88
 
        minutes = (td.seconds % 3600) // 60,
89
 
        seconds = td.seconds % 60))
 
85
    return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
 
86
            .format(days = "{0}T".format(td.days) if td.days else "",
 
87
                    hours = td.seconds // 3600,
 
88
                    minutes = (td.seconds % 3600) // 60,
 
89
                    seconds = td.seconds % 60,
 
90
                    ))
90
91
 
91
92
 
92
93
def rfc3339_duration_to_delta(duration):
116
117
    # avoid excessive use of external libraries.
117
118
    
118
119
    # New type for defining tokens, syntax, and semantics all-in-one
119
 
    Token = collections.namedtuple("Token", (
120
 
        "regexp",  # To match token; if "value" is not None, must have
121
 
                   # a "group" containing digits
122
 
        "value",   # datetime.timedelta or None
123
 
        "followers"))           # Tokens valid after this token
 
120
    Token = collections.namedtuple("Token",
 
121
                                   ("regexp", # To match token; if
 
122
                                              # "value" is not None,
 
123
                                              # must have a "group"
 
124
                                              # containing digits
 
125
                                    "value",  # datetime.timedelta or
 
126
                                              # None
 
127
                                    "followers")) # Tokens valid after
 
128
                                                  # this token
124
129
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
125
130
    # the "duration" ABNF definition in RFC 3339, Appendix A.
126
131
    token_end = Token(re.compile(r"$"), None, frozenset())
127
132
    token_second = Token(re.compile(r"(\d+)S"),
128
133
                         datetime.timedelta(seconds=1),
129
 
                         frozenset((token_end, )))
 
134
                         frozenset((token_end,)))
130
135
    token_minute = Token(re.compile(r"(\d+)M"),
131
136
                         datetime.timedelta(minutes=1),
132
137
                         frozenset((token_second, token_end)))
148
153
                       frozenset((token_month, token_end)))
149
154
    token_week = Token(re.compile(r"(\d+)W"),
150
155
                       datetime.timedelta(weeks=1),
151
 
                       frozenset((token_end, )))
 
156
                       frozenset((token_end,)))
152
157
    token_duration = Token(re.compile(r"P"), None,
153
158
                           frozenset((token_year, token_month,
154
159
                                      token_day, token_time,
156
161
    # Define starting values
157
162
    value = datetime.timedelta() # Value so far
158
163
    found_token = None
159
 
    followers = frozenset((token_duration, )) # Following valid tokens
 
164
    followers = frozenset((token_duration,)) # Following valid tokens
160
165
    s = duration                # String left to parse
161
166
    # Loop until end token is found
162
167
    while found_token is not token_end:
179
184
                break
180
185
        else:
181
186
            # No currently valid tokens were found
182
 
            raise ValueError("Invalid RFC 3339 duration: {!r}"
183
 
                             .format(duration))
 
187
            raise ValueError("Invalid RFC 3339 duration")
184
188
    # End token found
185
189
    return value
186
190
 
188
192
def string_to_delta(interval):
189
193
    """Parse a string and return a datetime.timedelta
190
194
    
191
 
    >>> string_to_delta('7d')
 
195
    >>> string_to_delta("7d")
192
196
    datetime.timedelta(7)
193
 
    >>> string_to_delta('60s')
 
197
    >>> string_to_delta("60s")
194
198
    datetime.timedelta(0, 60)
195
 
    >>> string_to_delta('60m')
 
199
    >>> string_to_delta("60m")
196
200
    datetime.timedelta(0, 3600)
197
 
    >>> string_to_delta('24h')
 
201
    >>> string_to_delta("24h")
198
202
    datetime.timedelta(1)
199
 
    >>> string_to_delta('1w')
 
203
    >>> string_to_delta("1w")
200
204
    datetime.timedelta(7)
201
 
    >>> string_to_delta('5m 30s')
 
205
    >>> string_to_delta("5m 30s")
202
206
    datetime.timedelta(0, 330)
203
207
    """
204
208
    
225
229
            value += datetime.timedelta(0, 0, 0, int(num))
226
230
    return value
227
231
 
228
 
 
229
232
def print_clients(clients, keywords):
230
233
    def valuetostring(value, keyword):
231
234
        if type(value) is dbus.Boolean:
237
240
    
238
241
    # Create format string to print table rows
239
242
    format_string = " ".join("{{{key}:{width}}}".format(
240
 
        width = max(len(tablewords[key]),
241
 
                    max(len(valuetostring(client[key], key))
242
 
                        for client in clients)),
243
 
        key = key)
244
 
                             for key in keywords)
 
243
            width = max(len(tablewords[key]),
 
244
                        max(len(valuetostring(client[key],
 
245
                                              key))
 
246
                            for client in
 
247
                            clients)),
 
248
            key = key) for key in keywords)
245
249
    # Print header line
246
250
    print(format_string.format(**tablewords))
247
251
    for client in clients:
248
 
        print(format_string.format(**{
249
 
            key: valuetostring(client[key], key)
250
 
            for key in keywords }))
251
 
 
 
252
        print(format_string.format(**dict((key,
 
253
                                           valuetostring(client[key],
 
254
                                                         key))
 
255
                                          for key in keywords)))
252
256
 
253
257
def has_actions(options):
254
258
    return any((options.enable,
270
274
                options.approve,
271
275
                options.deny))
272
276
 
273
 
 
274
277
def main():
275
278
    parser = argparse.ArgumentParser()
276
279
    parser.add_argument("--version", action="version",
277
 
                        version = "%(prog)s {}".format(version),
 
280
                        version = "%(prog)s {0}".format(version),
278
281
                        help="show version number and exit")
279
282
    parser.add_argument("-a", "--all", action="store_true",
280
283
                        help="Select all clients")
341
344
        bus = dbus.SystemBus()
342
345
        mandos_dbus_objc = bus.get_object(busname, server_path)
343
346
    except dbus.exceptions.DBusException:
344
 
        print("Could not connect to Mandos server", file=sys.stderr)
 
347
        print("Could not connect to Mandos server",
 
348
              file=sys.stderr)
345
349
        sys.exit(1)
346
350
    
347
351
    mandos_serv = dbus.Interface(mandos_dbus_objc,
348
352
                                 dbus_interface = server_interface)
349
 
    mandos_serv_object_manager = dbus.Interface(
350
 
        mandos_dbus_objc, dbus_interface = dbus.OBJECT_MANAGER_IFACE)
351
353
    
352
354
    #block stderr since dbus library prints to stderr
353
355
    null = os.open(os.path.devnull, os.O_RDWR)
356
358
    os.close(null)
357
359
    try:
358
360
        try:
359
 
            mandos_clients = { path: ifs_and_props[client_interface]
360
 
                               for path, ifs_and_props in
361
 
                               mandos_serv_object_manager
362
 
                               .GetManagedObjects().items()
363
 
                               if client_interface in ifs_and_props }
 
361
            mandos_clients = mandos_serv.GetAllClientsWithProperties()
364
362
        finally:
365
363
            #restore stderr
366
364
            os.dup2(stderrcopy, sys.stderr.fileno())
367
365
            os.close(stderrcopy)
368
 
    except dbus.exceptions.DBusException as e:
369
 
        print("Access denied: Accessing mandos server through D-Bus: {}"
370
 
              .format(e), file=sys.stderr)
 
366
    except dbus.exceptions.DBusException:
 
367
        print("Access denied: Accessing mandos server through dbus.",
 
368
              file=sys.stderr)
371
369
        sys.exit(1)
372
370
    
373
371
    # Compile dict of (clients: properties) to process
374
372
    clients={}
375
373
    
376
374
    if options.all or not options.client:
377
 
        clients = { bus.get_object(busname, path): properties
378
 
                    for path, properties in mandos_clients.items() }
 
375
        clients = dict((bus.get_object(busname, path), properties)
 
376
                       for path, properties in
 
377
                       mandos_clients.items())
379
378
    else:
380
379
        for name in options.client:
381
 
            for path, client in mandos_clients.items():
 
380
            for path, client in mandos_clients.iteritems():
382
381
                if client["Name"] == name:
383
382
                    client_objc = bus.get_object(busname, path)
384
383
                    clients[client_objc] = client
385
384
                    break
386
385
            else:
387
 
                print("Client not found on server: {!r}"
 
386
                print("Client not found on server: {0!r}"
388
387
                      .format(name), file=sys.stderr)
389
388
                sys.exit(1)
390
389
    
391
390
    if not has_actions(options) and clients:
392
391
        if options.verbose:
393
 
            keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
394
 
                        "Created", "Interval", "Host", "Fingerprint",
395
 
                        "CheckerRunning", "LastEnabled",
396
 
                        "ApprovalPending", "ApprovedByDefault",
 
392
            keywords = ("Name", "Enabled", "Timeout",
 
393
                        "LastCheckedOK", "Created", "Interval",
 
394
                        "Host", "Fingerprint", "CheckerRunning",
 
395
                        "LastEnabled", "ApprovalPending",
 
396
                        "ApprovedByDefault",
397
397
                        "LastApprovalRequest", "ApprovalDelay",
398
398
                        "ApprovalDuration", "Checker",
399
399
                        "ExtendedTimeout")
404
404
    else:
405
405
        # Process each client in the list by all selected options
406
406
        for client in clients:
407
 
            
408
407
            def set_client_prop(prop, value):
409
408
                """Set a Client D-Bus property"""
410
409
                client.Set(client_interface, prop, value,
411
410
                           dbus_interface=dbus.PROPERTIES_IFACE)
412
 
            
413
411
            def set_client_prop_ms(prop, value):
414
412
                """Set a Client D-Bus property, converted
415
413
                from a string to milliseconds."""
416
414
                set_client_prop(prop,
417
 
                                string_to_delta(value).total_seconds()
418
 
                                * 1000)
419
 
            
 
415
                                timedelta_to_milliseconds
 
416
                                (string_to_delta(value)))
420
417
            if options.remove:
421
418
                mandos_serv.RemoveClient(client.__dbus_object_path__)
422
419
            if options.enable:
466
463
                client.Approve(dbus.Boolean(False),
467
464
                               dbus_interface=client_interface)
468
465
 
469
 
 
470
466
if __name__ == "__main__":
471
467
    main()