/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

First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
# -*- mode: python; coding: utf-8 -*-
3
 
 
4
 
from __future__ import division
5
 
import sys
6
 
import dbus
7
 
from optparse import OptionParser
8
 
import locale
9
 
import datetime
10
 
import re
11
 
import os
12
 
 
13
 
locale.setlocale(locale.LC_ALL, u'')
14
 
 
15
 
tablewords = {
16
 
    'Name': u'Name',
17
 
    'Enabled': u'Enabled',
18
 
    'Timeout': u'Timeout',
19
 
    'LastCheckedOK': u'Last Successful Check',
20
 
    'LastApprovalRequest': u'Last Approval Request',
21
 
    'Created': u'Created',
22
 
    'Interval': u'Interval',
23
 
    'Host': u'Host',
24
 
    'Fingerprint': u'Fingerprint',
25
 
    'CheckerRunning': u'Check Is Running',
26
 
    'LastEnabled': u'Last Enabled',
27
 
    'ApprovalPending': u'Approval Is Pending',
28
 
    'ApprovedByDefault': u'Approved By Default',
29
 
    'ApprovalDelay': u"Approval Delay",
30
 
    'ApprovalDuration': u"Approval Duration",
31
 
    'Checker': u'Checker',
32
 
    }
33
 
defaultkeywords = ('Name', 'Enabled', 'Timeout', 'LastCheckedOK')
34
 
domain = 'se.bsnet.fukt'
35
 
busname = domain + '.Mandos'
36
 
server_path = '/'
37
 
server_interface = domain + '.Mandos'
38
 
client_interface = domain + '.Mandos.Client'
39
 
version = "1.0.14"
40
 
 
41
 
def timedelta_to_milliseconds(td):
42
 
    "Convert a datetime.timedelta object to milliseconds"
