/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos-ctl

  • Committer: Teddy Hogeborn
  • Date: 2015-03-10 18:03:38 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150310180338-pcxw6r2qmw9k6br9
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.

If Mandos was somehow made to use a non-ephemeral Diffie-Hellman key
exchange algorithm in the TLS handshake, any saved network traffic
could then be decrypted later if the Mandos client key was obtained.
By default, Mandos uses ephemeral DH key exchanges which does not have
this problem, but a non-ephemeral key exchange algorithm was still
enabled by default.  The simplest solution is to simply turn that off,
which ensures that Mandos will always use ephemeral DH key exchanges.

There is a "PFS" priority string specifier, but we can't use it because:

1. Security-wise, it is a mix between "NORMAL" and "SECURE128" - it
   enables a lot more algorithms than "SECURE256".

2. It is only available since GnuTLS 3.2.4.

Thanks to Andreas Fischer <af@bantuX.org> for reporting this issue.

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
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"
74
74
client_interface = domain + ".Mandos.Client"
75
75
version = "1.6.9"
76
76
 
77
 
 
78
77
def milliseconds_to_string(ms):
79
78
    td = datetime.timedelta(0, 0, 0, ms)
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))
 
79
    return ("{days}{hours:02}:{minutes:02}:{seconds:02}"
 
80
            .format(days = "{}T".format(td.days) if td.days else "",
 
81
                    hours = td.seconds // 3600,
 
82
                    minutes = (td.seconds % 3600) // 60,
 
83
                    seconds = td.seconds % 60,
 
84
                    ))
85
85
 
86
86
 
87
87
def rfc3339_duration_to_delta(duration):
111
111
    # avoid excessive use of external libraries.
112
112
    
113
113
    # New type for defining tokens, syntax, and semantics all-in-one
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
 
114
    Token = collections.namedtuple("Token",
 
115
                                   ("regexp", # To match token; if
 
116
                                              # "value" is not None,
 
117
                                              # must have a "group"
 
118
                                              # containing digits
 
119
                                    "value",  # datetime.timedelta or
 
120
                                              # None
 
121
                                    "followers")) # Tokens valid after
 
122
                                                  # this token
119
123
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
120
124
    # the "duration" ABNF definition in RFC 3339, Appendix A.
121
125
    token_end = Token(re.compile(r"$"), None, frozenset())
122
126
    token_second = Token(re.compile(r"(\d+)S"),
123
127
                         datetime.timedelta(seconds=1),
124
 
                         frozenset((token_end, )))
 
128
                         frozenset((token_end,)))
125
129
    token_minute = Token(re.compile(r"(\d+)M"),
126
130
                         datetime.timedelta(minutes=1),
127
131
                         frozenset((token_second, token_end)))
143
147
                       frozenset((token_month, token_end)))
144
148
    token_week = Token(re.compile(r"(\d+)W"),
145
149
                       datetime.timedelta(weeks=1),
146
 
                       frozenset((token_end, )))
 
150
                       frozenset((token_end,)))
