/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: 2015-07-20 03:03:33 UTC
  • Revision ID: teddy@recompile.se-20150720030333-203m2aeblypcsfte
Bug fix for GnuTLS 3: be compatible with old 2048-bit DSA keys.

The mandos-keygen program in Mandos version 1.6.0 and older generated
2048-bit DSA keys, and when GnuTLS uses these it has trouble
connecting using the Mandos default priority string.  This was
previously fixed in Mandos 1.6.2, but the bug reappeared when using
GnuTLS 3, so the default priority string has to change again; this
time also the Mandos client has to change its default, so now the
server and the client should use the same default priority string:

SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256

* mandos (main/server_defaults): Changed default priority string.
* mandos-options.xml (/section/para[id="priority_compat"]): Removed.
  (/section/para[id="priority"]): Changed default priority string.
* mandos.conf ([DEFAULT]/priority): - '' -
* mandos.conf.xml (OPTIONS/priority): Refer to the id "priority"
                                      instead of "priority_compat".
* mandos.xml (OPTIONS/--priority): - '' -
* plugins.d/mandos-client.c (main): Changed default priority string.

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-2014 Teddy Hogeborn
7
 
# Copyright © 2008-2014 Björn Påhlsson
 
6
# Copyright © 2008-2015 Teddy Hogeborn
 
7
# Copyright © 2008-2015 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[0] == 2:
 
45
if sys.version_info.major == 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.7"
 
75
version = "1.6.9"
76
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
77
 
83
78
def milliseconds_to_string(ms):
84
79
    td = datetime.timedelta(0, 0, 0, ms)
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
 
                    ))
 
80
    return ("{days}{hours:02}:{minutes:02}:{seconds:02}".format(
 
81
        days = "{}T".format(td.days) if td.days else "",
 
82
        hours = td.seconds // 3600,
 
83
        minutes = (td.seconds % 3600) // 60,
 
84
        seconds = td.seconds % 60))
91
85
 
92
86
 
93
87
def rfc3339_duration_to_delta(duration):
117
111
    # avoid excessive use of external libraries.
118
112
    
119
113
    # New type for defining tokens, syntax, and semantics all-in-one
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
 
114
    Token = collections.namedtuple("Token", (
 
115
        "regexp",  # To match token; if "value" is not None, must have
 
116
                   # a "group" containing digits
 
117
        "value",   # datetime.timedelta or None
 
118
        "followers"))           # Tokens valid after this token
129
119
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
130
120
    # the "duration" ABNF definition in RFC 3339, Appendix A.
131
121
    token_end = Token(re.compile(r"$"), None, frozenset())
132
122
    token_second = Token(re.compile(r"(\d+)S"),
133
123
                         datetime.timedelta(seconds=1),
134
 
                         frozenset((token_end,)))
 
124
                         frozenset((token_end, )))
135
125
    token_minute = Token(re.compile(r"(\d+)M"),
136
126
                         datetime.timedelta(minutes=1),
137
127
                         frozenset((token_second, token_end)))
153
143
                       frozenset((token_month, token_end)))
154
144
    token_week = Token(re.compile(r"(\d+)W"),
155
145
                       datetime.timedelta(weeks=1),
156
 
                       frozenset((token_end,)))
 
146
                       frozenset((token_end, )))
157
147
    token_duration = Token(re.compile(r"P"), None,
158
148
                           frozenset((token_year, token_month,
159
149
                                      token_day, token_time,
160
 
                                      token_week))),
 
150
                                      token_week)))
161
151
    # Define starting values
162
152
    value = datetime.timedelta() # Value so far
163
153
    found_token = None
164
 
    followers = frozenset(token_duration,) # Following valid tokens
 
154
    followers = frozenset((token_duration, )) # Following valid tokens
165
155
    s = duration                # String left to parse
166
156
    # Loop until end token is found
167
157
    while found_token is not token_end:
184
174
                break
185
175
        else:
186
176
            # No currently valid tokens were found
187
 
            raise ValueError("Invalid RFC 3339 duration")
 
177
            raise ValueError("Invalid RFC 3339 duration: {!r}"
 
178
                             .format(duration))
188
179
    # End token found
189
180
    return value
190
181
 
192
183
def string_to_delta(interval):
193
184
    """Parse a string and return a datetime.timedelta
194
185
    
195
 
    >>> string_to_delta("7d")
 
186
    >>> string_to_delta('7d')
196
187
    datetime.timedelta(7)
197
 
    >>> string_to_delta("60s")
 
188
    >>> string_to_delta('60s')
198
189
    datetime.timedelta(0, 60)
199
 
    >>> string_to_delta("60m")
 
190
    >>> string_to_delta('60m')
200
191
    datetime.timedelta(0, 3600)
201
 
    >>> string_to_delta("24h")
 
192
    >>> string_to_delta('24h')
202
193
    datetime.timedelta(1)
203
 
    >>> string_to_delta("1w")
 
194
    >>> string_to_delta('1w')
204
195
    datetime.timedelta(7)
205
 
    >>> string_to_delta("5m 30s")
 
196
    >>> string_to_delta('5m 30s')
206
197
    datetime.timedelta(0, 330)
207
198
    """
