165
154
u' %%(levelname)s: %%(message)s'
170
except dbus.exceptions.DBusException, error:
171
logger.critical(u"DBusException: %s", error)
174
158
self.rename_count += 1
175
159
def remove(self):
176
160
"""Derived from the Avahi example code"""
177
if self.group is not None:
161
if group is not None:
180
164
"""Derived from the Avahi example code"""
181
if self.group is None:
182
self.group = dbus.Interface(
183
self.bus.get_object(avahi.DBUS_NAME,
184
self.server.EntryGroupNew()),
185
avahi.DBUS_INTERFACE_ENTRY_GROUP)
186
self.group.connect_to_signal('StateChanged',
188
.entry_group_state_changed)
167
group = dbus.Interface(bus.get_object
169
server.EntryGroupNew()),
170
avahi.DBUS_INTERFACE_ENTRY_GROUP)
171
group.connect_to_signal('StateChanged',
172
entry_group_state_changed)
189
173
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
190
self.name, self.type)
191
self.group.AddService(
194
dbus.UInt32(0), # flags
195
self.name, self.type,
196
self.domain, self.host,
197
dbus.UInt16(self.port),
198
avahi.string_array_to_txt_array(self.TXT))
200
def entry_group_state_changed(self, state, error):
201
"""Derived from the Avahi example code"""
202
logger.debug(u"Avahi entry group state change: %i", state)
204
if state == avahi.ENTRY_GROUP_ESTABLISHED:
205
logger.debug(u"Zeroconf service established.")
206
elif state == avahi.ENTRY_GROUP_COLLISION:
207
logger.warning(u"Zeroconf service name collision.")
209
elif state == avahi.ENTRY_GROUP_FAILURE:
210
logger.critical(u"Avahi: Error in group state changed %s",
212
raise AvahiGroupError(u"State changed: %s"
215
"""Derived from the Avahi example code"""
216
if self.group is not None:
219
def server_state_changed(self, state):
220
"""Derived from the Avahi example code"""
221
logger.debug(u"Avahi server state change: %i", state)
222
if state == avahi.SERVER_COLLISION:
223
logger.error(u"Zeroconf server name collision")
225
elif state == avahi.SERVER_RUNNING:
228
"""Derived from the Avahi example code"""
229
if self.server is None:
230
self.server = dbus.Interface(
231
self.bus.get_object(avahi.DBUS_NAME,
232
avahi.DBUS_PATH_SERVER),
233
avahi.DBUS_INTERFACE_SERVER)
234
self.server.connect_to_signal(u"StateChanged",
235
self.server_state_changed)
236
self.server_state_changed(self.server.GetState())
174
service.name, service.type)
176
self.interface, # interface
177
self.protocol, # protocol
178
dbus.UInt32(0), # flags
179
self.name, self.type,
180
self.domain, self.host,
181
dbus.UInt16(self.port),
182
avahi.string_array_to_txt_array(self.TXT))
185
# From the Avahi example code:
186
group = None # our entry group
187
# End of Avahi example code
190
def _datetime_to_dbus(dt, variant_level=0):
191
"""Convert a UTC datetime.datetime() to a D-Bus type."""
192
return dbus.String(dt.isoformat(), variant_level=variant_level)
239
195
class Client(object):
240
196
"""A representation of a client host served by this server.
243
_approved: bool(); 'None' if not yet approved/disapproved
244
approval_delay: datetime.timedelta(); Time to wait for approval
245
approval_duration: datetime.timedelta(); Duration of one approval
199
name: string; from the config file, used in log messages and
201
fingerprint: string (40 or 32 hexadecimal digits); used to
202
uniquely identify the client
203
secret: bytestring; sent verbatim (over TLS) to client
204
host: string; available for use by the checker command
205
created: datetime.datetime(); (UTC) object creation
206
last_enabled: datetime.datetime(); (UTC)
208
last_checked_ok: datetime.datetime(); (UTC) or None
209
timeout: datetime.timedelta(); How long from last_checked_ok
210
until this client is invalid
211
interval: datetime.timedelta(); How often to start a new checker
212
disable_hook: If set, called by disable() as disable_hook(self)
246
213
checker: subprocess.Popen(); a running checker process used
247
214
to see if the client lives.
248
215
'None' if no process is running.
249
checker_callback_tag: a gobject event source tag, or None
250
checker_command: string; External command which is run to check
251
if client lives. %() expansions are done at
216
checker_initiator_tag: a gobject event source tag, or None
217
disable_initiator_tag: - '' -
218
checker_callback_tag: - '' -
219
checker_command: string; External command which is run to check if
220
client lives. %() expansions are done at
252
221
runtime with vars(self) as dict, so that for
253
222
instance %(name)s can be used in the command.
254
checker_initiator_tag: a gobject event source tag, or None
255
created: datetime.datetime(); (UTC) object creation
256
223
current_checker_command: string; current running checker_command
257
disable_hook: If set, called by disable() as disable_hook(self)
258
disable_initiator_tag: a gobject event source tag, or None
260
fingerprint: string (40 or 32 hexadecimal digits); used to
261
uniquely identify the client
262
host: string; available for use by the checker command
263
interval: datetime.timedelta(); How often to start a new checker
264
last_approval_request: datetime.datetime(); (UTC) or None
265
last_checked_ok: datetime.datetime(); (UTC) or None
266
last_enabled: datetime.datetime(); (UTC)
267
name: string; from the config file, used in log messages and
269
secret: bytestring; sent verbatim (over TLS) to client
270
timeout: datetime.timedelta(); How long from last_checked_ok
271
until this client is disabled
272
runtime_expansions: Allowed attributes for runtime expansion.
275
runtime_expansions = (u"approval_delay", u"approval_duration",
276
u"created", u"enabled", u"fingerprint",
277
u"host", u"interval", u"last_checked_ok",
278
u"last_enabled", u"name", u"timeout")
281
def _timedelta_to_milliseconds(td):
282
"Convert a datetime.timedelta() to milliseconds"
283
return ((td.days * 24 * 60 * 60 * 1000)
284
+ (td.seconds * 1000)
285
+ (td.microseconds // 1000))
227
def _datetime_to_milliseconds(dt):
228
"Convert a datetime.datetime() to milliseconds"
229
return ((dt.days * 24 * 60 * 60 * 1000)
230
+ (dt.seconds * 1000)
231
+ (dt.microseconds // 1000))
287
233
def timeout_milliseconds(self):
288
234
"Return the 'timeout' attribute in milliseconds"
289
return self._timedelta_to_milliseconds(self.timeout)
235
return self._datetime_to_milliseconds(self.timeout)
291
237
def interval_milliseconds(self):
292
238
"Return the 'interval' attribute in milliseconds"
293
return self._timedelta_to_milliseconds(self.interval)
295
def approval_delay_milliseconds(self):
296
return self._timedelta_to_milliseconds(self.approval_delay)
239
return self._datetime_to_milliseconds(self.interval)
298
241
def __init__(self, name = None, disable_hook=None, config=None):
299
242
"""Note: the 'checker' key in 'config' sets the
512
423
logger.debug(u"Stopping checker for %(name)s", vars(self))
514
425
os.kill(self.checker.pid, signal.SIGTERM)
516
427
#if self.checker.poll() is None:
517
428
# os.kill(self.checker.pid, signal.SIGKILL)
518
429
except OSError, error:
519
430
if error.errno != errno.ESRCH: # No such process
521
432
self.checker = None
523
def dbus_service_property(dbus_interface, signature=u"v",
524
access=u"readwrite", byte_arrays=False):
525
"""Decorators for marking methods of a DBusObjectWithProperties to
526
become properties on the D-Bus.
528
The decorated method will be called with no arguments by "Get"
529
and with one argument by "Set".
531
The parameters, where they are supported, are the same as
532
dbus.service.method, except there is only "signature", since the
533
type from Get() and the type sent to Set() is the same.
535
# Encoding deeply encoded byte arrays is not supported yet by the
536
# "Set" method, so we fail early here:
537
if byte_arrays and signature != u"ay":
538
raise ValueError(u"Byte arrays not supported for non-'ay'"
539
u" signature %r" % signature)
541
func._dbus_is_property = True
542
func._dbus_interface = dbus_interface
543
func._dbus_signature = signature
544
func._dbus_access = access
545
func._dbus_name = func.__name__
546
if func._dbus_name.endswith(u"_dbus_property"):
547
func._dbus_name = func._dbus_name[:-14]
548
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
553
class DBusPropertyException(dbus.exceptions.DBusException):
554
"""A base class for D-Bus property-related exceptions
556
def __unicode__(self):
557
return unicode(str(self))
560
class DBusPropertyAccessException(DBusPropertyException):
561
"""A property's access permissions disallows an operation.
566
class DBusPropertyNotFound(DBusPropertyException):
567
"""An attempt was made to access a non-existing property.
572
class DBusObjectWithProperties(dbus.service.Object):
573
"""A D-Bus object with properties.
575
Classes inheriting from this can use the dbus_service_property
576
decorator to expose methods as D-Bus properties. It exposes the
577
standard Get(), Set(), and GetAll() methods on the D-Bus.
581
def _is_dbus_property(obj):
582
return getattr(obj, u"_dbus_is_property", False)
584
def _get_all_dbus_properties(self):
585
"""Returns a generator of (name, attribute) pairs
587
return ((prop._dbus_name, prop)
589
inspect.getmembers(self, self._is_dbus_property))
591
def _get_dbus_property(self, interface_name, property_name):
592
"""Returns a bound method if one exists which is a D-Bus
593
property with the specified name and interface.
595
for name in (property_name,
596
property_name + u"_dbus_property"):
597
prop = getattr(self, name, None)
599
or not self._is_dbus_property(prop)
600
or prop._dbus_name != property_name
601
or (interface_name and prop._dbus_interface
602
and interface_name != prop._dbus_interface)):
606
raise DBusPropertyNotFound(self.dbus_object_path + u":"
607
+ interface_name + u"."
610
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
612
def Get(self, interface_name, property_name):
613
"""Standard D-Bus property Get() method, see D-Bus standard.
615
prop = self._get_dbus_property(interface_name, property_name)
616
if prop._dbus_access == u"write":
617
raise DBusPropertyAccessException(property_name)
619
if not hasattr(value, u"variant_level"):
621
return type(value)(value, variant_level=value.variant_level+1)
623
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
624
def Set(self, interface_name, property_name, value):
625
"""Standard D-Bus property Set() method, see D-Bus standard.
627
prop = self._get_dbus_property(interface_name, property_name)
628
if prop._dbus_access == u"read":
629
raise DBusPropertyAccessException(property_name)
630
if prop._dbus_get_args_options[u"byte_arrays"]:
631
# The byte_arrays option is not supported yet on
632
# signatures other than "ay".
633
if prop._dbus_signature != u"ay":
635
value = dbus.ByteArray(''.join(unichr(byte)
639
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
640
out_signature=u"a{sv}")
641
def GetAll(self, interface_name):
642
"""Standard D-Bus property GetAll() method, see D-Bus
645
Note: Will not include properties with access="write".
648
for name, prop in self._get_all_dbus_properties():
650
and interface_name != prop._dbus_interface):
651
# Interface non-empty but did not match
653
# Ignore write-only properties
654
if prop._dbus_access == u"write":
657
if not hasattr(value, u"variant_level"):
660
all[name] = type(value)(value, variant_level=
661
value.variant_level+1)
662
return dbus.Dictionary(all, signature=u"sv")
664
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
666
path_keyword='object_path',
667
connection_keyword='connection')
668
def Introspect(self, object_path, connection):
669
"""Standard D-Bus method, overloaded to insert property tags.
671
xmlstring = dbus.service.Object.Introspect(self, object_path,
674
document = xml.dom.minidom.parseString(xmlstring)
675
def make_tag(document, name, prop):
676
e = document.createElement(u"property")
677
e.setAttribute(u"name", name)
678
e.setAttribute(u"type", prop._dbus_signature)
679
e.setAttribute(u"access", prop._dbus_access)
681
for if_tag in document.getElementsByTagName(u"interface"):
682
for tag in (make_tag(document, name, prop)
684
in self._get_all_dbus_properties()
685
if prop._dbus_interface
686
== if_tag.getAttribute(u"name")):
687
if_tag.appendChild(tag)
688
# Add the names to the return values for the
689
# "org.freedesktop.DBus.Properties" methods
690
if (if_tag.getAttribute(u"name")
691
== u"org.freedesktop.DBus.Properties"):
692
for cn in if_tag.getElementsByTagName(u"method"):
693
if cn.getAttribute(u"name") == u"Get":
694
for arg in cn.getElementsByTagName(u"arg"):
695
if (arg.getAttribute(u"direction")
697
arg.setAttribute(u"name", u"value")
698
elif cn.getAttribute(u"name") == u"GetAll":
699
for arg in cn.getElementsByTagName(u"arg"):
700
if (arg.getAttribute(u"direction")
702
arg.setAttribute(u"name", u"props")
703
xmlstring = document.toxml(u"utf-8")
705
except (AttributeError, xml.dom.DOMException,
706
xml.parsers.expat.ExpatError), error:
707
logger.error(u"Failed to override Introspection method",
712
class ClientDBus(Client, DBusObjectWithProperties):
434
def still_valid(self):
435
"""Has the timeout not yet passed for this client?"""
436
if not getattr(self, u"enabled", False):
438
now = datetime.datetime.utcnow()
439
if self.last_checked_ok is None:
440
return now < (self.created + self.timeout)
442
return now < (self.last_checked_ok + self.timeout)
445
class ClientDBus(Client, dbus.service.Object):
713
446
"""A Client class using D-Bus
716
dbus_object_path: dbus.ObjectPath
717
bus: dbus.SystemBus()
449
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
720
runtime_expansions = (Client.runtime_expansions
721
+ (u"dbus_object_path",))
723
451
# dbus.service.Object doesn't use super(), so we can't either.
725
def __init__(self, bus = None, *args, **kwargs):
726
self._approvals_pending = 0
453
def __init__(self, *args, **kwargs):
728
454
Client.__init__(self, *args, **kwargs)
729
455
# Only now, when this client is initialized, can it show up on
731
client_object_name = unicode(self.name).translate(
732
{ord(u"."): ord(u"_"),
733
ord(u"-"): ord(u"_")})
734
457
self.dbus_object_path = (dbus.ObjectPath
735
(u"/clients/" + client_object_name))
736
DBusObjectWithProperties.__init__(self, self.bus,
737
self.dbus_object_path)
739
def _get_approvals_pending(self):
740
return self._approvals_pending
741
def _set_approvals_pending(self, value):
742
old_value = self._approvals_pending
743
self._approvals_pending = value
745
if (hasattr(self, "dbus_object_path")
746
and bval is not bool(old_value)):
747
dbus_bool = dbus.Boolean(bval, variant_level=1)
748
self.PropertyChanged(dbus.String(u"ApprovalPending"),
751
approvals_pending = property(_get_approvals_pending,
752
_set_approvals_pending)
753
del _get_approvals_pending, _set_approvals_pending
756
def _datetime_to_dbus(dt, variant_level=0):
757
"""Convert a UTC datetime.datetime() to a D-Bus type."""
758
return dbus.String(dt.isoformat(),
759
variant_level=variant_level)
459
+ self.name.replace(u".", u"_")))
460
dbus.service.Object.__init__(self, bus,
461
self.dbus_object_path)
761
462
def enable(self):
762
463
oldstate = getattr(self, u"enabled", False)
763
464
r = Client.enable(self)
764
465
if oldstate != self.enabled:
765
466
# Emit D-Bus signals
766
self.PropertyChanged(dbus.String(u"Enabled"),
467
self.PropertyChanged(dbus.String(u"enabled"),
767
468
dbus.Boolean(True, variant_level=1))
768
self.PropertyChanged(
769
dbus.String(u"LastEnabled"),
770
self._datetime_to_dbus(self.last_enabled,
469
self.PropertyChanged(dbus.String(u"last_enabled"),
470
(_datetime_to_dbus(self.last_enabled,
774
def disable(self, quiet = False):
474
def disable(self, signal = True):
775
475
oldstate = getattr(self, u"enabled", False)
776
r = Client.disable(self, quiet=quiet)
777
if not quiet and oldstate != self.enabled:
476
r = Client.disable(self)
477
if signal and oldstate != self.enabled:
778
478
# Emit D-Bus signal
779
self.PropertyChanged(dbus.String(u"Enabled"),
479
self.PropertyChanged(dbus.String(u"enabled"),
780
480
dbus.Boolean(False, variant_level=1))
569
# GetAllProperties - method
570
@dbus.service.method(_interface, out_signature=u"a{sv}")
571
def GetAllProperties(self):
573
return dbus.Dictionary({
574
dbus.String(u"name"):
575
dbus.String(self.name, variant_level=1),
576
dbus.String(u"fingerprint"):
577
dbus.String(self.fingerprint, variant_level=1),
578
dbus.String(u"host"):
579
dbus.String(self.host, variant_level=1),
580
dbus.String(u"created"):
581
_datetime_to_dbus(self.created, variant_level=1),
582
dbus.String(u"last_enabled"):
583
(_datetime_to_dbus(self.last_enabled,
585
if self.last_enabled is not None
586
else dbus.Boolean(False, variant_level=1)),
587
dbus.String(u"enabled"):
588
dbus.Boolean(self.enabled, variant_level=1),
589
dbus.String(u"last_checked_ok"):
590
(_datetime_to_dbus(self.last_checked_ok,
592
if self.last_checked_ok is not None
593
else dbus.Boolean (False, variant_level=1)),
594
dbus.String(u"timeout"):
595
dbus.UInt64(self.timeout_milliseconds(),
597
dbus.String(u"interval"):
598
dbus.UInt64(self.interval_milliseconds(),
600
dbus.String(u"checker"):
601
dbus.String(self.checker_command,
603
dbus.String(u"checker_running"):
604
dbus.Boolean(self.checker is not None,
606
dbus.String(u"object_path"):
607
dbus.ObjectPath(self.dbus_object_path,
611
# IsStillValid - method
612
@dbus.service.method(_interface, out_signature=u"b")
613
def IsStillValid(self):
614
return self.still_valid()
887
616
# PropertyChanged - signal
888
617
@dbus.service.signal(_interface, signature=u"sv")
889
618
def PropertyChanged(self, property, value):
622
# ReceivedSecret - signal
894
623
@dbus.service.signal(_interface)
897
Is sent after a successful transfer of secret from the Mandos
898
server to mandos-client
624
def ReceivedSecret(self):
902
628
# Rejected - signal
903
@dbus.service.signal(_interface, signature=u"s")
904
def Rejected(self, reason):
629
@dbus.service.signal(_interface)
908
# NeedApproval - signal
909
@dbus.service.signal(_interface, signature=u"tb")
910
def NeedApproval(self, timeout, default):
912
return self.need_approval()
917
@dbus.service.method(_interface, in_signature=u"b")
918
def Approve(self, value):
922
@dbus.service.method(_interface)
924
return self.checked_ok()
634
# SetChecker - method
635
@dbus.service.method(_interface, in_signature=u"s")
636
def SetChecker(self, checker):
637
"D-Bus setter method"
638
self.checker_command = checker
640
self.PropertyChanged(dbus.String(u"checker"),
641
dbus.String(self.checker_command,
645
@dbus.service.method(_interface, in_signature=u"s")
646
def SetHost(self, host):
647
"D-Bus setter method"
650
self.PropertyChanged(dbus.String(u"host"),
651
dbus.String(self.host, variant_level=1))
653
# SetInterval - method
654
@dbus.service.method(_interface, in_signature=u"t")
655
def SetInterval(self, milliseconds):
656
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
658
self.PropertyChanged(dbus.String(u"interval"),
659
(dbus.UInt64(self.interval_milliseconds(),
663
@dbus.service.method(_interface, in_signature=u"ay",
665
def SetSecret(self, secret):
666
"D-Bus setter method"
667
self.secret = str(secret)
669
# SetTimeout - method
670
@dbus.service.method(_interface, in_signature=u"t")
671
def SetTimeout(self, milliseconds):
672
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
674
self.PropertyChanged(dbus.String(u"timeout"),
675
(dbus.UInt64(self.timeout_milliseconds(),
926
678
# Enable - method
927
679
@dbus.service.method(_interface)
946
698
def StopChecker(self):
947
699
self.stop_checker()
951
# ApprovalPending - property
952
@dbus_service_property(_interface, signature=u"b", access=u"read")
953
def ApprovalPending_dbus_property(self):
954
return dbus.Boolean(bool(self.approvals_pending))
956
# ApprovedByDefault - property
957
@dbus_service_property(_interface, signature=u"b",
959
def ApprovedByDefault_dbus_property(self, value=None):
960
if value is None: # get
961
return dbus.Boolean(self.approved_by_default)
962
self.approved_by_default = bool(value)
964
self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
965
dbus.Boolean(value, variant_level=1))
967
# ApprovalDelay - property
968
@dbus_service_property(_interface, signature=u"t",
970
def ApprovalDelay_dbus_property(self, value=None):
971
if value is None: # get
972
return dbus.UInt64(self.approval_delay_milliseconds())
973
self.approval_delay = datetime.timedelta(0, 0, 0, value)
975
self.PropertyChanged(dbus.String(u"ApprovalDelay"),
976
dbus.UInt64(value, variant_level=1))
978
# ApprovalDuration - property
979
@dbus_service_property(_interface, signature=u"t",
981
def ApprovalDuration_dbus_property(self, value=None):
982
if value is None: # get
983
return dbus.UInt64(self._timedelta_to_milliseconds(
984
self.approval_duration))
985
self.approval_duration = datetime.timedelta(0, 0, 0, value)
987
self.PropertyChanged(dbus.String(u"ApprovalDuration"),
988
dbus.UInt64(value, variant_level=1))
991
@dbus_service_property(_interface, signature=u"s", access=u"read")
992
def Name_dbus_property(self):
993
return dbus.String(self.name)
995
# Fingerprint - property
996
@dbus_service_property(_interface, signature=u"s", access=u"read")
997
def Fingerprint_dbus_property(self):
998
return dbus.String(self.fingerprint)
1001
@dbus_service_property(_interface, signature=u"s",
1002
access=u"readwrite")
1003
def Host_dbus_property(self, value=None):
1004
if value is None: # get
1005
return dbus.String(self.host)
1008
self.PropertyChanged(dbus.String(u"Host"),
1009
dbus.String(value, variant_level=1))
1011
# Created - property
1012
@dbus_service_property(_interface, signature=u"s", access=u"read")
1013
def Created_dbus_property(self):
1014
return dbus.String(self._datetime_to_dbus(self.created))
1016
# LastEnabled - property
1017
@dbus_service_property(_interface, signature=u"s", access=u"read")
1018
def LastEnabled_dbus_property(self):
1019
if self.last_enabled is None:
1020
return dbus.String(u"")
1021
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1023
# Enabled - property
1024
@dbus_service_property(_interface, signature=u"b",
1025
access=u"readwrite")
1026
def Enabled_dbus_property(self, value=None):
1027
if value is None: # get
1028
return dbus.Boolean(self.enabled)
1034
# LastCheckedOK - property
1035
@dbus_service_property(_interface, signature=u"s",
1036
access=u"readwrite")
1037
def LastCheckedOK_dbus_property(self, value=None):
1038
if value is not None:
1041
if self.last_checked_ok is None:
1042
return dbus.String(u"")
1043
return dbus.String(self._datetime_to_dbus(self
1046
# LastApprovalRequest - property
1047
@dbus_service_property(_interface, signature=u"s", access=u"read")
1048
def LastApprovalRequest_dbus_property(self):
1049
if self.last_approval_request is None:
1050
return dbus.String(u"")
1051
return dbus.String(self.
1052
_datetime_to_dbus(self
1053
.last_approval_request))
1055
# Timeout - property
1056
@dbus_service_property(_interface, signature=u"t",
1057
access=u"readwrite")
1058
def Timeout_dbus_property(self, value=None):
1059
if value is None: # get
1060
return dbus.UInt64(self.timeout_milliseconds())
1061
self.timeout = datetime.timedelta(0, 0, 0, value)
1063
self.PropertyChanged(dbus.String(u"Timeout"),
1064
dbus.UInt64(value, variant_level=1))
1065
if getattr(self, u"disable_initiator_tag", None) is None:
1067
# Reschedule timeout
1068
gobject.source_remove(self.disable_initiator_tag)
1069
self.disable_initiator_tag = None
1070
time_to_die = (self.
1071
_timedelta_to_milliseconds((self
1076
if time_to_die <= 0:
1077
# The timeout has passed
1080
self.disable_initiator_tag = (gobject.timeout_add
1081
(time_to_die, self.disable))
1083
# Interval - property
1084
@dbus_service_property(_interface, signature=u"t",
1085
access=u"readwrite")
1086
def Interval_dbus_property(self, value=None):
1087
if value is None: # get
1088
return dbus.UInt64(self.interval_milliseconds())
1089
self.interval = datetime.timedelta(0, 0, 0, value)
1091
self.PropertyChanged(dbus.String(u"Interval"),
1092
dbus.UInt64(value, variant_level=1))
1093
if getattr(self, u"checker_initiator_tag", None) is None:
1095
# Reschedule checker run
1096
gobject.source_remove(self.checker_initiator_tag)
1097
self.checker_initiator_tag = (gobject.timeout_add
1098
(value, self.start_checker))
1099
self.start_checker() # Start one now, too
1101
# Checker - property
1102
@dbus_service_property(_interface, signature=u"s",
1103
access=u"readwrite")
1104
def Checker_dbus_property(self, value=None):
1105
if value is None: # get
1106
return dbus.String(self.checker_command)
1107
self.checker_command = value
1109
self.PropertyChanged(dbus.String(u"Checker"),
1110
dbus.String(self.checker_command,
1113
# CheckerRunning - property
1114
@dbus_service_property(_interface, signature=u"b",
1115
access=u"readwrite")
1116
def CheckerRunning_dbus_property(self, value=None):
1117
if value is None: # get
1118
return dbus.Boolean(self.checker is not None)
1120
self.start_checker()
1124
# ObjectPath - property
1125
@dbus_service_property(_interface, signature=u"o", access=u"read")
1126
def ObjectPath_dbus_property(self):
1127
return self.dbus_object_path # is already a dbus.ObjectPath
1130
@dbus_service_property(_interface, signature=u"ay",
1131
access=u"write", byte_arrays=True)
1132
def Secret_dbus_property(self, value):
1133
self.secret = str(value)
1138
class ProxyClient(object):
1139
def __init__(self, child_pipe, fpr, address):
1140
self._pipe = child_pipe
1141
self._pipe.send(('init', fpr, address))
1142
if not self._pipe.recv():
1145
def __getattribute__(self, name):
1146
if(name == '_pipe'):
1147
return super(ProxyClient, self).__getattribute__(name)
1148
self._pipe.send(('getattr', name))
1149
data = self._pipe.recv()
1150
if data[0] == 'data':
1152
if data[0] == 'function':
1153
def func(*args, **kwargs):
1154
self._pipe.send(('funcall', name, args, kwargs))
1155
return self._pipe.recv()[1]
1158
def __setattr__(self, name, value):
1159
if(name == '_pipe'):
1160
return super(ProxyClient, self).__setattr__(name, value)
1161
self._pipe.send(('setattr', name, value))
1164
704
class ClientHandler(socketserver.BaseRequestHandler, object):
1165
705
"""A class to handle client connections.
1216
752
# established. Just abandon the request.
1218
754
logger.debug(u"Handshake succeeded")
1220
approval_required = False
1223
fpr = self.fingerprint(self.peer_certificate
1225
except (TypeError, gnutls.errors.GNUTLSError), error:
1226
logger.warning(u"Bad certificate: %s", error)
1228
logger.debug(u"Fingerprint: %s", fpr)
1231
client = ProxyClient(child_pipe, fpr,
1232
self.client_address)
1236
if client.approval_delay:
1237
delay = client.approval_delay
1238
client.approvals_pending += 1
1239
approval_required = True
1242
if not client.enabled:
1243
logger.warning(u"Client %s is disabled",
1245
if self.server.use_dbus:
1247
client.Rejected("Disabled")
1250
if client._approved or not client.approval_delay:
1251
#We are approved or approval is disabled
1253
elif client._approved is None:
1254
logger.info(u"Client %s needs approval",
1256
if self.server.use_dbus:
1258
client.NeedApproval(
1259
client.approval_delay_milliseconds(),
1260
client.approved_by_default)
1262
logger.warning(u"Client %s was not approved",
1264
if self.server.use_dbus:
1266
client.Rejected("Denied")
1269
#wait until timeout or approved
1270
#x = float(client._timedelta_to_milliseconds(delay))
1271
time = datetime.datetime.now()
1272
client.changedstate.acquire()
1273
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1274
client.changedstate.release()
1275
time2 = datetime.datetime.now()
1276
if (time2 - time) >= delay:
1277
if not client.approved_by_default:
1278
logger.warning("Client %s timed out while"
1279
" waiting for approval",
1281
if self.server.use_dbus:
1283
client.Rejected("Approval timed out")
1288
delay -= time2 - time
1291
while sent_size < len(client.secret):
1293
sent = session.send(client.secret[sent_size:])
1294
except (gnutls.errors.GNUTLSError), error:
1295
logger.warning("gnutls send failed")
1297
logger.debug(u"Sent: %d, remaining: %d",
1298
sent, len(client.secret)
1299
- (sent_size + sent))
1302
logger.info(u"Sending secret to %s", client.name)
1303
# bump the timeout as if seen
1305
if self.server.use_dbus:
756
fpr = self.fingerprint(self.peer_certificate(session))
757
except (TypeError, gnutls.errors.GNUTLSError), error:
758
logger.warning(u"Bad certificate: %s", error)
761
logger.debug(u"Fingerprint: %s", fpr)
1310
if approval_required:
1311
client.approvals_pending -= 1
1314
except (gnutls.errors.GNUTLSError), error:
1315
logger.warning("GnuTLS bye failed")
763
for c in self.server.clients:
764
if c.fingerprint == fpr:
768
ipc.write(u"NOTFOUND %s\n" % fpr)
771
# Have to check if client.still_valid(), since it is
772
# possible that the client timed out while establishing
773
# the GnuTLS session.
774
if not client.still_valid():
775
ipc.write(u"INVALID %s\n" % client.name)
778
ipc.write(u"SENDING %s\n" % client.name)
780
while sent_size < len(client.secret):
781
sent = session.send(client.secret[sent_size:])
782
logger.debug(u"Sent: %d, remaining: %d",
783
sent, len(client.secret)
784
- (sent_size + sent))
1318
789
def peer_certificate(session):
1381
class MultiprocessingMixIn(object):
1382
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1383
def sub_process_main(self, request, address):
1385
self.finish_request(request, address)
1387
self.handle_error(request, address)
1388
self.close_request(request)
1390
def process_request(self, request, address):
1391
"""Start a new process to process the request."""
1392
multiprocessing.Process(target = self.sub_process_main,
1393
args = (request, address)).start()
1395
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1396
""" adds a pipe to the MixIn """
852
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
853
"""Like socketserver.ForkingMixIn, but also pass a pipe.
855
Assumes a gobject.MainLoop event loop.
1397
857
def process_request(self, request, client_address):
1398
858
"""Overrides and wraps the original process_request().
1400
This function creates a new pipe in self.pipe
860
This function creates a new pipe in self.pipe
1402
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1404
super(MultiprocessingMixInWithPipe,
862
self.pipe = os.pipe()
863
super(ForkingMixInWithPipe,
1405
864
self).process_request(request, client_address)
1406
self.child_pipe.close()
1407
self.add_pipe(parent_pipe)
1409
def add_pipe(self, parent_pipe):
865
os.close(self.pipe[1]) # close write end
866
# Call "handle_ipc" for both data and EOF events
867
gobject.io_add_watch(self.pipe[0],
868
gobject.IO_IN | gobject.IO_HUP,
870
def handle_ipc(source, condition):
1410
871
"""Dummy function; override as necessary"""
1413
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
876
class IPv6_TCPServer(ForkingMixInWithPipe,
1414
877
socketserver.TCPServer, object):
1415
878
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1524
953
for cond, name in
1525
954
condition_names.iteritems()
1526
955
if cond & condition)
1527
# error or the other end of multiprocessing.Pipe has closed
1528
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1531
# Read a request from the child
1532
request = parent_pipe.recv()
1533
command = request[0]
1535
if command == 'init':
1537
address = request[2]
1539
for c in self.clients:
1540
if c.fingerprint == fpr:
1544
logger.warning(u"Client not found for fingerprint: %s, ad"
1545
u"dress: %s", fpr, address)
1548
mandos_dbus_service.ClientNotFound(fpr, address[0])
1549
parent_pipe.send(False)
1552
gobject.io_add_watch(parent_pipe.fileno(),
1553
gobject.IO_IN | gobject.IO_HUP,
1554
functools.partial(self.handle_ipc,
1555
parent_pipe = parent_pipe,
1556
client_object = client))
1557
parent_pipe.send(True)
1558
# remove the old hook in favor of the new above hook on same fileno
1560
if command == 'funcall':
1561
funcname = request[1]
1565
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1567
if command == 'getattr':
1568
attrname = request[1]
1569
if callable(client_object.__getattribute__(attrname)):
1570
parent_pipe.send(('function',))
1572
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1574
if command == 'setattr':
1575
attrname = request[1]
1577
setattr(client_object, attrname, value)
956
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
959
# Turn the pipe file descriptor into a Python file object
960
if source not in file_objects:
961
file_objects[source] = os.fdopen(source, u"r", 1)
963
# Read a line from the file object
964
cmdline = file_objects[source].readline()
965
if not cmdline: # Empty line means end of file
967
file_objects[source].close()
968
del file_objects[source]
970
# Stop calling this function
973
logger.debug(u"IPC command: %r", cmdline)
975
# Parse and act on command
976
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
978
if cmd == u"NOTFOUND":
979
logger.warning(u"Client not found for fingerprint: %s",
983
mandos_dbus_service.ClientNotFound(args)
984
elif cmd == u"INVALID":
985
for client in self.clients:
986
if client.name == args:
987
logger.warning(u"Client %s is invalid", args)
993
logger.error(u"Unknown client %s is invalid", args)
994
elif cmd == u"SENDING":
995
for client in self.clients:
996
if client.name == args:
997
logger.info(u"Sending secret to %s", client.name)
1001
client.ReceivedSecret()
1004
logger.error(u"Sending secret to unknown client %s",
1007
logger.error(u"Unknown IPC command: %r", cmdline)
1009
# Keep calling this function
1838
1281
(gnutls.library.functions
1839
1282
.gnutls_global_set_log_function(debug_gnutls))
1841
# Redirect stdin so all checkers get /dev/null
1842
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1843
os.dup2(null, sys.stdin.fileno())
1847
# No console logging
1848
logger.removeHandler(console)
1285
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1286
service = AvahiService(name = server_settings[u"servicename"],
1287
servicetype = u"_mandos._tcp",
1288
protocol = protocol)
1289
if server_settings["interface"]:
1290
service.interface = (if_nametoindex
1291
(str(server_settings[u"interface"])))
1851
1293
global main_loop
1852
1296
# From the Avahi example code
1853
1297
DBusGMainLoop(set_as_default=True )
1854
1298
main_loop = gobject.MainLoop()
1855
1299
bus = dbus.SystemBus()
1300
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1301
avahi.DBUS_PATH_SERVER),
1302
avahi.DBUS_INTERFACE_SERVER)
1856
1303
# End of Avahi example code
1859
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1860
bus, do_not_queue=True)
1861
except dbus.exceptions.NameExistsException, e:
1862
logger.error(unicode(e) + u", disabling D-Bus")
1864
server_settings[u"use_dbus"] = False
1865
tcp_server.use_dbus = False
1866
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1867
service = AvahiService(name = server_settings[u"servicename"],
1868
servicetype = u"_mandos._tcp",
1869
protocol = protocol, bus = bus)
1870
if server_settings["interface"]:
1871
service.interface = (if_nametoindex
1872
(str(server_settings[u"interface"])))
1875
# Close all input and output, do double fork, etc.
1878
global multiprocessing_manager
1879
multiprocessing_manager = multiprocessing.Manager()
1305
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1881
1307
client_class = Client
1883
client_class = functools.partial(ClientDBus, bus = bus)
1884
def client_config_items(config, section):
1885
special_settings = {
1886
"approved_by_default":
1887
lambda: config.getboolean(section,
1888
"approved_by_default"),
1890
for name, value in config.items(section):
1892
yield (name, special_settings[name]())
1896
tcp_server.clients.update(set(
1309
client_class = ClientDBus
1897
1311
client_class(name = section,
1898
config= dict(client_config_items(
1899
client_config, section)))
1312
config= dict(client_config.items(section)))
1900
1313
for section in client_config.sections()))
1901
if not tcp_server.clients:
1902
1315
logger.warning(u"No clients defined")
1318
# Redirect stdin so all checkers get /dev/null
1319
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1320
os.dup2(null, sys.stdin.fileno())
1324
# No console logging
1325
logger.removeHandler(console)
1326
# Close all input and output, do double fork, etc.
1330
with closing(pidfile):
1332
pidfile.write(str(pid) + "\n")
1335
logger.error(u"Could not write to file %r with PID %d",
1338
# "pidfile" was never created
1343
"Cleanup function; run on exit"
1345
# From the Avahi example code
1346
if not group is None:
1349
# End of Avahi example code
1352
client = clients.pop()
1353
client.disable_hook = None
1356
atexit.register(cleanup)
1908
pidfile.write(str(pid) + "\n")
1911
logger.error(u"Could not write to file %r with PID %d",
1914
# "pidfile" was never created
1918
1359
signal.signal(signal.SIGINT, signal.SIG_IGN)
1920
1360
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1921
1361
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())