147
151
    token_duration = Token(re.compile(r"P"), None,
148
152
                           frozenset((token_year, token_month,
149
153
                                      token_day, token_time,
151
155
    # Define starting values
152
156
    value = datetime.timedelta() # Value so far
153
157
    found_token = None
154
 
    followers = frozenset((token_duration, )) # Following valid tokens
 
158
    followers = frozenset((token_duration,)) # Following valid tokens
155
159
    s = duration                # String left to parse
156
160
    # Loop until end token is found
157
161
    while found_token is not token_end:
174
178
                break
175
179
        else:
176
180
            # No currently valid tokens were found
177
 
            raise ValueError("Invalid RFC 3339 duration: {!r}"
178
 
                             .format(duration))
 
181
            raise ValueError("Invalid RFC 3339 duration")
179
182
    # End token found
180
183
    return value
181
184
 
183
186
def string_to_delta(interval):
184
187
    """Parse a string and return a datetime.timedelta
185
188
    
186
 
    >>> string_to_delta('7d')
 
189
    >>> string_to_delta("7d")
187
190
    datetime.timedelta(7)
188
 
    >>> string_to_delta('60s')
 
191
    >>> string_to_delta("60s")
189
192
    datetime.timedelta(0, 60)
190
 
    >>> string_to_delta('60m')
 
193
    >>> string_to_delta("60m")
191
194
    datetime.timedelta(0, 3600)
192
 
    >>> string_to_delta('24h')
 
195
    >>> string_to_delta("24h")
193
196
    datetime.timedelta(1)
194
 
    >>> string_to_delta('1w')
 
197
    >>> string_to_delta("1w")
195
198
    datetime.timedelta(7)
196
 
    >>> string_to_delta('5m 30s')
 
199
    >>> string_to_delta("5m 30s")
197
200
    datetime.timedelta(0, 330)
198
201
    """
199
202
    
220
223
            value += datetime.timedelta(0, 0, 0, int(num))
221
224
    return value
222
225
 
223
 
 
224
226
def print_clients(clients, keywords):
225
227
    def valuetostring(value, keyword):
226
228
        if type(value) is dbus.Boolean:
232
234
    
233
235
    # Create format string to print table rows
234
236
    format_string = " ".join("{{{key}:{width}}}".format(
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)
 
237
            width = max(len(tablewords[key]),
 
238
                        max(len(valuetostring(client[key],
 
239
                                              key))
 
240
                            for client in
 
241
                            clients)),
 
242
            key = key) for key in keywords)
240
243
    # Print header line
241
244
    print(format_string.format(**tablewords))
242
245
    for client in clients:
243
 
        print(format_string.format(**{
244
 
            key: valuetostring(client[key], key)
245
 
            for key in keywords }))
246
 
 
 
246
        print(format_string.format(**{ key:
 
247
                                           valuetostring(client[key],
 
248
                                                         key)
 
249
                                       for key in keywords }))
247
250
 
248
251
def has_actions(options):
249
252
    return any((options.enable,
265
268
                options.approve,
266
269
                options.deny))
267
270
 
268
 
 
269
271
def main():
270
272
    parser = argparse.ArgumentParser()
271
273
    parser.add_argument("--version", action="version",
336
338
        bus = dbus.SystemBus()
337
339
        mandos_dbus_objc = bus.get_object(busname, server_path)
338
340
    except dbus.exceptions.DBusException:
339
 
        print("Could not connect to Mandos server", file=sys.stderr)
 
341
        print("Could not connect to Mandos server",
 
342
              file=sys.stderr)
340
343
        sys.exit(1)
341
344
    
342
345
    mandos_serv = dbus.Interface(mandos_dbus_objc,
379
382
    
380
383
    if not has_actions(options) and clients:
381
384
        if options.verbose:
382
 
            keywords = ("Name", "Enabled", "Timeout", "LastCheckedOK",
383
 
                        "Created", "Interval", "Host", "Fingerprint",
384
 
                        "CheckerRunning", "LastEnabled",
385
 
                        "ApprovalPending", "ApprovedByDefault",
 
385
            keywords = ("Name", "Enabled", "Timeout",
 
386
                        "LastCheckedOK", "Created", "Interval",
 
387
                        "Host", "Fingerprint", "CheckerRunning",
 
388
                        "LastEnabled", "ApprovalPending",
 
389
                        "ApprovedByDefault",
386
390
                        "LastApprovalRequest", "ApprovalDelay",
387
391
                        "ApprovalDuration", "Checker",
388
392
                        "ExtendedTimeout")
393
397
    else:
394
398
        # Process each client in the list by all selected options
395
399
        for client in clients:
396
 
            
397
400
            def set_client_prop(prop, value):
398
401
                """Set a Client D-Bus property"""
399
402
                client.Set(client_interface, prop, value,
400
403
                           dbus_interface=dbus.PROPERTIES_IFACE)
401
 
            
402
404
            def set_client_prop_ms(prop, value):
403
405
                """Set a Client D-Bus property, converted
404
406
                from a string to milliseconds."""
405
407
                set_client_prop(prop,
406
408
                                string_to_delta(value).total_seconds()
407
409
                                * 1000)
408
 
            
409
410
            if options.remove:
410
411
                mandos_serv.RemoveClient(client.__dbus_object_path__)
411
412
            if options.enable:
455
456
                client.Approve(dbus.Boolean(False),
456
457
                               dbus_interface=client_interface)
457
458
 
458
 
 
459
459
if __name__ == "__main__":
460
460
    main()