208
199
    
229
220
            value += datetime.timedelta(0, 0, 0, int(num))
230
221
    return value
231
222
 
 
223
 
232
224
def print_clients(clients, keywords):
233
225
    def valuetostring(value, keyword):
234
226
        if type(value) is dbus.Boolean:
240
232
    
241
233
    # Create format string to print table rows
242
234
    format_string = " ".join("{{{key}:{width}}}".format(
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)
 
235
        width = max(len(tablewords[key]),
 
236
                    max(len(valuetostring(client[key], key))
 
237
                        for client in clients)),
 
238
        key = key)
 
239
                             for key in keywords)
249
240
    # Print header line
250
241
    print(format_string.format(**tablewords))
251
242
    for client in clients:
252
 
        print(format_string.format(**dict((key,
253
 
                                           valuetostring(client[key],
254
 
                                                         key))
255
 
                                          for key in keywords)))
 
243
        print(format_string.format(**{
 
244
            key: valuetostring(client[key], key)
 
245
            for key in keywords }))
 
246
 
256
247
 
257
248
def has_actions(options):
258
249
    return any((options.enable,
274
265
                options.approve,
275
266
                options.deny))
276
267
 
 
268
 
277
269
def main():
278
270
    parser = argparse.ArgumentParser()
279
271
    parser.add_argument("--version", action="version",
280
 
                        version = "%(prog)s {0}".format(version),
 
272
                        version = "%(prog)s {}".format(version),
281
273
                        help="show version number and exit")
282
274
    parser.add_argument("-a", "--all", action="store_true",
283
275
                        help="Select all clients")
344
336
        bus = dbus.SystemBus()
345
337
        mandos_dbus_objc = bus.get_object(busname, server_path)
346
338
    except dbus.exceptions.DBusException:
347
 
        print("Could not connect to Mandos server",
348
 
              file=sys.stderr)
 
339
        print("Could not connect to Mandos server", file=sys.stderr)
349
340
        sys.exit(1)
350
341
    
351
342
    mandos_serv = dbus.Interface(mandos_dbus_objc,
372
363
    clients={}
373
364
    
374
365
    if options.all or not options.client:
375
 
        clients = dict((bus.get_object(busname, path), properties)
376
 
                       for path, properties in
377
 
                       mandos_clients.items())
 
366
        clients = { bus.get_object(busname, path): properties
 
367
                    for path, properties in mandos_clients.items() }
378
368
    else:
379
369
        for name in options.client:
380
 
            for path, client in mandos_clients.iteritems():
 
370
            for path, client in mandos_clients.items():
381
371
                if client["Name"] == name:
382
372
                    client_objc = bus.get_object(busname, path)
383
373
                    clients[client_objc] = client
384
374
                    break
385
375
            else:
386
 
                print("Client not found on server: {0!r}"
 
376
                print("Client not found on server: {!r}"
387
377
                      .format(name), file=sys.stderr)
388
378
                sys.exit(1)
389
379
    
390
380
    if not has_actions(options) and clients:
391
381
        if options.verbose:
392
 
            keywords = ("Name", "Enabled", "Timeout",
393
 
                        "LastCheckedOK", "Created", "Interval",
394
 
                        "Host", "Fingerprint", "CheckerRunning",
395
 
                        "LastEnabled", "ApprovalPending",
396
 
                        "ApprovedByDefault",
 
382
            keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
 
383
                        "Created", "Interval", "Host", "Fingerprint",
 
384
                        "CheckerRunning", "LastEnabled",
 
385
                        "ApprovalPending", "ApprovedByDefault",
397
386
                        "LastApprovalRequest", "ApprovalDelay",
398
387
                        "ApprovalDuration", "Checker",
399
388
                        "ExtendedTimeout")
404
393
    else:
405
394
        # Process each client in the list by all selected options
406
395
        for client in clients:
 
396
            
407
397
            def set_client_prop(prop, value):
408
398
                """Set a Client D-Bus property"""
409
399
                client.Set(client_interface, prop, value,
410
400
                           dbus_interface=dbus.PROPERTIES_IFACE)
 
401
            
411
402
            def set_client_prop_ms(prop, value):
412
403
                """Set a Client D-Bus property, converted
413
404
                from a string to milliseconds."""
414
405
                set_client_prop(prop,
415
 
                                timedelta_to_milliseconds
416
 
                                (string_to_delta(value)))
 
406
                                string_to_delta(value).total_seconds()
 
407
                                * 1000)
 
408
            
417
409
            if options.remove:
418
410
                mandos_serv.RemoveClient(client.__dbus_object_path__)
419
411
            if options.enable:
463
455
                client.Approve(dbus.Boolean(False),
464
456
                               dbus_interface=client_interface)
465
457
 
 
458
 
466
459
if __name__ == "__main__":
467
460
    main()