241
228
"""A representation of a client host served by this server.
244
_approved: bool(); 'None' if not yet approved/disapproved
245
approval_delay: datetime.timedelta(); Time to wait for approval
246
approval_duration: datetime.timedelta(); Duration of one approval
231
name: string; from the config file, used in log messages and
233
fingerprint: string (40 or 32 hexadecimal digits); used to
234
uniquely identify the client
235
secret: bytestring; sent verbatim (over TLS) to client
236
host: string; available for use by the checker command
237
created: datetime.datetime(); (UTC) object creation
238
last_enabled: datetime.datetime(); (UTC)
240
last_checked_ok: datetime.datetime(); (UTC) or None
241
timeout: datetime.timedelta(); How long from last_checked_ok
242
until this client is invalid
243
interval: datetime.timedelta(); How often to start a new checker
244
disable_hook: If set, called by disable() as disable_hook(self)
247
245
checker: subprocess.Popen(); a running checker process used
248
246
to see if the client lives.
249
247
'None' if no process is running.
250
checker_callback_tag: a gobject event source tag, or None
251
checker_command: string; External command which is run to check
252
if client lives. %() expansions are done at
248
checker_initiator_tag: a gobject event source tag, or None
249
disable_initiator_tag: - '' -
250
checker_callback_tag: - '' -
251
checker_command: string; External command which is run to check if
252
client lives. %() expansions are done at
253
253
runtime with vars(self) as dict, so that for
254
254
instance %(name)s can be used in the command.
255
checker_initiator_tag: a gobject event source tag, or None
256
created: datetime.datetime(); (UTC) object creation
257
255
current_checker_command: string; current running checker_command
258
disable_hook: If set, called by disable() as disable_hook(self)
259
disable_initiator_tag: a gobject event source tag, or None
261
fingerprint: string (40 or 32 hexadecimal digits); used to
262
uniquely identify the client
263
host: string; available for use by the checker command
264
interval: datetime.timedelta(); How often to start a new checker
265
last_approval_request: datetime.datetime(); (UTC) or None
266
last_checked_ok: datetime.datetime(); (UTC) or None
267
last_enabled: datetime.datetime(); (UTC)
268
name: string; from the config file, used in log messages and
270
secret: bytestring; sent verbatim (over TLS) to client
271
timeout: datetime.timedelta(); How long from last_checked_ok
272
until this client is disabled
273
runtime_expansions: Allowed attributes for runtime expansion.
276
runtime_expansions = ("approval_delay", "approval_duration",
277
"created", "enabled", "fingerprint",
278
"host", "interval", "last_checked_ok",
279
"last_enabled", "name", "timeout")
282
def _timedelta_to_milliseconds(td):
283
"Convert a datetime.timedelta() to milliseconds"
284
return ((td.days * 24 * 60 * 60 * 1000)
285
+ (td.seconds * 1000)
286
+ (td.microseconds // 1000))
259
def _datetime_to_milliseconds(dt):
260
"Convert a datetime.datetime() to milliseconds"
261
return ((dt.days * 24 * 60 * 60 * 1000)
262
+ (dt.seconds * 1000)
263
+ (dt.microseconds // 1000))
288
265
def timeout_milliseconds(self):
289
266
"Return the 'timeout' attribute in milliseconds"
290
return self._timedelta_to_milliseconds(self.timeout)
267
return self._datetime_to_milliseconds(self.timeout)
292
269
def interval_milliseconds(self):
293
270
"Return the 'interval' attribute in milliseconds"
294
return self._timedelta_to_milliseconds(self.interval)
296
def approval_delay_milliseconds(self):
297
return self._timedelta_to_milliseconds(self.approval_delay)
271
return self._datetime_to_milliseconds(self.interval)
299
273
def __init__(self, name = None, disable_hook=None, config=None):
300
274
"""Note: the 'checker' key in 'config' sets the
304
278
if config is None:
306
logger.debug("Creating client %r", self.name)
280
logger.debug(u"Creating client %r", self.name)
307
281
# Uppercase and remove spaces from fingerprint for later
308
282
# comparison purposes with return value from the fingerprint()
310
self.fingerprint = (config["fingerprint"].upper()
312
logger.debug(" Fingerprint: %s", self.fingerprint)
313
if "secret" in config:
314
self.secret = config["secret"].decode("base64")
315
elif "secfile" in config:
316
with open(os.path.expanduser(os.path.expandvars
317
(config["secfile"])),
284
self.fingerprint = (config[u"fingerprint"].upper()
286
logger.debug(u" Fingerprint: %s", self.fingerprint)
287
if u"secret" in config:
288
self.secret = config[u"secret"].decode(u"base64")
289
elif u"secfile" in config:
290
with closing(open(os.path.expanduser
292
(config[u"secfile"])))) as secfile:
319
293
self.secret = secfile.read()
321
raise TypeError("No secret or secfile for client %s"
295
raise TypeError(u"No secret or secfile for client %s"
323
self.host = config.get("host", "")
297
self.host = config.get(u"host", u"")
324
298
self.created = datetime.datetime.utcnow()
325
299
self.enabled = False
326
self.last_approval_request = None
327
300
self.last_enabled = None
328
301
self.last_checked_ok = None
329
self.timeout = string_to_delta(config["timeout"])
330
self.interval = string_to_delta(config["interval"])
302
self.timeout = string_to_delta(config[u"timeout"])
303
self.interval = string_to_delta(config[u"interval"])
331
304
self.disable_hook = disable_hook
332
305
self.checker = None
333
306
self.checker_initiator_tag = None
334
307
self.disable_initiator_tag = None
335
308
self.checker_callback_tag = None
336
self.checker_command = config["checker"]
309
self.checker_command = config[u"checker"]
337
310
self.current_checker_command = None
338
311
self.last_connect = None
339
self._approved = None
340
self.approved_by_default = config.get("approved_by_default",
342
self.approvals_pending = 0
343
self.approval_delay = string_to_delta(
344
config["approval_delay"])
345
self.approval_duration = string_to_delta(
346
config["approval_duration"])
347
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
349
def send_changedstate(self):
350
self.changedstate.acquire()
351
self.changedstate.notify_all()
352
self.changedstate.release()
354
313
def enable(self):
355
314
"""Start this client's checker and timeout hooks"""
356
if getattr(self, "enabled", False):
359
self.send_changedstate()
360
315
self.last_enabled = datetime.datetime.utcnow()
361
316
# Schedule a new checker to be started an 'interval' from now,
362
317
# and every interval from then on.
363
318
self.checker_initiator_tag = (gobject.timeout_add
364
319
(self.interval_milliseconds(),
365
320
self.start_checker))
321
# Also start a new checker *right now*.
366
323
# Schedule a disable() when 'timeout' has passed
367
324
self.disable_initiator_tag = (gobject.timeout_add
368
325
(self.timeout_milliseconds(),
370
327
self.enabled = True
371
# Also start a new checker *right now*.
374
def disable(self, quiet=True):
375
330
"""Disable this client."""
376
331
if not getattr(self, "enabled", False):
379
self.send_changedstate()
381
logger.info("Disabling client %s", self.name)
382
if getattr(self, "disable_initiator_tag", False):
333
logger.info(u"Disabling client %s", self.name)
334
if getattr(self, u"disable_initiator_tag", False):
383
335
gobject.source_remove(self.disable_initiator_tag)
384
336
self.disable_initiator_tag = None
385
if getattr(self, "checker_initiator_tag", False):
337
if getattr(self, u"checker_initiator_tag", False):
386
338
gobject.source_remove(self.checker_initiator_tag)
387
339
self.checker_initiator_tag = None
388
340
self.stop_checker()
508
450
if self.checker_callback_tag:
509
451
gobject.source_remove(self.checker_callback_tag)
510
452
self.checker_callback_tag = None
511
if getattr(self, "checker", None) is None:
453
if getattr(self, u"checker", None) is None:
513
logger.debug("Stopping checker for %(name)s", vars(self))
455
logger.debug(u"Stopping checker for %(name)s", vars(self))
515
457
os.kill(self.checker.pid, signal.SIGTERM)
517
459
#if self.checker.poll() is None:
518
460
# os.kill(self.checker.pid, signal.SIGKILL)
519
461
except OSError, error:
520
462
if error.errno != errno.ESRCH: # No such process
522
464
self.checker = None
524
def dbus_service_property(dbus_interface, signature="v",
525
access="readwrite", byte_arrays=False):
526
"""Decorators for marking methods of a DBusObjectWithProperties to
527
become properties on the D-Bus.
529
The decorated method will be called with no arguments by "Get"
530
and with one argument by "Set".
532
The parameters, where they are supported, are the same as
533
dbus.service.method, except there is only "signature", since the
534
type from Get() and the type sent to Set() is the same.
536
# Encoding deeply encoded byte arrays is not supported yet by the
537
# "Set" method, so we fail early here:
538
if byte_arrays and signature != "ay":
539
raise ValueError("Byte arrays not supported for non-'ay'"
540
" signature %r" % signature)
542
func._dbus_is_property = True
543
func._dbus_interface = dbus_interface
544
func._dbus_signature = signature
545
func._dbus_access = access
546
func._dbus_name = func.__name__
547
if func._dbus_name.endswith("_dbus_property"):
548
func._dbus_name = func._dbus_name[:-14]
549
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
554
class DBusPropertyException(dbus.exceptions.DBusException):
555
"""A base class for D-Bus property-related exceptions
557
def __unicode__(self):
558
return unicode(str(self))
561
class DBusPropertyAccessException(DBusPropertyException):
562
"""A property's access permissions disallows an operation.
567
class DBusPropertyNotFound(DBusPropertyException):
568
"""An attempt was made to access a non-existing property.
573
class DBusObjectWithProperties(dbus.service.Object):
574
"""A D-Bus object with properties.
576
Classes inheriting from this can use the dbus_service_property
577
decorator to expose methods as D-Bus properties. It exposes the
578
standard Get(), Set(), and GetAll() methods on the D-Bus.
582
def _is_dbus_property(obj):
583
return getattr(obj, "_dbus_is_property", False)
585
def _get_all_dbus_properties(self):
586
"""Returns a generator of (name, attribute) pairs
588
return ((prop._dbus_name, prop)
590
inspect.getmembers(self, self._is_dbus_property))
592
def _get_dbus_property(self, interface_name, property_name):
593
"""Returns a bound method if one exists which is a D-Bus
594
property with the specified name and interface.
596
for name in (property_name,
597
property_name + "_dbus_property"):
598
prop = getattr(self, name, None)
600
or not self._is_dbus_property(prop)
601
or prop._dbus_name != property_name
602
or (interface_name and prop._dbus_interface
603
and interface_name != prop._dbus_interface)):
607
raise DBusPropertyNotFound(self.dbus_object_path + ":"
608
+ interface_name + "."
611
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
613
def Get(self, interface_name, property_name):
614
"""Standard D-Bus property Get() method, see D-Bus standard.
616
prop = self._get_dbus_property(interface_name, property_name)
617
if prop._dbus_access == "write":
618
raise DBusPropertyAccessException(property_name)
620
if not hasattr(value, "variant_level"):
622
return type(value)(value, variant_level=value.variant_level+1)
624
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
625
def Set(self, interface_name, property_name, value):
626
"""Standard D-Bus property Set() method, see D-Bus standard.
628
prop = self._get_dbus_property(interface_name, property_name)
629
if prop._dbus_access == "read":
630
raise DBusPropertyAccessException(property_name)
631
if prop._dbus_get_args_options["byte_arrays"]:
632
# The byte_arrays option is not supported yet on
633
# signatures other than "ay".
634
if prop._dbus_signature != "ay":
636
value = dbus.ByteArray(''.join(unichr(byte)
640
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
641
out_signature="a{sv}")
642
def GetAll(self, interface_name):
643
"""Standard D-Bus property GetAll() method, see D-Bus
646
Note: Will not include properties with access="write".
649
for name, prop in self._get_all_dbus_properties():
651
and interface_name != prop._dbus_interface):
652
# Interface non-empty but did not match
654
# Ignore write-only properties
655
if prop._dbus_access == "write":
658
if not hasattr(value, "variant_level"):
661
all[name] = type(value)(value, variant_level=
662
value.variant_level+1)
663
return dbus.Dictionary(all, signature="sv")
665
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
667
path_keyword='object_path',
668
connection_keyword='connection')
669
def Introspect(self, object_path, connection):
670
"""Standard D-Bus method, overloaded to insert property tags.
672
xmlstring = dbus.service.Object.Introspect(self, object_path,
675
document = xml.dom.minidom.parseString(xmlstring)
676
def make_tag(document, name, prop):
677
e = document.createElement("property")
678
e.setAttribute("name", name)
679
e.setAttribute("type", prop._dbus_signature)
680
e.setAttribute("access", prop._dbus_access)
682
for if_tag in document.getElementsByTagName("interface"):
683
for tag in (make_tag(document, name, prop)
685
in self._get_all_dbus_properties()
686
if prop._dbus_interface
687
== if_tag.getAttribute("name")):
688
if_tag.appendChild(tag)
689
# Add the names to the return values for the
690
# "org.freedesktop.DBus.Properties" methods
691
if (if_tag.getAttribute("name")
692
== "org.freedesktop.DBus.Properties"):
693
for cn in if_tag.getElementsByTagName("method"):
694
if cn.getAttribute("name") == "Get":
695
for arg in cn.getElementsByTagName("arg"):
696
if (arg.getAttribute("direction")
698
arg.setAttribute("name", "value")
699
elif cn.getAttribute("name") == "GetAll":
700
for arg in cn.getElementsByTagName("arg"):
701
if (arg.getAttribute("direction")
703
arg.setAttribute("name", "props")
704
xmlstring = document.toxml("utf-8")
706
except (AttributeError, xml.dom.DOMException,
707
xml.parsers.expat.ExpatError), error:
708
logger.error("Failed to override Introspection method",
713
class ClientDBus(Client, DBusObjectWithProperties):
466
def still_valid(self):
467
"""Has the timeout not yet passed for this client?"""
468
if not getattr(self, u"enabled", False):
470
now = datetime.datetime.utcnow()
471
if self.last_checked_ok is None:
472
return now < (self.created + self.timeout)
474
return now < (self.last_checked_ok + self.timeout)
477
class ClientDBus(Client, dbus.service.Object):
714
478
"""A Client class using D-Bus
717
481
dbus_object_path: dbus.ObjectPath
718
482
bus: dbus.SystemBus()
721
runtime_expansions = (Client.runtime_expansions
722
+ ("dbus_object_path",))
724
484
# dbus.service.Object doesn't use super(), so we can't either.
726
486
def __init__(self, bus = None, *args, **kwargs):
727
self._approvals_pending = 0
729
488
Client.__init__(self, *args, **kwargs)
730
489
# Only now, when this client is initialized, can it show up on
732
client_object_name = unicode(self.name).translate(
735
491
self.dbus_object_path = (dbus.ObjectPath
736
("/clients/" + client_object_name))
737
DBusObjectWithProperties.__init__(self, self.bus,
738
self.dbus_object_path)
740
def _get_approvals_pending(self):
741
return self._approvals_pending
742
def _set_approvals_pending(self, value):
743
old_value = self._approvals_pending
744
self._approvals_pending = value
746
if (hasattr(self, "dbus_object_path")
747
and bval is not bool(old_value)):
748
dbus_bool = dbus.Boolean(bval, variant_level=1)
749
self.PropertyChanged(dbus.String("ApprovalPending"),
752
approvals_pending = property(_get_approvals_pending,
753
_set_approvals_pending)
754
del _get_approvals_pending, _set_approvals_pending
493
+ self.name.replace(u".", u"_")))
494
dbus.service.Object.__init__(self, self.bus,
495
self.dbus_object_path)
757
498
def _datetime_to_dbus(dt, variant_level=0):
843
575
# Emit D-Bus signal
844
576
self.CheckerStarted(self.current_checker_command)
845
577
self.PropertyChanged(
846
dbus.String("CheckerRunning"),
578
dbus.String(u"checker_running"),
847
579
dbus.Boolean(True, variant_level=1))
850
582
def stop_checker(self, *args, **kwargs):
851
old_checker = getattr(self, "checker", None)
583
old_checker = getattr(self, u"checker", None)
852
584
r = Client.stop_checker(self, *args, **kwargs)
853
585
if (old_checker is not None
854
and getattr(self, "checker", None) is None):
855
self.PropertyChanged(dbus.String("CheckerRunning"),
586
and getattr(self, u"checker", None) is None):
587
self.PropertyChanged(dbus.String(u"checker_running"),
856
588
dbus.Boolean(False, variant_level=1))
859
def _reset_approved(self):
860
self._approved = None
863
def approve(self, value=True):
864
self.send_changedstate()
865
self._approved = value
866
gobject.timeout_add(self._timedelta_to_milliseconds
867
(self.approval_duration),
868
self._reset_approved)
871
## D-Bus methods, signals & properties
872
_interface = "se.bsnet.fukt.Mandos.Client"
591
## D-Bus methods & signals
592
_interface = u"se.bsnet.fukt.Mandos.Client"
595
@dbus.service.method(_interface)
597
return self.checked_ok()
876
599
# CheckerCompleted - signal
877
@dbus.service.signal(_interface, signature="nxs")
600
@dbus.service.signal(_interface, signature=u"nxs")
878
601
def CheckerCompleted(self, exitcode, waitstatus, command):
882
605
# CheckerStarted - signal
883
@dbus.service.signal(_interface, signature="s")
606
@dbus.service.signal(_interface, signature=u"s")
884
607
def CheckerStarted(self, command):
611
# GetAllProperties - method
612
@dbus.service.method(_interface, out_signature=u"a{sv}")
613
def GetAllProperties(self):
615
return dbus.Dictionary({
616
dbus.String(u"name"):
617
dbus.String(self.name, variant_level=1),
618
dbus.String(u"fingerprint"):
619
dbus.String(self.fingerprint, variant_level=1),
620
dbus.String(u"host"):
621
dbus.String(self.host, variant_level=1),
622
dbus.String(u"created"):
623
self._datetime_to_dbus(self.created,
625
dbus.String(u"last_enabled"):
626
(self._datetime_to_dbus(self.last_enabled,
628
if self.last_enabled is not None
629
else dbus.Boolean(False, variant_level=1)),
630
dbus.String(u"enabled"):
631
dbus.Boolean(self.enabled, variant_level=1),
632
dbus.String(u"last_checked_ok"):
633
(self._datetime_to_dbus(self.last_checked_ok,
635
if self.last_checked_ok is not None
636
else dbus.Boolean (False, variant_level=1)),
637
dbus.String(u"timeout"):
638
dbus.UInt64(self.timeout_milliseconds(),
640
dbus.String(u"interval"):
641
dbus.UInt64(self.interval_milliseconds(),
643
dbus.String(u"checker"):
644
dbus.String(self.checker_command,
646
dbus.String(u"checker_running"):
647
dbus.Boolean(self.checker is not None,
649
dbus.String(u"object_path"):
650
dbus.ObjectPath(self.dbus_object_path,
654
# IsStillValid - method
655
@dbus.service.method(_interface, out_signature=u"b")
656
def IsStillValid(self):
657
return self.still_valid()
888
659
# PropertyChanged - signal
889
@dbus.service.signal(_interface, signature="sv")
660
@dbus.service.signal(_interface, signature=u"sv")
890
661
def PropertyChanged(self, property, value):
665
# ReceivedSecret - signal
895
666
@dbus.service.signal(_interface)
898
Is sent after a successful transfer of secret from the Mandos
899
server to mandos-client
667
def ReceivedSecret(self):
903
671
# Rejected - signal
904
@dbus.service.signal(_interface, signature="s")
905
def Rejected(self, reason):
672
@dbus.service.signal(_interface)
909
# NeedApproval - signal
910
@dbus.service.signal(_interface, signature="tb")
911
def NeedApproval(self, timeout, default):
913
return self.need_approval()
918
@dbus.service.method(_interface, in_signature="b")
919
def Approve(self, value):
923
@dbus.service.method(_interface)
925
return self.checked_ok()
677
# SetChecker - method
678
@dbus.service.method(_interface, in_signature=u"s")
679
def SetChecker(self, checker):
680
"D-Bus setter method"
681
self.checker_command = checker
683
self.PropertyChanged(dbus.String(u"checker"),
684
dbus.String(self.checker_command,
688
@dbus.service.method(_interface, in_signature=u"s")
689
def SetHost(self, host):
690
"D-Bus setter method"
693
self.PropertyChanged(dbus.String(u"host"),
694
dbus.String(self.host, variant_level=1))
696
# SetInterval - method
697
@dbus.service.method(_interface, in_signature=u"t")
698
def SetInterval(self, milliseconds):
699
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
701
self.PropertyChanged(dbus.String(u"interval"),
702
(dbus.UInt64(self.interval_milliseconds(),
706
@dbus.service.method(_interface, in_signature=u"ay",
708
def SetSecret(self, secret):
709
"D-Bus setter method"
710
self.secret = str(secret)
712
# SetTimeout - method
713
@dbus.service.method(_interface, in_signature=u"t")
714
def SetTimeout(self, milliseconds):
715
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
717
self.PropertyChanged(dbus.String(u"timeout"),
718
(dbus.UInt64(self.timeout_milliseconds(),
927
721
# Enable - method
928
722
@dbus.service.method(_interface)
947
741
def StopChecker(self):
948
742
self.stop_checker()
952
# ApprovalPending - property
953
@dbus_service_property(_interface, signature="b", access="read")
954
def ApprovalPending_dbus_property(self):
955
return dbus.Boolean(bool(self.approvals_pending))
957
# ApprovedByDefault - property
958
@dbus_service_property(_interface, signature="b",
960
def ApprovedByDefault_dbus_property(self, value=None):
961
if value is None: # get
962
return dbus.Boolean(self.approved_by_default)
963
self.approved_by_default = bool(value)
965
self.PropertyChanged(dbus.String("ApprovedByDefault"),
966
dbus.Boolean(value, variant_level=1))
968
# ApprovalDelay - property
969
@dbus_service_property(_interface, signature="t",
971
def ApprovalDelay_dbus_property(self, value=None):
972
if value is None: # get
973
return dbus.UInt64(self.approval_delay_milliseconds())
974
self.approval_delay = datetime.timedelta(0, 0, 0, value)
976
self.PropertyChanged(dbus.String("ApprovalDelay"),
977
dbus.UInt64(value, variant_level=1))
979
# ApprovalDuration - property
980
@dbus_service_property(_interface, signature="t",
982
def ApprovalDuration_dbus_property(self, value=None):
983
if value is None: # get
984
return dbus.UInt64(self._timedelta_to_milliseconds(
985
self.approval_duration))
986
self.approval_duration = datetime.timedelta(0, 0, 0, value)
988
self.PropertyChanged(dbus.String("ApprovalDuration"),
989
dbus.UInt64(value, variant_level=1))
992
@dbus_service_property(_interface, signature="s", access="read")
993
def Name_dbus_property(self):
994
return dbus.String(self.name)
996
# Fingerprint - property
997
@dbus_service_property(_interface, signature="s", access="read")
998
def Fingerprint_dbus_property(self):
999
return dbus.String(self.fingerprint)
1002
@dbus_service_property(_interface, signature="s",
1004
def Host_dbus_property(self, value=None):
1005
if value is None: # get
1006
return dbus.String(self.host)
1009
self.PropertyChanged(dbus.String("Host"),
1010
dbus.String(value, variant_level=1))
1012
# Created - property
1013
@dbus_service_property(_interface, signature="s", access="read")
1014
def Created_dbus_property(self):
1015
return dbus.String(self._datetime_to_dbus(self.created))
1017
# LastEnabled - property
1018
@dbus_service_property(_interface, signature="s", access="read")
1019
def LastEnabled_dbus_property(self):
1020
if self.last_enabled is None:
1021
return dbus.String("")
1022
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1024
# Enabled - property
1025
@dbus_service_property(_interface, signature="b",
1027
def Enabled_dbus_property(self, value=None):
1028
if value is None: # get
1029
return dbus.Boolean(self.enabled)
1035
# LastCheckedOK - property
1036
@dbus_service_property(_interface, signature="s",
1038
def LastCheckedOK_dbus_property(self, value=None):
1039
if value is not None:
1042
if self.last_checked_ok is None:
1043
return dbus.String("")
1044
return dbus.String(self._datetime_to_dbus(self
1047
# LastApprovalRequest - property
1048
@dbus_service_property(_interface, signature="s", access="read")
1049
def LastApprovalRequest_dbus_property(self):
1050
if self.last_approval_request is None:
1051
return dbus.String("")
1052
return dbus.String(self.
1053
_datetime_to_dbus(self
1054
.last_approval_request))
1056
# Timeout - property
1057
@dbus_service_property(_interface, signature="t",
1059
def Timeout_dbus_property(self, value=None):
1060
if value is None: # get
1061
return dbus.UInt64(self.timeout_milliseconds())
1062
self.timeout = datetime.timedelta(0, 0, 0, value)
1064
self.PropertyChanged(dbus.String("Timeout"),
1065
dbus.UInt64(value, variant_level=1))
1066
if getattr(self, "disable_initiator_tag", None) is None:
1068
# Reschedule timeout
1069
gobject.source_remove(self.disable_initiator_tag)
1070
self.disable_initiator_tag = None
1071
time_to_die = (self.
1072
_timedelta_to_milliseconds((self
1077
if time_to_die <= 0:
1078
# The timeout has passed
1081
self.disable_initiator_tag = (gobject.timeout_add
1082
(time_to_die, self.disable))
1084
# Interval - property
1085
@dbus_service_property(_interface, signature="t",
1087
def Interval_dbus_property(self, value=None):
1088
if value is None: # get
1089
return dbus.UInt64(self.interval_milliseconds())
1090
self.interval = datetime.timedelta(0, 0, 0, value)
1092
self.PropertyChanged(dbus.String("Interval"),
1093
dbus.UInt64(value, variant_level=1))
1094
if getattr(self, "checker_initiator_tag", None) is None:
1096
# Reschedule checker run
1097
gobject.source_remove(self.checker_initiator_tag)
1098
self.checker_initiator_tag = (gobject.timeout_add
1099
(value, self.start_checker))
1100
self.start_checker() # Start one now, too
1102
# Checker - property
1103
@dbus_service_property(_interface, signature="s",
1105
def Checker_dbus_property(self, value=None):
1106
if value is None: # get
1107
return dbus.String(self.checker_command)
1108
self.checker_command = value
1110
self.PropertyChanged(dbus.String("Checker"),
1111
dbus.String(self.checker_command,
1114
# CheckerRunning - property
1115
@dbus_service_property(_interface, signature="b",
1117
def CheckerRunning_dbus_property(self, value=None):
1118
if value is None: # get
1119
return dbus.Boolean(self.checker is not None)
1121
self.start_checker()
1125
# ObjectPath - property
1126
@dbus_service_property(_interface, signature="o", access="read")
1127
def ObjectPath_dbus_property(self):
1128
return self.dbus_object_path # is already a dbus.ObjectPath
1131
@dbus_service_property(_interface, signature="ay",
1132
access="write", byte_arrays=True)
1133
def Secret_dbus_property(self, value):
1134
self.secret = str(value)
1139
class ProxyClient(object):
1140
def __init__(self, child_pipe, fpr, address):
1141
self._pipe = child_pipe
1142
self._pipe.send(('init', fpr, address))
1143
if not self._pipe.recv():
1146
def __getattribute__(self, name):
1147
if(name == '_pipe'):
1148
return super(ProxyClient, self).__getattribute__(name)
1149
self._pipe.send(('getattr', name))
1150
data = self._pipe.recv()
1151
if data[0] == 'data':
1153
if data[0] == 'function':
1154
def func(*args, **kwargs):
1155
self._pipe.send(('funcall', name, args, kwargs))
1156
return self._pipe.recv()[1]
1159
def __setattr__(self, name, value):
1160
if(name == '_pipe'):
1161
return super(ProxyClient, self).__setattr__(name, value)
1162
self._pipe.send(('setattr', name, value))
1165
747
class ClientHandler(socketserver.BaseRequestHandler, object):
1166
748
"""A class to handle client connections.
1169
751
Note: This will run in its own forked process."""
1171
753
def handle(self):
1172
with contextlib.closing(self.server.child_pipe) as child_pipe:
1173
logger.info("TCP connection from: %s",
1174
unicode(self.client_address))
1175
logger.debug("Pipe FD: %d",
1176
self.server.child_pipe.fileno())
754
logger.info(u"TCP connection from: %s",
755
unicode(self.client_address))
756
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
757
# Open IPC pipe to parent process
758
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
1178
759
session = (gnutls.connection
1179
760
.ClientSession(self.request,
1180
761
gnutls.connection
1181
762
.X509Credentials()))
764
line = self.request.makefile().readline()
765
logger.debug(u"Protocol version: %r", line)
767
if int(line.strip().split()[0]) > 1:
769
except (ValueError, IndexError, RuntimeError), error:
770
logger.error(u"Unknown protocol version: %s", error)
1183
773
# Note: gnutls.connection.X509Credentials is really a
1184
774
# generic GnuTLS certificate credentials object so long as
1185
775
# no X.509 keys are added to it. Therefore, we can use it
1186
776
# here despite using OpenPGP certificates.
1188
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1189
# "+AES-256-CBC", "+SHA1",
1190
# "+COMP-NULL", "+CTYPE-OPENPGP",
778
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
779
# u"+AES-256-CBC", u"+SHA1",
780
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1192
782
# Use a fallback default, since this MUST be set.
1193
783
priority = self.server.gnutls_priority
1194
784
if priority is None:
1196
786
(gnutls.library.functions
1197
787
.gnutls_priority_set_direct(session._c_object,
1198
788
priority, None))
1200
# Start communication using the Mandos protocol
1201
# Get protocol number
1202
line = self.request.makefile().readline()
1203
logger.debug("Protocol version: %r", line)
1205
if int(line.strip().split()[0]) > 1:
1207
except (ValueError, IndexError, RuntimeError), error:
1208
logger.error("Unknown protocol version: %s", error)
1211
# Start GnuTLS connection
1213
791
session.handshake()
1214
792
except gnutls.errors.GNUTLSError, error:
1215
logger.warning("Handshake failed: %s", error)
793
logger.warning(u"Handshake failed: %s", error)
1216
794
# Do not run session.bye() here: the session is not
1217
795
# established. Just abandon the request.
1219
logger.debug("Handshake succeeded")
1221
approval_required = False
797
logger.debug(u"Handshake succeeded")
1224
fpr = self.fingerprint(self.peer_certificate
1226
except (TypeError, gnutls.errors.GNUTLSError), error:
1227
logger.warning("Bad certificate: %s", error)
1229
logger.debug("Fingerprint: %s", fpr)
1232
client = ProxyClient(child_pipe, fpr,
1233
self.client_address)
1237
if client.approval_delay:
1238
delay = client.approval_delay
1239
client.approvals_pending += 1
1240
approval_required = True
1243
if not client.enabled:
1244
logger.warning("Client %s is disabled",
1246
if self.server.use_dbus:
1248
client.Rejected("Disabled")
1251
if client._approved or not client.approval_delay:
1252
#We are approved or approval is disabled
1254
elif client._approved is None:
1255
logger.info("Client %s needs approval",
1257
if self.server.use_dbus:
1259
client.NeedApproval(
1260
client.approval_delay_milliseconds(),
1261
client.approved_by_default)
1263
logger.warning("Client %s was not approved",
1265
if self.server.use_dbus:
1267
client.Rejected("Denied")
1270
#wait until timeout or approved
1271
#x = float(client._timedelta_to_milliseconds(delay))
1272
time = datetime.datetime.now()
1273
client.changedstate.acquire()
1274
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1275
client.changedstate.release()
1276
time2 = datetime.datetime.now()
1277
if (time2 - time) >= delay:
1278
if not client.approved_by_default:
1279
logger.warning("Client %s timed out while"
1280
" waiting for approval",
1282
if self.server.use_dbus:
1284
client.Rejected("Approval timed out")
1289
delay -= time2 - time
1292
while sent_size < len(client.secret):
1294
sent = session.send(client.secret[sent_size:])
1295
except (gnutls.errors.GNUTLSError), error:
1296
logger.warning("gnutls send failed")
1298
logger.debug("Sent: %d, remaining: %d",
1299
sent, len(client.secret)
1300
- (sent_size + sent))
1303
logger.info("Sending secret to %s", client.name)
1304
# bump the timeout as if seen
1306
if self.server.use_dbus:
799
fpr = self.fingerprint(self.peer_certificate(session))
800
except (TypeError, gnutls.errors.GNUTLSError), error:
801
logger.warning(u"Bad certificate: %s", error)
804
logger.debug(u"Fingerprint: %s", fpr)
1311
if approval_required:
1312
client.approvals_pending -= 1
1315
except (gnutls.errors.GNUTLSError), error:
1316
logger.warning("GnuTLS bye failed")
806
for c in self.server.clients:
807
if c.fingerprint == fpr:
811
ipc.write(u"NOTFOUND %s\n" % fpr)
814
# Have to check if client.still_valid(), since it is
815
# possible that the client timed out while establishing
816
# the GnuTLS session.
817
if not client.still_valid():
818
ipc.write(u"INVALID %s\n" % client.name)
821
ipc.write(u"SENDING %s\n" % client.name)
823
while sent_size < len(client.secret):
824
sent = session.send(client.secret[sent_size:])
825
logger.debug(u"Sent: %d, remaining: %d",
826
sent, len(client.secret)
827
- (sent_size + sent))
1319
832
def peer_certificate(session):
1375
888
# Convert the buffer to a Python bytestring
1376
889
fpr = ctypes.string_at(buf, buf_len.value)
1377
890
# Convert the bytestring to hexadecimal notation
1378
hex_fpr = ''.join("%02X" % ord(char) for char in fpr)
891
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1382
class MultiprocessingMixIn(object):
1383
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1384
def sub_process_main(self, request, address):
1386
self.finish_request(request, address)
1388
self.handle_error(request, address)
1389
self.close_request(request)
1391
def process_request(self, request, address):
1392
"""Start a new process to process the request."""
1393
multiprocessing.Process(target = self.sub_process_main,
1394
args = (request, address)).start()
1396
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1397
""" adds a pipe to the MixIn """
895
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
896
"""Like socketserver.ForkingMixIn, but also pass a pipe.
898
Assumes a gobject.MainLoop event loop.
1398
900
def process_request(self, request, client_address):
1399
901
"""Overrides and wraps the original process_request().
1401
This function creates a new pipe in self.pipe
903
This function creates a new pipe in self.pipe
1403
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1405
super(MultiprocessingMixInWithPipe,
905
self.pipe = os.pipe()
906
super(ForkingMixInWithPipe,
1406
907
self).process_request(request, client_address)
1407
self.child_pipe.close()
1408
self.add_pipe(parent_pipe)
1410
def add_pipe(self, parent_pipe):
908
os.close(self.pipe[1]) # close write end
909
# Call "handle_ipc" for both data and EOF events
910
gobject.io_add_watch(self.pipe[0],
911
gobject.IO_IN | gobject.IO_HUP,
913
def handle_ipc(source, condition):
1411
914
"""Dummy function; override as necessary"""
1412
raise NotImplementedError
1414
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
919
class IPv6_TCPServer(ForkingMixInWithPipe,
1415
920
socketserver.TCPServer, object):
1416
921
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1472
986
# if_nametoindex
1473
987
# (self.interface))
1474
988
return socketserver.TCPServer.server_bind(self)
1477
class MandosServer(IPv6_TCPServer):
1481
clients: set of Client objects
1482
gnutls_priority GnuTLS priority string
1483
use_dbus: Boolean; to emit D-Bus signals or not
1485
Assumes a gobject.MainLoop event loop.
1487
def __init__(self, server_address, RequestHandlerClass,
1488
interface=None, use_ipv6=True, clients=None,
1489
gnutls_priority=None, use_dbus=True):
1490
self.enabled = False
1491
self.clients = clients
1492
if self.clients is None:
1493
self.clients = set()
1494
self.use_dbus = use_dbus
1495
self.gnutls_priority = gnutls_priority
1496
IPv6_TCPServer.__init__(self, server_address,
1497
RequestHandlerClass,
1498
interface = interface,
1499
use_ipv6 = use_ipv6)
1500
989
def server_activate(self):
1501
990
if self.enabled:
1502
991
return socketserver.TCPServer.server_activate(self)
1503
992
def enable(self):
1504
993
self.enabled = True
1505
def add_pipe(self, parent_pipe):
1506
# Call "handle_ipc" for both data and EOF events
1507
gobject.io_add_watch(parent_pipe.fileno(),
1508
gobject.IO_IN | gobject.IO_HUP,
1509
functools.partial(self.handle_ipc,
1510
parent_pipe = parent_pipe))
1512
def handle_ipc(self, source, condition, parent_pipe=None,
1513
client_object=None):
994
def handle_ipc(self, source, condition, file_objects={}):
1514
995
condition_names = {
1515
gobject.IO_IN: "IN", # There is data to read.
1516
gobject.IO_OUT: "OUT", # Data can be written (without
996
gobject.IO_IN: u"IN", # There is data to read.
997
gobject.IO_OUT: u"OUT", # Data can be written (without
1518
gobject.IO_PRI: "PRI", # There is urgent data to read.
1519
gobject.IO_ERR: "ERR", # Error condition.
1520
gobject.IO_HUP: "HUP" # Hung up (the connection has been
999
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1000
gobject.IO_ERR: u"ERR", # Error condition.
1001
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1521
1002
# broken, usually for pipes and
1525
1006
for cond, name in
1526
1007
condition_names.iteritems()
1527
1008
if cond & condition)
1528
# error or the other end of multiprocessing.Pipe has closed
1529
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1532
# Read a request from the child
1533
request = parent_pipe.recv()
1534
command = request[0]
1536
if command == 'init':
1538
address = request[2]
1540
for c in self.clients:
1541
if c.fingerprint == fpr:
1545
logger.warning("Client not found for fingerprint: %s, ad"
1546
"dress: %s", fpr, address)
1549
mandos_dbus_service.ClientNotFound(fpr, address[0])
1550
parent_pipe.send(False)
1553
gobject.io_add_watch(parent_pipe.fileno(),
1554
gobject.IO_IN | gobject.IO_HUP,
1555
functools.partial(self.handle_ipc,
1556
parent_pipe = parent_pipe,
1557
client_object = client))
1558
parent_pipe.send(True)
1559
# remove the old hook in favor of the new above hook on same fileno
1561
if command == 'funcall':
1562
funcname = request[1]
1566
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1568
if command == 'getattr':
1569
attrname = request[1]
1570
if callable(client_object.__getattribute__(attrname)):
1571
parent_pipe.send(('function',))
1573
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1575
if command == 'setattr':
1576
attrname = request[1]
1578
setattr(client_object, attrname, value)
1009
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1012
# Turn the pipe file descriptor into a Python file object
1013
if source not in file_objects:
1014
file_objects[source] = os.fdopen(source, u"r", 1)
1016
# Read a line from the file object
1017
cmdline = file_objects[source].readline()
1018
if not cmdline: # Empty line means end of file
1019
# close the IPC pipe
1020
file_objects[source].close()
1021
del file_objects[source]
1023
# Stop calling this function
1026
logger.debug(u"IPC command: %r", cmdline)
1028
# Parse and act on command
1029
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1031
if cmd == u"NOTFOUND":
1032
logger.warning(u"Client not found for fingerprint: %s",
1036
mandos_dbus_service.ClientNotFound(args)
1037
elif cmd == u"INVALID":
1038
for client in self.clients:
1039
if client.name == args:
1040
logger.warning(u"Client %s is invalid", args)
1046
logger.error(u"Unknown client %s is invalid", args)
1047
elif cmd == u"SENDING":
1048
for client in self.clients:
1049
if client.name == args:
1050
logger.info(u"Sending secret to %s", client.name)
1054
client.ReceivedSecret()
1057
logger.error(u"Sending secret to unknown client %s",
1060
logger.error(u"Unknown IPC command: %r", cmdline)
1062
# Keep calling this function
1583
1066
def string_to_delta(interval):
1584
1067
"""Parse a string and return a datetime.timedelta
1586
>>> string_to_delta('7d')
1069
>>> string_to_delta(u'7d')
1587
1070
datetime.timedelta(7)
1588
>>> string_to_delta('60s')
1071
>>> string_to_delta(u'60s')
1589
1072
datetime.timedelta(0, 60)
1590
>>> string_to_delta('60m')
1073
>>> string_to_delta(u'60m')
1591
1074
datetime.timedelta(0, 3600)
1592
>>> string_to_delta('24h')
1075
>>> string_to_delta(u'24h')
1593
1076
datetime.timedelta(1)
1594
>>> string_to_delta('1w')
1077
>>> string_to_delta(u'1w')
1595
1078
datetime.timedelta(7)
1596
>>> string_to_delta('5m 30s')
1079
>>> string_to_delta(u'5m 30s')
1597
1080
datetime.timedelta(0, 330)
1599
1082
timevalue = datetime.timedelta(0)
1673
##################################################################
1155
######################################################################
1674
1156
# Parsing of options, both command line and config file
1676
1158
parser = optparse.OptionParser(version = "%%prog %s" % version)
1677
parser.add_option("-i", "--interface", type="string",
1678
metavar="IF", help="Bind to interface IF")
1679
parser.add_option("-a", "--address", type="string",
1680
help="Address to listen for requests on")
1681
parser.add_option("-p", "--port", type="int",
1682
help="Port number to receive requests on")
1683
parser.add_option("--check", action="store_true",
1684
help="Run self-test")
1685
parser.add_option("--debug", action="store_true",
1686
help="Debug mode; run in foreground and log to"
1688
parser.add_option("--debuglevel", type="string", metavar="LEVEL",
1689
help="Debug level for stdout output")
1690
parser.add_option("--priority", type="string", help="GnuTLS"
1691
" priority string (see GnuTLS documentation)")
1692
parser.add_option("--servicename", type="string",
1693
metavar="NAME", help="Zeroconf service name")
1694
parser.add_option("--configdir", type="string",
1695
default="/etc/mandos", metavar="DIR",
1696
help="Directory to search for configuration"
1698
parser.add_option("--no-dbus", action="store_false",
1699
dest="use_dbus", help="Do not provide D-Bus"
1700
" system bus interface")
1701
parser.add_option("--no-ipv6", action="store_false",
1702
dest="use_ipv6", help="Do not use IPv6")
1159
parser.add_option("-i", u"--interface", type=u"string",
1160
metavar="IF", help=u"Bind to interface IF")
1161
parser.add_option("-a", u"--address", type=u"string",
1162
help=u"Address to listen for requests on")
1163
parser.add_option("-p", u"--port", type=u"int",
1164
help=u"Port number to receive requests on")
1165
parser.add_option("--check", action=u"store_true",
1166
help=u"Run self-test")
1167
parser.add_option("--debug", action=u"store_true",
1168
help=u"Debug mode; run in foreground and log to"
1170
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1171
u" priority string (see GnuTLS documentation)")
1172
parser.add_option("--servicename", type=u"string",
1173
metavar=u"NAME", help=u"Zeroconf service name")
1174
parser.add_option("--configdir", type=u"string",
1175
default=u"/etc/mandos", metavar=u"DIR",
1176
help=u"Directory to search for configuration"
1178
parser.add_option("--no-dbus", action=u"store_false",
1179
dest=u"use_dbus", help=u"Do not provide D-Bus"
1180
u" system bus interface")
1181
parser.add_option("--no-ipv6", action=u"store_false",
1182
dest=u"use_ipv6", help=u"Do not use IPv6")
1703
1183
options = parser.parse_args()[0]
1705
1185
if options.check:
1710
1190
# Default values for config file for server-global settings
1711
server_defaults = { "interface": "",
1716
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1717
"servicename": "Mandos",
1191
server_defaults = { u"interface": u"",
1196
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1197
u"servicename": u"Mandos",
1198
u"use_dbus": u"True",
1199
u"use_ipv6": u"True",
1723
1202
# Parse config file for server-global settings
1724
1203
server_config = configparser.SafeConfigParser(server_defaults)
1725
1204
del server_defaults
1726
1205
server_config.read(os.path.join(options.configdir,
1728
1207
# Convert the SafeConfigParser object to a dict
1729
1208
server_settings = server_config.defaults()
1730
1209
# Use the appropriate methods on the non-string config options
1731
for option in ("debug", "use_dbus", "use_ipv6"):
1732
server_settings[option] = server_config.getboolean("DEFAULT",
1210
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1211
server_settings[option] = server_config.getboolean(u"DEFAULT",
1734
1213
if server_settings["port"]:
1735
server_settings["port"] = server_config.getint("DEFAULT",
1214
server_settings["port"] = server_config.getint(u"DEFAULT",
1737
1216
del server_config
1739
1218
# Override the settings from the config file with command line
1740
1219
# options, if set.
1741
for option in ("interface", "address", "port", "debug",
1742
"priority", "servicename", "configdir",
1743
"use_dbus", "use_ipv6", "debuglevel"):
1220
for option in (u"interface", u"address", u"port", u"debug",
1221
u"priority", u"servicename", u"configdir",
1222
u"use_dbus", u"use_ipv6"):
1744
1223
value = getattr(options, option)
1745
1224
if value is not None:
1746
1225
server_settings[option] = value
1754
1233
##################################################################
1756
1235
# For convenience
1757
debug = server_settings["debug"]
1758
debuglevel = server_settings["debuglevel"]
1759
use_dbus = server_settings["use_dbus"]
1760
use_ipv6 = server_settings["use_ipv6"]
1762
if server_settings["servicename"] != "Mandos":
1236
debug = server_settings[u"debug"]
1237
use_dbus = server_settings[u"use_dbus"]
1238
use_ipv6 = server_settings[u"use_ipv6"]
1241
syslogger.setLevel(logging.WARNING)
1242
console.setLevel(logging.WARNING)
1244
if server_settings[u"servicename"] != u"Mandos":
1763
1245
syslogger.setFormatter(logging.Formatter
1764
('Mandos (%s) [%%(process)d]:'
1765
' %%(levelname)s: %%(message)s'
1766
% server_settings["servicename"]))
1246
(u'Mandos (%s) [%%(process)d]:'
1247
u' %%(levelname)s: %%(message)s'
1248
% server_settings[u"servicename"]))
1768
1250
# Parse config file with clients
1769
client_defaults = { "timeout": "1h",
1771
"checker": "fping -q -- %%(host)s",
1773
"approval_delay": "0s",
1774
"approval_duration": "1s",
1251
client_defaults = { u"timeout": u"1h",
1253
u"checker": u"fping -q -- %%(host)s",
1776
1256
client_config = configparser.SafeConfigParser(client_defaults)
1777
client_config.read(os.path.join(server_settings["configdir"],
1257
client_config.read(os.path.join(server_settings[u"configdir"],
1780
1260
global mandos_dbus_service
1781
1261
mandos_dbus_service = None
1783
tcp_server = MandosServer((server_settings["address"],
1784
server_settings["port"]),
1786
interface=(server_settings["interface"]
1790
server_settings["priority"],
1793
pidfilename = "/var/run/mandos.pid"
1795
pidfile = open(pidfilename, "w")
1797
logger.error("Could not open file %r", pidfilename)
1264
tcp_server = IPv6_TCPServer((server_settings[u"address"],
1265
server_settings[u"port"]),
1268
server_settings[u"interface"],
1272
server_settings[u"priority"],
1274
pidfilename = u"/var/run/mandos.pid"
1276
pidfile = open(pidfilename, u"w")
1278
logger.error(u"Could not open file %r", pidfilename)
1800
uid = pwd.getpwnam("_mandos").pw_uid
1801
gid = pwd.getpwnam("_mandos").pw_gid
1281
uid = pwd.getpwnam(u"_mandos").pw_uid
1282
gid = pwd.getpwnam(u"_mandos").pw_gid
1802
1283
except KeyError:
1804
uid = pwd.getpwnam("mandos").pw_uid
1805
gid = pwd.getpwnam("mandos").pw_gid
1285
uid = pwd.getpwnam(u"mandos").pw_uid
1286
gid = pwd.getpwnam(u"mandos").pw_gid
1806
1287
except KeyError:
1808
uid = pwd.getpwnam("nobody").pw_uid
1809
gid = pwd.getpwnam("nobody").pw_gid
1289
uid = pwd.getpwnam(u"nobody").pw_uid
1290
gid = pwd.getpwnam(u"nobody").pw_gid
1810
1291
except KeyError:
1860
1318
bus = dbus.SystemBus()
1861
1319
# End of Avahi example code
1864
bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1865
bus, do_not_queue=True)
1866
except dbus.exceptions.NameExistsException, e:
1867
logger.error(unicode(e) + ", disabling D-Bus")
1869
server_settings["use_dbus"] = False
1870
tcp_server.use_dbus = False
1321
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1871
1322
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1872
service = AvahiService(name = server_settings["servicename"],
1873
servicetype = "_mandos._tcp",
1323
service = AvahiService(name = server_settings[u"servicename"],
1324
servicetype = u"_mandos._tcp",
1874
1325
protocol = protocol, bus = bus)
1875
1326
if server_settings["interface"]:
1876
1327
service.interface = (if_nametoindex
1877
(str(server_settings["interface"])))
1879
global multiprocessing_manager
1880
multiprocessing_manager = multiprocessing.Manager()
1328
(str(server_settings[u"interface"])))
1882
1330
client_class = Client
1884
1332
client_class = functools.partial(ClientDBus, bus = bus)
1885
def client_config_items(config, section):
1886
special_settings = {
1887
"approved_by_default":
1888
lambda: config.getboolean(section,
1889
"approved_by_default"),
1891
for name, value in config.items(section):
1893
yield (name, special_settings[name]())
1897
tcp_server.clients.update(set(
1898
1334
client_class(name = section,
1899
config= dict(client_config_items(
1900
client_config, section)))
1335
config= dict(client_config.items(section)))
1901
1336
for section in client_config.sections()))
1902
if not tcp_server.clients:
1903
logger.warning("No clients defined")
1338
logger.warning(u"No clients defined")
1341
# Redirect stdin so all checkers get /dev/null
1342
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1343
os.dup2(null, sys.stdin.fileno())
1347
# No console logging
1348
logger.removeHandler(console)
1349
# Close all input and output, do double fork, etc.
1353
with closing(pidfile):
1355
pidfile.write(str(pid) + "\n")
1358
logger.error(u"Could not write to file %r with PID %d",
1361
# "pidfile" was never created
1366
"Cleanup function; run on exit"
1370
client = clients.pop()
1371
client.disable_hook = None
1374
atexit.register(cleanup)
1909
pidfile.write(str(pid) + "\n".encode("utf-8"))
1912
logger.error("Could not write to file %r with PID %d",
1915
# "pidfile" was never created
1919
1377
signal.signal(signal.SIGINT, signal.SIG_IGN)
1921
1378
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1922
1379
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1925
1382
class MandosDBusService(dbus.service.Object):
1926
1383
"""A D-Bus proxy object"""
1927
1384
def __init__(self):
1928
dbus.service.Object.__init__(self, bus, "/")
1929
_interface = "se.bsnet.fukt.Mandos"
1931
@dbus.service.signal(_interface, signature="o")
1932
def ClientAdded(self, objpath):
1936
@dbus.service.signal(_interface, signature="ss")
1937
def ClientNotFound(self, fingerprint, address):
1941
@dbus.service.signal(_interface, signature="os")
1385
dbus.service.Object.__init__(self, bus, u"/")
1386
_interface = u"se.bsnet.fukt.Mandos"
1388
@dbus.service.signal(_interface, signature=u"oa{sv}")
1389
def ClientAdded(self, objpath, properties):
1393
@dbus.service.signal(_interface, signature=u"s")
1394
def ClientNotFound(self, fingerprint):
1398
@dbus.service.signal(_interface, signature=u"os")
1942
1399
def ClientRemoved(self, objpath, name):
1946
@dbus.service.method(_interface, out_signature="ao")
1403
@dbus.service.method(_interface, out_signature=u"ao")
1947
1404
def GetAllClients(self):
1949
return dbus.Array(c.dbus_object_path
1950
for c in tcp_server.clients)
1406
return dbus.Array(c.dbus_object_path for c in clients)
1952
1408
@dbus.service.method(_interface,
1953
out_signature="a{oa{sv}}")
1409
out_signature=u"a{oa{sv}}")
1954
1410
def GetAllClientsWithProperties(self):
1956
1412
return dbus.Dictionary(
1957
((c.dbus_object_path, c.GetAll(""))
1958
for c in tcp_server.clients),
1413
((c.dbus_object_path, c.GetAllProperties())
1415
signature=u"oa{sv}")
1961
@dbus.service.method(_interface, in_signature="o")
1417
@dbus.service.method(_interface, in_signature=u"o")
1962
1418
def RemoveClient(self, object_path):
1964
for c in tcp_server.clients:
1965
1421
if c.dbus_object_path == object_path:
1966
tcp_server.clients.remove(c)
1967
1423
c.remove_from_connection()
1968
1424
# Don't signal anything except ClientRemoved
1969
c.disable(quiet=True)
1425
c.disable(signal=False)
1970
1426
# Emit D-Bus signal
1971
1427
self.ClientRemoved(object_path, c.name)
1973
raise KeyError(object_path)
1977
1433
mandos_dbus_service = MandosDBusService()
1980
"Cleanup function; run on exit"
1983
while tcp_server.clients:
1984
client = tcp_server.clients.pop()
1986
client.remove_from_connection()
1987
client.disable_hook = None
1988
# Don't signal anything except ClientRemoved
1989
client.disable(quiet=True)
1992
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1995
atexit.register(cleanup)
1997
for client in tcp_server.clients:
1435
for client in clients:
1999
1437
# Emit D-Bus signal
2000
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1438
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1439
client.GetAllProperties())
2001
1440
client.enable()
2003
1442
tcp_server.enable()