4
4
# Mandos Monitor - Control and monitor the Mandos server
6
# Copyright © 2009-2016 Teddy Hogeborn
7
# Copyright © 2009-2016 Björn Påhlsson
6
# Copyright © 2009-2014 Teddy Hogeborn
7
# Copyright © 2009-2014 Björn Påhlsson
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
60
60
domain = 'se.recompile'
61
61
server_interface = domain + '.Mandos'
62
62
client_interface = domain + '.Mandos.Client'
66
dbus.OBJECT_MANAGER_IFACE
67
except AttributeError:
68
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
70
65
def isoformat_to_datetime(iso):
71
66
"Parse an ISO 8601 date string to a datetime.datetime()"
92
87
self.proxy = proxy_object # Mandos Client proxy object
93
88
self.properties = dict() if properties is None else properties
94
89
self.property_changed_match = (
95
self.proxy.connect_to_signal("PropertiesChanged",
96
self.properties_changed,
97
dbus.PROPERTIES_IFACE,
90
self.proxy.connect_to_signal("PropertyChanged",
91
self._property_changed,
100
95
if properties is None:
106
101
super(MandosClientPropertyCache, self).__init__(**kwargs)
108
def properties_changed(self, interface, properties, invalidated):
109
"""This is called whenever we get a PropertiesChanged signal
110
It updates the changed properties in the "properties" dict.
103
def _property_changed(self, property, value):
104
"""Helper which takes positional arguments"""
105
return self.property_changed(property=property, value=value)
107
def property_changed(self, property=None, value=None):
108
"""This is called whenever we get a PropertyChanged signal
109
It updates the changed property in the "properties" dict.
112
111
# Update properties dict with new value
113
if interface == client_interface:
114
self.properties.update(properties)
112
self.properties[property] = value
116
114
def delete(self):
117
115
self.property_changed_match.remove()
182
180
def checker_completed(self, exitstatus, condition, command):
183
181
if exitstatus == 0:
184
self.logger('Checker for client {} (command "{}")'
182
self.logger('Checker for client {0} (command "{1}")'
185
183
' succeeded'.format(self.properties["Name"],
186
184
command), level=0)
190
188
if os.WIFEXITED(condition):
191
self.logger('Checker for client {} (command "{}") failed'
189
self.logger('Checker for client {0} (command "{1}")'
190
' failed with exit code {2}'
193
191
.format(self.properties["Name"], command,
194
192
os.WEXITSTATUS(condition)))
195
193
elif os.WIFSIGNALED(condition):
196
self.logger('Checker for client {} (command "{}") was'
197
' killed by signal {}'
194
self.logger('Checker for client {0} (command "{1}") was'
195
' killed by signal {2}'
198
196
.format(self.properties["Name"], command,
199
197
os.WTERMSIG(condition)))
198
elif os.WCOREDUMP(condition):
199
self.logger('Checker for client {0} (command "{1}")'
201
.format(self.properties["Name"], command))
203
self.logger('Checker for client {0} completed'
205
.format(self.properties["Name"]))
202
208
def checker_started(self, command):
203
209
"""Server signals that a checker started."""
204
self.logger('Client {} started checker "{}"'
210
self.logger('Client {0} started checker "{1}"'
205
211
.format(self.properties["Name"],
206
212
command), level=0)
208
214
def got_secret(self):
209
self.logger('Client {} received its secret'
215
self.logger('Client {0} received its secret'
210
216
.format(self.properties["Name"]))
212
218
def need_approval(self, timeout, default):
214
message = 'Client {} needs approval within {} seconds'
220
message = 'Client {0} needs approval within {1} seconds'
216
message = 'Client {} will get its secret in {} seconds'
222
message = 'Client {0} will get its secret in {1} seconds'
217
223
self.logger(message.format(self.properties["Name"],
220
226
def rejected(self, reason):
221
self.logger('Client {} was rejected; reason: {}'
227
self.logger('Client {0} was rejected; reason: {1}'
222
228
.format(self.properties["Name"], reason))
224
230
def selectable(self):
269
275
timer = datetime.timedelta()
270
276
if self.properties["ApprovedByDefault"]:
271
message = "Approval in {}. (d)eny?"
277
message = "Approval in {0}. (d)eny?"
273
message = "Denial in {}. (a)pprove?"
279
message = "Denial in {0}. (a)pprove?"
274
280
message = message.format(str(timer).rsplit(".", 1)[0])
275
281
self.using_timer(True)
276
282
elif self.properties["LastCheckerStatus"] != 0:
284
290
timer = max(expires - datetime.datetime.utcnow(),
285
291
datetime.timedelta())
286
292
message = ('A checker has failed! Time until client'
293
' gets disabled: {0}'
288
294
.format(str(timer).rsplit(".", 1)[0]))
289
295
self.using_timer(True)
291
297
message = "enabled"
292
298
self.using_timer(False)
293
self._text = "{}{}".format(base, message)
299
self._text = "{0}{1}".format(base, message)
295
301
if not urwid.supports_unicode():
296
302
self._text = self._text.encode("ascii", "replace")
336
342
This overrides the method from urwid.FlowWidget"""
338
self.proxy.Set(client_interface, "Enabled",
339
dbus.Boolean(True), ignore_reply = True,
340
dbus_interface = dbus.PROPERTIES_IFACE)
344
self.proxy.Enable(dbus_interface = client_interface,
342
self.proxy.Set(client_interface, "Enabled", False,
344
dbus_interface = dbus.PROPERTIES_IFACE)
347
self.proxy.Disable(dbus_interface = client_interface,
346
350
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
347
351
dbus_interface = client_interface,
356
360
ignore_reply=True)
358
self.proxy.Set(client_interface, "CheckerRunning",
359
dbus.Boolean(True), ignore_reply = True,
360
dbus_interface = dbus.PROPERTIES_IFACE)
362
self.proxy.StartChecker(dbus_interface = client_interface,
362
self.proxy.Set(client_interface, "CheckerRunning",
363
dbus.Boolean(False), ignore_reply = True,
364
dbus_interface = dbus.PROPERTIES_IFACE)
365
self.proxy.StopChecker(dbus_interface = client_interface,
366
368
self.proxy.CheckedOK(dbus_interface = client_interface,
367
369
ignore_reply=True)
378
def properties_changed(self, interface, properties, invalidated):
379
"""Call self.update() if any properties changed.
380
def property_changed(self, property=None, **kwargs):
381
"""Call self.update() if old value is not new value.
380
382
This overrides the method from MandosClientPropertyCache"""
381
old_values = { key: self.properties.get(key)
382
for key in properties.keys() }
383
super(MandosClientWidget, self).properties_changed(
384
interface, properties, invalidated)
385
if any(old_values[key] != self.properties.get(key)
386
for key in old_values):
383
property_name = str(property)
384
old_value = self.properties.get(property_name)
385
super(MandosClientWidget, self).property_changed(
386
property=property, **kwargs)
387
if self.properties.get(property_name) != old_value:
468
469
self.main_loop = gobject.MainLoop()
470
471
def client_not_found(self, fingerprint, address):
471
self.log_message("Client with address {} and fingerprint {}"
472
" could not be found"
472
self.log_message("Client with address {0} and fingerprint"
473
" {1} could not be found"
473
474
.format(address, fingerprint))
475
476
def rebuild(self):
493
494
if level < self.log_level:
495
496
timestamp = datetime.datetime.now().isoformat()
496
self.log_message_raw("{}: {}".format(timestamp, message),
497
self.log_message_raw("{0}: {1}".format(timestamp, message),
499
500
def log_message_raw(self, markup, level=1):
512
513
"""Toggle visibility of the log buffer."""
513
514
self.log_visible = not self.log_visible
515
self.log_message("Log visibility changed to: {}"
516
self.log_message("Log visibility changed to: {0}"
516
517
.format(self.log_visible), level=0)
518
519
def change_log_display(self):
524
525
self.log_wrap = "clip"
525
526
for textwidget in self.log:
526
527
textwidget.set_wrap_mode(self.log_wrap)
527
self.log_message("Wrap mode: {}".format(self.log_wrap),
528
self.log_message("Wrap mode: {0}".format(self.log_wrap),
530
def find_and_remove_client(self, path, interfaces):
531
def find_and_remove_client(self, path, name):
531
532
"""Find a client by its object path and remove it.
533
This is connected to the InterfacesRemoved signal from the
534
This is connected to the ClientRemoved signal from the
534
535
Mandos server object."""
535
if client_interface not in interfaces:
536
# Not a Mandos client object; ignore
539
537
client = self.clients_dict[path]
542
self.log_message("Unknown client {!r} removed"
540
self.log_message("Unknown client {0!r} ({1!r}) removed"
547
def add_new_client(self, path, ifs_and_props):
548
"""Find a client by its object path and remove it.
550
This is connected to the InterfacesAdded signal from the
551
Mandos server object.
553
if client_interface not in ifs_and_props:
554
# Not a Mandos client object; ignore
545
def add_new_client(self, path):
556
546
client_proxy_object = self.bus.get_object(self.busname, path)
557
547
self.add_client(MandosClientWidget(server_proxy_object
558
548
=self.mandos_serv,
607
594
mandos_clients = dbus.Dictionary()
609
596
(self.mandos_serv
610
.connect_to_signal("InterfacesRemoved",
597
.connect_to_signal("ClientRemoved",
611
598
self.find_and_remove_client,
613
= dbus.OBJECT_MANAGER_IFACE,
599
dbus_interface=server_interface,
614
600
byte_arrays=True))
615
601
(self.mandos_serv
616
.connect_to_signal("InterfacesAdded",
602
.connect_to_signal("ClientAdded",
617
603
self.add_new_client,
619
= dbus.OBJECT_MANAGER_IFACE,
604
dbus_interface=server_interface,
620
605
byte_arrays=True))
621
606
(self.mandos_serv
622
607
.connect_to_signal("ClientNotFound",