43
 
    return ((td.days * 24 * 60 * 60 * 1000)
44
 
            + (td.seconds * 1000)
45
 
            + (td.microseconds // 1000))
46
 
 
47
 
def milliseconds_to_string(ms):
48
 
    td = datetime.timedelta(0, 0, 0, ms)
49
 
    return (u"%(days)s%(hours)02d:%(minutes)02d:%(seconds)02d"
50
 
            % { "days": "%dT" % td.days if td.days else "",
51
 
                "hours": td.seconds // 3600,
52
 
                "minutes": (td.seconds % 3600) // 60,
53
 
                "seconds": td.seconds % 60,
54
 
                })
55
 
 
56
 
 
57
 
def string_to_delta(interval):
58
 
    """Parse a string and return a datetime.timedelta
59
 
 
60
 
    >>> string_to_delta('7d')
61
 
    datetime.timedelta(7)
62
 
    >>> string_to_delta('60s')
63
 
    datetime.timedelta(0, 60)
64
 
    >>> string_to_delta('60m')
65
 
    datetime.timedelta(0, 3600)
66
 
    >>> string_to_delta('24h')
67
 
    datetime.timedelta(1)
68
 
    >>> string_to_delta(u'1w')
69
 
    datetime.timedelta(7)
70
 
    >>> string_to_delta('5m 30s')
71
 
    datetime.timedelta(0, 330)
72
 
    """
73
 
    timevalue = datetime.timedelta(0)
74
 
    regexp = re.compile("\d+[dsmhw]")
75
 
    
76
 
    for s in regexp.findall(interval):
77
 
        try:
78
 
            suffix = unicode(s[-1])
79
 
            value = int(s[:-1])
80
 
            if suffix == u"d":
81
 
                delta = datetime.timedelta(value)
82
 
            elif suffix == u"s":
83
 
                delta = datetime.timedelta(0, value)
84
 
            elif suffix == u"m":
85
 
                delta = datetime.timedelta(0, 0, 0, 0, value)
86
 
            elif suffix == u"h":
87
 
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
88
 
            elif suffix == u"w":
89
 
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
90
 
            else:
91
 
                raise ValueError
92
 
        except (ValueError, IndexError):
93
 
            raise ValueError
94
 
        timevalue += delta
95
 
    return timevalue
96
 
 
97
 
def print_clients(clients, keywords):
98
 
    def valuetostring(value, keyword):
99
 
        if type(value) is dbus.Boolean:
100
 
            return u"Yes" if value else u"No"
101
 
        if keyword in (u"Timeout", u"Interval", u"ApprovalDelay",
102
 
                       u"ApprovalDuration"):
103
 
            return milliseconds_to_string(value)
104
 
        return unicode(value)
105
 
    
106
 
    # Create format string to print table rows
107
 
    format_string = u' '.join(u'%%-%ds' %
108
 
                              max(len(tablewords[key]),
109
 
                                  max(len(valuetostring(client[key],
110
 
                                                        key))
111
 
                                      for client in
112
 
                                      clients))
113
 
                              for key in keywords)
114
 
    # Print header line
115
 
    print format_string % tuple(tablewords[key] for key in keywords)
116
 
    for client in clients:
117
 
        print format_string % tuple(valuetostring(client[key], key)
118
 
                                    for key in keywords)
119
 
 
120
 
def has_actions(options):
121
 
    return any((options.enable,
122
 
                options.disable,
123
 
                options.bump_timeout,
124
 
                options.start_checker,
125
 
                options.stop_checker,
126
 
                options.is_enabled,
127
 
                options.remove,
128
 
                options.checker is not None,
129
 
                options.timeout is not None,
130
 
                options.interval is not None,
131
 
                options.approved_by_default is not None,
132
 
                options.approval_delay is not None,
133
 
                options.approval_duration is not None,
134
 
                options.host is not None,
135
 
                options.secret is not None,
136
 
                options.approve,
137
 
                options.deny))
138
 
        
139
 
def main():
140
 
        parser = OptionParser(version = "%%prog %s" % version)
141
 
        parser.add_option("-a", "--all", action="store_true",
142
 
                          help="Select all clients")
143
 
        parser.add_option("-v", "--verbose", action="store_true",
144
 
                          help="Print all fields")
145
 
        parser.add_option("-e", "--enable", action="store_true",
146
 
                          help="Enable client")
147
 
        parser.add_option("-d", "--disable", action="store_true",
148
 
                          help="disable client")
149
 
        parser.add_option("-b", "--bump-timeout", action="store_true",
150
 
                          help="Bump timeout for client")
151
 
        parser.add_option("--start-checker", action="store_true",
152
 
                          help="Start checker for client")
153
 
        parser.add_option("--stop-checker", action="store_true",
154
 
                          help="Stop checker for client")
155
 
        parser.add_option("-V", "--is-enabled", action="store_true",
156
 
                          help="Check if client is enabled")
157
 
        parser.add_option("-r", "--remove", action="store_true",
158
 
                          help="Remove client")
159
 
        parser.add_option("-c", "--checker", type="string",
160
 
                          help="Set checker command for client")
161
 
        parser.add_option("-t", "--timeout", type="string",
162
 
                          help="Set timeout for client")
163
 
        parser.add_option("-i", "--interval", type="string",
164
 
                          help="Set checker interval for client")
165
 
        parser.add_option("--approve-by-default", action="store_true",
166
 
                          dest=u"approved_by_default",
167
 
                          help="Set client to be approved by default")
168
 
        parser.add_option("--deny-by-default", action="store_false",
169
 
                          dest=u"approved_by_default",
170
 
                          help="Set client to be denied by default")
171
 
        parser.add_option("--approval-delay", type="string",
172
 
                          help="Set delay before client approve/deny")
173
 
        parser.add_option("--approval-duration", type="string",
174
 
                          help="Set duration of one client approval")
175
 
        parser.add_option("-H", "--host", type="string",
176
 
                          help="Set host for client")
177
 
        parser.add_option("-s", "--secret", type="string",
178
 
                          help="Set password blob (file) for client")
179
 
        parser.add_option("-A", "--approve", action="store_true",
180
 
                          help="Approve any current client request")
181
 
        parser.add_option("-D", "--deny", action="store_true",
182
 
                          help="Deny any current client request")
183
 
        options, client_names = parser.parse_args()
184
 
        
185
 
        if has_actions(options) and not client_names and not options.all:
186
 
            parser.error('Options require clients names or --all.')
187
 
        if options.verbose and has_actions(options):
188
 
            parser.error('--verbose can only be used alone or with'
189
 
                         ' --all.')
190
 
        if options.all and not has_actions(options):
191
 
            parser.error('--all requires an action.')
192
 
        
193
 
        try:
194
 
            bus = dbus.SystemBus()
195
 
            mandos_dbus_objc = bus.get_object(busname, server_path)
196
 
        except dbus.exceptions.DBusException:
197
 
            print >> sys.stderr, "Could not connect to Mandos server"
198
 
            sys.exit(1)
199
 
    
200
 
        mandos_serv = dbus.Interface(mandos_dbus_objc,
201
 
                                     dbus_interface = server_interface)
202
 
 
203
 
        #block stderr since dbus library prints to stderr
204
 
        null = os.open(os.path.devnull, os.O_RDWR)
205
 
        stderrcopy = os.dup(sys.stderr.fileno())
206
 
        os.dup2(null, sys.stderr.fileno())
207
 
        os.close(null)
208
 
        try:
209
 
            try:
210
 
                mandos_clients = mandos_serv.GetAllClientsWithProperties()
211
 
            finally:
212
 
                #restore stderr
213
 
                os.dup2(stderrcopy, sys.stderr.fileno())
214
 
                os.close(stderrcopy)
215
 
        except dbus.exceptions.DBusException, e:
216
 
            print >> sys.stderr, "Access denied: Accessing mandos server through dbus."
217
 
            sys.exit(1)
218
 
            
219
 
        # Compile dict of (clients: properties) to process
220
 
        clients={}
221
 
        
222
 
        if options.all or not client_names:
223
 
            clients = dict((bus.get_object(busname, path), properties)
224
 
                           for path, properties in
225
 
                           mandos_clients.iteritems())
226
 
        else:
227
 
            for name in client_names:
228
 
                for path, client in mandos_clients.iteritems():
229
 
                    if client['Name'] == name:
230
 
                        client_objc = bus.get_object(busname, path)
231
 
                        clients[client_objc] = client
232
 
                        break
233
 
                else:
234
 
                    print >> sys.stderr, "Client not found on server: %r" % name
235
 
                    sys.exit(1)
236
 
            
237
 
        if not has_actions(options) and clients:
238
 
            if options.verbose:
239
 
                keywords = ('Name', 'Enabled', 'Timeout',
240
 
                            'LastCheckedOK', 'Created', 'Interval',
241
 
                            'Host', 'Fingerprint', 'CheckerRunning',
242
 
                            'LastEnabled', 'ApprovalPending',
243
 
                            'ApprovedByDefault',
244
 
                            'LastApprovalRequest', 'ApprovalDelay',
245
 
                            'ApprovalDuration', 'Checker')
246
 
            else:
247
 
                keywords = defaultkeywords
248
 
            
249
 
            print_clients(clients.values(), keywords)
250
 
        else:
251
 
            # Process each client in the list by all selected options
252
 
            for client in clients:
253
 
                if options.remove:
254
 
                    mandos_serv.RemoveClient(client.__dbus_object_path__)
255
 
                if options.enable:
256
 
                    client.Enable(dbus_interface=client_interface)
257
 
                if options.disable:
258
 
                    client.Disable(dbus_interface=client_interface)
259
 
                if options.bump_timeout:
260
 
                    client.CheckedOK(dbus_interface=client_interface)
261
 
                if options.start_checker:
262
 
                    client.StartChecker(dbus_interface=client_interface)
263
 
                if options.stop_checker:
264
 
                    client.StopChecker(dbus_interface=client_interface)
265
 
                if options.is_enabled:
266
 
                    sys.exit(0 if client.Get(client_interface,
267
 
                                             u"Enabled",
268
 
                                             dbus_interface=dbus.PROPERTIES_IFACE)
269
 
                             else 1)
270
 
                if options.checker:
271
 
                    client.Set(client_interface, u"Checker", options.checker,
272
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
273
 
                if options.host:
274
 
                    client.Set(client_interface, u"Host", options.host,
275
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
276
 
                if options.interval:
277
 
                    client.Set(client_interface, u"Interval",
278
 
                               timedelta_to_milliseconds
279
 
                               (string_to_delta(options.interval)),
280
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
281
 
                if options.approval_delay:
282
 
                    client.Set(client_interface, u"ApprovalDelay",
283
 
                               timedelta_to_milliseconds
284
 
                               (string_to_delta(options.
285
 
                                                approval_delay)),
286
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
287
 
                if options.approval_duration:
288
 
                    client.Set(client_interface, u"ApprovalDuration",
289
 
                               timedelta_to_milliseconds
290
 
                               (string_to_delta(options.
291
 
                                                approval_duration)),
292
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
293
 
                if options.timeout:
294
 
                    client.Set(client_interface, u"Timeout",
295
 
                               timedelta_to_milliseconds
296
 
                               (string_to_delta(options.timeout)),
297
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
298
 
                if options.secret:
299
 
                    client.Set(client_interface, u"Secret",
300
 
                               dbus.ByteArray(open(options.secret,
301
 
                                                   u'rb').read()),
302
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
303
 
                if options.approved_by_default is not None:
304
 
                    client.Set(client_interface, u"ApprovedByDefault",
305
 
                               dbus.Boolean(options
306
 
                                            .approved_by_default),
307
 
                               dbus_interface=dbus.PROPERTIES_IFACE)
308
 
                if options.approve:
309
 
                    client.Approve(dbus.Boolean(True),
310
 
                                   dbus_interface=client_interface)
311
 
                elif options.deny:
312
 
                    client.Approve(dbus.Boolean(False),
313
 
                                   dbus_interface=client_interface)
314
 
 
315
 
if __name__ == '__main__':
316
 
    main()