474
433
if error.errno != errno.ESRCH: # No such process
476
435
self.checker = None
437
self.PropertyChanged(dbus.String(u"checker_running"),
438
dbus.Boolean(False, variant_level=1))
478
440
def still_valid(self):
479
441
"""Has the timeout not yet passed for this client?"""
480
if not getattr(self, u"enabled", False):
442
if not getattr(self, "enabled", False):
482
444
now = datetime.datetime.utcnow()
483
445
if self.last_checked_ok is None:
484
446
return now < (self.created + self.timeout)
486
448
return now < (self.last_checked_ok + self.timeout)
489
def dbus_service_property(dbus_interface, signature=u"v",
490
access=u"readwrite", byte_arrays=False):
491
"""Decorators for marking methods of a DBusObjectWithProperties to
492
become properties on the D-Bus.
494
The decorated method will be called with no arguments by "Get"
495
and with one argument by "Set".
497
The parameters, where they are supported, are the same as
498
dbus.service.method, except there is only "signature", since the
499
type from Get() and the type sent to Set() is the same.
502
func._dbus_is_property = True
503
func._dbus_interface = dbus_interface
504
func._dbus_signature = signature
505
func._dbus_access = access
506
func._dbus_name = func.__name__
507
if func._dbus_name.endswith(u"_dbus_property"):
508
func._dbus_name = func._dbus_name[:-14]
509
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
514
class DBusPropertyException(dbus.exceptions.DBusException):
515
"""A base class for D-Bus property-related exceptions
517
def __unicode__(self):
518
return unicode(str(self))
521
class DBusPropertyAccessException(DBusPropertyException):
522
"""A property's access permissions disallows an operation.
527
class DBusPropertyNotFound(DBusPropertyException):
528
"""An attempt was made to access a non-existing property.
533
class DBusObjectWithProperties(dbus.service.Object):
534
"""A D-Bus object with properties.
536
Classes inheriting from this can use the dbus_service_property
537
decorator to expose methods as D-Bus properties. It exposes the
538
standard Get(), Set(), and GetAll() methods on the D-Bus.
542
def _is_dbus_property(obj):
543
return getattr(obj, u"_dbus_is_property", False)
545
def _get_all_dbus_properties(self):
546
"""Returns a generator of (name, attribute) pairs
548
return ((prop._dbus_name, prop)
550
inspect.getmembers(self, self._is_dbus_property))
552
def _get_dbus_property(self, interface_name, property_name):
553
"""Returns a bound method if one exists which is a D-Bus
554
property with the specified name and interface.
556
for name in (property_name,
557
property_name + u"_dbus_property"):
558
prop = getattr(self, name, None)
560
or not self._is_dbus_property(prop)
561
or prop._dbus_name != property_name
562
or (interface_name and prop._dbus_interface
563
and interface_name != prop._dbus_interface)):
567
raise DBusPropertyNotFound(self.dbus_object_path + u":"
568
+ interface_name + u"."
571
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
573
def Get(self, interface_name, property_name):
574
"""Standard D-Bus property Get() method, see D-Bus standard.
576
prop = self._get_dbus_property(interface_name, property_name)
577
if prop._dbus_access == u"write":
578
raise DBusPropertyAccessException(property_name)
580
if not hasattr(value, u"variant_level"):
582
return type(value)(value, variant_level=value.variant_level+1)
584
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
585
def Set(self, interface_name, property_name, value):
586
"""Standard D-Bus property Set() method, see D-Bus standard.
588
prop = self._get_dbus_property(interface_name, property_name)
589
if prop._dbus_access == u"read":
590
raise DBusPropertyAccessException(property_name)
591
if prop._dbus_get_args_options[u"byte_arrays"]:
592
value = dbus.ByteArray(''.join(unichr(byte)
596
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
597
out_signature=u"a{sv}")
598
def GetAll(self, interface_name):
599
"""Standard D-Bus property GetAll() method, see D-Bus
602
Note: Will not include properties with access="write".
605
for name, prop in self._get_all_dbus_properties():
607
and interface_name != prop._dbus_interface):
608
# Interface non-empty but did not match
610
# Ignore write-only properties
611
if prop._dbus_access == u"write":
614
if not hasattr(value, u"variant_level"):
617
all[name] = type(value)(value, variant_level=
618
value.variant_level+1)
619
return dbus.Dictionary(all, signature=u"sv")
621
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
623
path_keyword='object_path',
624
connection_keyword='connection')
625
def Introspect(self, object_path, connection):
626
"""Standard D-Bus method, overloaded to insert property tags.
628
xmlstring = dbus.service.Object.Introspect(self, object_path,
630
document = xml.dom.minidom.parseString(xmlstring)
632
def make_tag(document, name, prop):
633
e = document.createElement(u"property")
634
e.setAttribute(u"name", name)
635
e.setAttribute(u"type", prop._dbus_signature)
636
e.setAttribute(u"access", prop._dbus_access)
638
for if_tag in document.getElementsByTagName(u"interface"):
639
for tag in (make_tag(document, name, prop)
641
in self._get_all_dbus_properties()
642
if prop._dbus_interface
643
== if_tag.getAttribute(u"name")):
644
if_tag.appendChild(tag)
645
# Add the names to the return values for the
646
# "org.freedesktop.DBus.Properties" methods
647
if (if_tag.getAttribute(u"name")
648
== u"org.freedesktop.DBus.Properties"):
649
for cn in if_tag.getElementsByTagName(u"method"):
650
if cn.getAttribute(u"name") == u"Get":
651
for arg in cn.getElementsByTagName(u"arg"):
652
if (arg.getAttribute(u"direction")
654
arg.setAttribute(u"name", u"value")
655
elif cn.getAttribute(u"name") == u"GetAll":
656
for arg in cn.getElementsByTagName(u"arg"):
657
if (arg.getAttribute(u"direction")
659
arg.setAttribute(u"name", u"props")
660
xmlstring = document.toxml(u"utf-8")
665
class ClientDBus(Client, DBusObjectWithProperties):
666
"""A Client class using D-Bus
669
dbus_object_path: dbus.ObjectPath
670
bus: dbus.SystemBus()
672
# dbus.service.Object doesn't use super(), so we can't either.
674
def __init__(self, bus = None, *args, **kwargs):
676
Client.__init__(self, *args, **kwargs)
677
# Only now, when this client is initialized, can it show up on
679
self.dbus_object_path = (dbus.ObjectPath
681
+ self.name.replace(u".", u"_")))
682
DBusObjectWithProperties.__init__(self, self.bus,
683
self.dbus_object_path)
686
def _datetime_to_dbus(dt, variant_level=0):
687
"""Convert a UTC datetime.datetime() to a D-Bus type."""
688
return dbus.String(dt.isoformat(),
689
variant_level=variant_level)
692
oldstate = getattr(self, u"enabled", False)
693
r = Client.enable(self)
694
if oldstate != self.enabled:
696
self.PropertyChanged(dbus.String(u"enabled"),
697
dbus.Boolean(True, variant_level=1))
698
self.PropertyChanged(
699
dbus.String(u"last_enabled"),
700
self._datetime_to_dbus(self.last_enabled,
704
def disable(self, signal = True):
705
oldstate = getattr(self, u"enabled", False)
706
r = Client.disable(self)
707
if signal and oldstate != self.enabled:
709
self.PropertyChanged(dbus.String(u"enabled"),
710
dbus.Boolean(False, variant_level=1))
713
def __del__(self, *args, **kwargs):
715
self.remove_from_connection()
718
if hasattr(DBusObjectWithProperties, u"__del__"):
719
DBusObjectWithProperties.__del__(self, *args, **kwargs)
720
Client.__del__(self, *args, **kwargs)
722
def checker_callback(self, pid, condition, command,
724
self.checker_callback_tag = None
727
self.PropertyChanged(dbus.String(u"checker_running"),
728
dbus.Boolean(False, variant_level=1))
729
if os.WIFEXITED(condition):
730
exitstatus = os.WEXITSTATUS(condition)
732
self.CheckerCompleted(dbus.Int16(exitstatus),
733
dbus.Int64(condition),
734
dbus.String(command))
737
self.CheckerCompleted(dbus.Int16(-1),
738
dbus.Int64(condition),
739
dbus.String(command))
741
return Client.checker_callback(self, pid, condition, command,
744
def checked_ok(self, *args, **kwargs):
745
r = Client.checked_ok(self, *args, **kwargs)
747
self.PropertyChanged(
748
dbus.String(u"last_checked_ok"),
749
(self._datetime_to_dbus(self.last_checked_ok,
753
def start_checker(self, *args, **kwargs):
754
old_checker = self.checker
755
if self.checker is not None:
756
old_checker_pid = self.checker.pid
758
old_checker_pid = None
759
r = Client.start_checker(self, *args, **kwargs)
760
# Only if new checker process was started
761
if (self.checker is not None
762
and old_checker_pid != self.checker.pid):
764
self.CheckerStarted(self.current_checker_command)
765
self.PropertyChanged(
766
dbus.String(u"checker_running"),
767
dbus.Boolean(True, variant_level=1))
770
def stop_checker(self, *args, **kwargs):
771
old_checker = getattr(self, u"checker", None)
772
r = Client.stop_checker(self, *args, **kwargs)
773
if (old_checker is not None
774
and getattr(self, u"checker", None) is None):
775
self.PropertyChanged(dbus.String(u"checker_running"),
776
dbus.Boolean(False, variant_level=1))
779
450
## D-Bus methods & signals
780
_interface = u"se.bsnet.fukt.Mandos.Client"
451
_interface = u"org.mandos_system.Mandos.Client"
783
@dbus.service.method(_interface)
785
return self.checked_ok()
453
# BumpTimeout - method
454
BumpTimeout = dbus.service.method(_interface)(bump_timeout)
455
BumpTimeout.__name__ = "BumpTimeout"
787
457
# CheckerCompleted - signal
788
@dbus.service.signal(_interface, signature=u"nxs")
789
def CheckerCompleted(self, exitcode, waitstatus, command):
458
@dbus.service.signal(_interface, signature="bqs")
459
def CheckerCompleted(self, success, condition, command):
793
463
# CheckerStarted - signal
794
@dbus.service.signal(_interface, signature=u"s")
464
@dbus.service.signal(_interface, signature="s")
795
465
def CheckerStarted(self, command):
469
# GetAllProperties - method
470
@dbus.service.method(_interface, out_signature="a{sv}")
471
def GetAllProperties(self):
473
return dbus.Dictionary({
475
dbus.String(self.name, variant_level=1),
476
dbus.String("fingerprint"):
477
dbus.String(self.fingerprint, variant_level=1),
479
dbus.String(self.host, variant_level=1),
480
dbus.String("created"):
481
_datetime_to_dbus(self.created, variant_level=1),
482
dbus.String("last_enabled"):
483
(_datetime_to_dbus(self.last_enabled,
485
if self.last_enabled is not None
486
else dbus.Boolean(False, variant_level=1)),
487
dbus.String("enabled"):
488
dbus.Boolean(self.enabled, variant_level=1),
489
dbus.String("last_checked_ok"):
490
(_datetime_to_dbus(self.last_checked_ok,
492
if self.last_checked_ok is not None
493
else dbus.Boolean (False, variant_level=1)),
494
dbus.String("timeout"):
495
dbus.UInt64(self.timeout_milliseconds(),
497
dbus.String("interval"):
498
dbus.UInt64(self.interval_milliseconds(),
500
dbus.String("checker"):
501
dbus.String(self.checker_command,
503
dbus.String("checker_running"):
504
dbus.Boolean(self.checker is not None,
508
# IsStillValid - method
509
IsStillValid = (dbus.service.method(_interface, out_signature="b")
511
IsStillValid.__name__ = "IsStillValid"
799
513
# PropertyChanged - signal
800
@dbus.service.signal(_interface, signature=u"sv")
514
@dbus.service.signal(_interface, signature="sv")
801
515
def PropertyChanged(self, property, value):
805
# ReceivedSecret - signal
806
@dbus.service.signal(_interface)
807
def ReceivedSecret(self):
812
@dbus.service.signal(_interface)
519
# SetChecker - method
520
@dbus.service.method(_interface, in_signature="s")
521
def SetChecker(self, checker):
522
"D-Bus setter method"
523
self.checker_command = checker
525
self.PropertyChanged(dbus.String(u"checker"),
526
dbus.String(self.checker_command,
530
@dbus.service.method(_interface, in_signature="s")
531
def SetHost(self, host):
532
"D-Bus setter method"
535
self.PropertyChanged(dbus.String(u"host"),
536
dbus.String(self.host, variant_level=1))
538
# SetInterval - method
539
@dbus.service.method(_interface, in_signature="t")
540
def SetInterval(self, milliseconds):
541
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
543
self.PropertyChanged(dbus.String(u"interval"),
544
(dbus.UInt64(self.interval_milliseconds(),
548
@dbus.service.method(_interface, in_signature="ay",
550
def SetSecret(self, secret):
551
"D-Bus setter method"
552
self.secret = str(secret)
554
# SetTimeout - method
555
@dbus.service.method(_interface, in_signature="t")
556
def SetTimeout(self, milliseconds):
557
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
559
self.PropertyChanged(dbus.String(u"timeout"),
560
(dbus.UInt64(self.timeout_milliseconds(),
817
563
# Enable - method
818
@dbus.service.method(_interface)
564
Enable = dbus.service.method(_interface)(enable)
565
Enable.__name__ = "Enable"
823
567
# StartChecker - method
824
568
@dbus.service.method(_interface)
835
579
# StopChecker - method
836
@dbus.service.method(_interface)
837
def StopChecker(self):
841
@dbus_service_property(_interface, signature=u"s", access=u"read")
842
def name_dbus_property(self):
843
return dbus.String(self.name)
845
# fingerprint - property
846
@dbus_service_property(_interface, signature=u"s", access=u"read")
847
def fingerprint_dbus_property(self):
848
return dbus.String(self.fingerprint)
851
@dbus_service_property(_interface, signature=u"s",
853
def host_dbus_property(self, value=None):
854
if value is None: # get
855
return dbus.String(self.host)
858
self.PropertyChanged(dbus.String(u"host"),
859
dbus.String(value, variant_level=1))
862
@dbus_service_property(_interface, signature=u"s", access=u"read")
863
def created_dbus_property(self):
864
return dbus.String(self._datetime_to_dbus(self.created))
866
# last_enabled - property
867
@dbus_service_property(_interface, signature=u"s", access=u"read")
868
def last_enabled_dbus_property(self):
869
if self.last_enabled is None:
870
return dbus.String(u"")
871
return dbus.String(self._datetime_to_dbus(self.last_enabled))
874
@dbus_service_property(_interface, signature=u"b",
876
def enabled_dbus_property(self, value=None):
877
if value is None: # get
878
return dbus.Boolean(self.enabled)
884
# last_checked_ok - property
885
@dbus_service_property(_interface, signature=u"s",
887
def last_checked_ok_dbus_property(self, value=None):
888
if value is not None:
891
if self.last_checked_ok is None:
892
return dbus.String(u"")
893
return dbus.String(self._datetime_to_dbus(self
897
@dbus_service_property(_interface, signature=u"t",
899
def timeout_dbus_property(self, value=None):
900
if value is None: # get
901
return dbus.UInt64(self.timeout_milliseconds())
902
self.timeout = datetime.timedelta(0, 0, 0, value)
904
self.PropertyChanged(dbus.String(u"timeout"),
905
dbus.UInt64(value, variant_level=1))
906
if getattr(self, u"disable_initiator_tag", None) is None:
909
gobject.source_remove(self.disable_initiator_tag)
910
self.disable_initiator_tag = None
912
_timedelta_to_milliseconds((self
918
# The timeout has passed
921
self.disable_initiator_tag = (gobject.timeout_add
922
(time_to_die, self.disable))
924
# interval - property
925
@dbus_service_property(_interface, signature=u"t",
927
def interval_dbus_property(self, value=None):
928
if value is None: # get
929
return dbus.UInt64(self.interval_milliseconds())
930
self.interval = datetime.timedelta(0, 0, 0, value)
932
self.PropertyChanged(dbus.String(u"interval"),
933
dbus.UInt64(value, variant_level=1))
934
if getattr(self, u"checker_initiator_tag", None) is None:
936
# Reschedule checker run
937
gobject.source_remove(self.checker_initiator_tag)
938
self.checker_initiator_tag = (gobject.timeout_add
939
(value, self.start_checker))
940
self.start_checker() # Start one now, too
943
@dbus_service_property(_interface, signature=u"s",
945
def checker_dbus_property(self, value=None):
946
if value is None: # get
947
return dbus.String(self.checker_command)
948
self.checker_command = value
950
self.PropertyChanged(dbus.String(u"checker"),
951
dbus.String(self.checker_command,
954
# checker_running - property
955
@dbus_service_property(_interface, signature=u"b",
957
def checker_running_dbus_property(self, value=None):
958
if value is None: # get
959
return dbus.Boolean(self.checker is not None)
965
# object_path - property
966
@dbus_service_property(_interface, signature=u"o", access=u"read")
967
def object_path_dbus_property(self):
968
return self.dbus_object_path # is already a dbus.ObjectPath
971
@dbus_service_property(_interface, signature=u"ay",
972
access=u"write", byte_arrays=True)
973
def secret_dbus_property(self, value):
974
self.secret = str(value)
580
StopChecker = dbus.service.method(_interface)(stop_checker)
581
StopChecker.__name__ = "StopChecker"
979
class ClientHandler(socketserver.BaseRequestHandler, object):
980
"""A class to handle client connections.
982
Instantiated once for each connection to handle it.
586
def peer_certificate(session):
587
"Return the peer's OpenPGP certificate as a bytestring"
588
# If not an OpenPGP certificate...
589
if (gnutls.library.functions
590
.gnutls_certificate_type_get(session._c_object)
591
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
592
# ...do the normal thing
593
return session.peer_certificate
594
list_size = ctypes.c_uint()
595
cert_list = (gnutls.library.functions
596
.gnutls_certificate_get_peers
597
(session._c_object, ctypes.byref(list_size)))
598
if list_size.value == 0:
601
return ctypes.string_at(cert.data, cert.size)
604
def fingerprint(openpgp):
605
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
606
# New GnuTLS "datum" with the OpenPGP public key
607
datum = (gnutls.library.types
608
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
611
ctypes.c_uint(len(openpgp))))
612
# New empty GnuTLS certificate
613
crt = gnutls.library.types.gnutls_openpgp_crt_t()
614
(gnutls.library.functions
615
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
616
# Import the OpenPGP public key into the certificate
617
(gnutls.library.functions
618
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
619
gnutls.library.constants
620
.GNUTLS_OPENPGP_FMT_RAW))
621
# Verify the self signature in the key
622
crtverify = ctypes.c_uint()
623
(gnutls.library.functions
624
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
625
if crtverify.value != 0:
626
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
627
raise gnutls.errors.CertificateSecurityError("Verify failed")
628
# New buffer for the fingerprint
629
buf = ctypes.create_string_buffer(20)
630
buf_len = ctypes.c_size_t()
631
# Get the fingerprint from the certificate into the buffer
632
(gnutls.library.functions
633
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
634
ctypes.byref(buf_len)))
635
# Deinit the certificate
636
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
637
# Convert the buffer to a Python bytestring
638
fpr = ctypes.string_at(buf, buf_len.value)
639
# Convert the bytestring to hexadecimal notation
640
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
644
class TCP_handler(SocketServer.BaseRequestHandler, object):
645
"""A TCP request handler class.
646
Instantiated by IPv6_TCPServer for each request to handle it.
983
647
Note: This will run in its own forked process."""
985
649
def handle(self):
986
650
logger.info(u"TCP connection from: %s",
987
651
unicode(self.client_address))
988
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
989
# Open IPC pipe to parent process
990
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
991
session = (gnutls.connection
992
.ClientSession(self.request,
996
line = self.request.makefile().readline()
997
logger.debug(u"Protocol version: %r", line)
999
if int(line.strip().split()[0]) > 1:
1001
except (ValueError, IndexError, RuntimeError), error:
1002
logger.error(u"Unknown protocol version: %s", error)
1005
# Note: gnutls.connection.X509Credentials is really a
1006
# generic GnuTLS certificate credentials object so long as
1007
# no X.509 keys are added to it. Therefore, we can use it
1008
# here despite using OpenPGP certificates.
1010
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1011
# u"+AES-256-CBC", u"+SHA1",
1012
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1014
# Use a fallback default, since this MUST be set.
1015
priority = self.server.gnutls_priority
1016
if priority is None:
1017
priority = u"NORMAL"
1018
(gnutls.library.functions
1019
.gnutls_priority_set_direct(session._c_object,
1024
except gnutls.errors.GNUTLSError, error:
1025
logger.warning(u"Handshake failed: %s", error)
1026
# Do not run session.bye() here: the session is not
1027
# established. Just abandon the request.
1029
logger.debug(u"Handshake succeeded")
1031
fpr = self.fingerprint(self.peer_certificate(session))
1032
except (TypeError, gnutls.errors.GNUTLSError), error:
1033
logger.warning(u"Bad certificate: %s", error)
1036
logger.debug(u"Fingerprint: %s", fpr)
1038
for c in self.server.clients:
1039
if c.fingerprint == fpr:
1043
ipc.write(u"NOTFOUND %s %s\n"
1044
% (fpr, unicode(self.client_address)))
1047
# Have to check if client.still_valid(), since it is
1048
# possible that the client timed out while establishing
1049
# the GnuTLS session.
1050
if not client.still_valid():
1051
ipc.write(u"INVALID %s\n" % client.name)
1054
ipc.write(u"SENDING %s\n" % client.name)
1056
while sent_size < len(client.secret):
1057
sent = session.send(client.secret[sent_size:])
1058
logger.debug(u"Sent: %d, remaining: %d",
1059
sent, len(client.secret)
1060
- (sent_size + sent))
1065
def peer_certificate(session):
1066
"Return the peer's OpenPGP certificate as a bytestring"
1067
# If not an OpenPGP certificate...
1068
if (gnutls.library.functions
1069
.gnutls_certificate_type_get(session._c_object)
1070
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1071
# ...do the normal thing
1072
return session.peer_certificate
1073
list_size = ctypes.c_uint(1)
1074
cert_list = (gnutls.library.functions
1075
.gnutls_certificate_get_peers
1076
(session._c_object, ctypes.byref(list_size)))
1077
if not bool(cert_list) and list_size.value != 0:
1078
raise gnutls.errors.GNUTLSError(u"error getting peer"
1080
if list_size.value == 0:
1083
return ctypes.string_at(cert.data, cert.size)
1086
def fingerprint(openpgp):
1087
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1088
# New GnuTLS "datum" with the OpenPGP public key
1089
datum = (gnutls.library.types
1090
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1093
ctypes.c_uint(len(openpgp))))
1094
# New empty GnuTLS certificate
1095
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1096
(gnutls.library.functions
1097
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1098
# Import the OpenPGP public key into the certificate
1099
(gnutls.library.functions
1100
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1101
gnutls.library.constants
1102
.GNUTLS_OPENPGP_FMT_RAW))
1103
# Verify the self signature in the key
1104
crtverify = ctypes.c_uint()
1105
(gnutls.library.functions
1106
.gnutls_openpgp_crt_verify_self(crt, 0,
1107
ctypes.byref(crtverify)))
1108
if crtverify.value != 0:
1109
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1110
raise (gnutls.errors.CertificateSecurityError
1112
# New buffer for the fingerprint
1113
buf = ctypes.create_string_buffer(20)
1114
buf_len = ctypes.c_size_t()
1115
# Get the fingerprint from the certificate into the buffer
1116
(gnutls.library.functions
1117
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1118
ctypes.byref(buf_len)))
1119
# Deinit the certificate
1120
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1121
# Convert the buffer to a Python bytestring
1122
fpr = ctypes.string_at(buf, buf_len.value)
1123
# Convert the bytestring to hexadecimal notation
1124
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1128
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1129
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1130
def process_request(self, request, client_address):
1131
"""Overrides and wraps the original process_request().
1133
This function creates a new pipe in self.pipe
1135
self.pipe = os.pipe()
1136
super(ForkingMixInWithPipe,
1137
self).process_request(request, client_address)
1138
os.close(self.pipe[1]) # close write end
1139
self.add_pipe(self.pipe[0])
1140
def add_pipe(self, pipe):
1141
"""Dummy function; override as necessary"""
1145
class IPv6_TCPServer(ForkingMixInWithPipe,
1146
socketserver.TCPServer, object):
1147
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
652
session = (gnutls.connection
653
.ClientSession(self.request,
657
line = self.request.makefile().readline()
658
logger.debug(u"Protocol version: %r", line)
660
if int(line.strip().split()[0]) > 1:
662
except (ValueError, IndexError, RuntimeError), error:
663
logger.error(u"Unknown protocol version: %s", error)
666
# Note: gnutls.connection.X509Credentials is really a generic
667
# GnuTLS certificate credentials object so long as no X.509
668
# keys are added to it. Therefore, we can use it here despite
669
# using OpenPGP certificates.
671
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
672
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
674
# Use a fallback default, since this MUST be set.
675
priority = self.server.settings.get("priority", "NORMAL")
676
(gnutls.library.functions
677
.gnutls_priority_set_direct(session._c_object,
682
except gnutls.errors.GNUTLSError, error:
683
logger.warning(u"Handshake failed: %s", error)
684
# Do not run session.bye() here: the session is not
685
# established. Just abandon the request.
688
fpr = fingerprint(peer_certificate(session))
689
except (TypeError, gnutls.errors.GNUTLSError), error:
690
logger.warning(u"Bad certificate: %s", error)
693
logger.debug(u"Fingerprint: %s", fpr)
694
for c in self.server.clients:
695
if c.fingerprint == fpr:
699
logger.warning(u"Client not found for fingerprint: %s",
703
# Have to check if client.still_valid(), since it is possible
704
# that the client timed out while establishing the GnuTLS
706
if not client.still_valid():
707
logger.warning(u"Client %(name)s is invalid",
711
## This won't work here, since we're in a fork.
712
# client.bump_timeout()
714
while sent_size < len(client.secret):
715
sent = session.send(client.secret[sent_size:])
716
logger.debug(u"Sent: %d, remaining: %d",
717
sent, len(client.secret)
718
- (sent_size + sent))
723
class IPv6_TCPServer(SocketServer.ForkingMixIn,
724
SocketServer.TCPServer, object):
725
"""IPv6 TCP server. Accepts 'None' as address and/or port.
727
settings: Server settings
728
clients: Set() of Client objects
1150
729
enabled: Boolean; whether this server is activated yet
1151
interface: None or a network interface name (string)
1152
use_ipv6: Boolean; to use IPv6 or not
1154
def __init__(self, server_address, RequestHandlerClass,
1155
interface=None, use_ipv6=True):
1156
self.interface = interface
1158
self.address_family = socket.AF_INET6
1159
socketserver.TCPServer.__init__(self, server_address,
1160
RequestHandlerClass)
731
address_family = socket.AF_INET6
732
def __init__(self, *args, **kwargs):
733
if "settings" in kwargs:
734
self.settings = kwargs["settings"]
735
del kwargs["settings"]
736
if "clients" in kwargs:
737
self.clients = kwargs["clients"]
738
del kwargs["clients"]
740
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
1161
741
def server_bind(self):
1162
742
"""This overrides the normal server_bind() function
1163
743
to bind to an interface if one was specified, and also NOT to
1164
744
bind to an address or port if they were not specified."""
1165
if self.interface is not None:
1166
if SO_BINDTODEVICE is None:
1167
logger.error(u"SO_BINDTODEVICE does not exist;"
1168
u" cannot bind to interface %s",
1172
self.socket.setsockopt(socket.SOL_SOCKET,
1176
except socket.error, error:
1177
if error[0] == errno.EPERM:
1178
logger.error(u"No permission to"
1179
u" bind to interface %s",
1181
elif error[0] == errno.ENOPROTOOPT:
1182
logger.error(u"SO_BINDTODEVICE not available;"
1183
u" cannot bind to interface %s",
745
if self.settings["interface"]:
746
# 25 is from /usr/include/asm-i486/socket.h
747
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
749
self.socket.setsockopt(socket.SOL_SOCKET,
751
self.settings["interface"])
752
except socket.error, error:
753
if error[0] == errno.EPERM:
754
logger.error(u"No permission to"
755
u" bind to interface %s",
756
self.settings["interface"])
1187
759
# Only bind(2) the socket if we really need to.
1188
760
if self.server_address[0] or self.server_address[1]:
1189
761
if not self.server_address[0]:
1190
if self.address_family == socket.AF_INET6:
1191
any_address = u"::" # in6addr_any
1193
any_address = socket.INADDR_ANY
1194
self.server_address = (any_address,
763
self.server_address = (in6addr_any,
1195
764
self.server_address[1])
1196
765
elif not self.server_address[1]:
1197
766
self.server_address = (self.server_address[0],
1199
# if self.interface:
768
# if self.settings["interface"]:
1200
769
# self.server_address = (self.server_address[0],
1203
772
# if_nametoindex
1205
return socketserver.TCPServer.server_bind(self)
1208
class MandosServer(IPv6_TCPServer):
1212
clients: set of Client objects
1213
gnutls_priority GnuTLS priority string
1214
use_dbus: Boolean; to emit D-Bus signals or not
1216
Assumes a gobject.MainLoop event loop.
1218
def __init__(self, server_address, RequestHandlerClass,
1219
interface=None, use_ipv6=True, clients=None,
1220
gnutls_priority=None, use_dbus=True):
1221
self.enabled = False
1222
self.clients = clients
1223
if self.clients is None:
1224
self.clients = set()
1225
self.use_dbus = use_dbus
1226
self.gnutls_priority = gnutls_priority
1227
IPv6_TCPServer.__init__(self, server_address,
1228
RequestHandlerClass,
1229
interface = interface,
1230
use_ipv6 = use_ipv6)
775
return super(IPv6_TCPServer, self).server_bind()
1231
776
def server_activate(self):
1232
777
if self.enabled:
1233
return socketserver.TCPServer.server_activate(self)
778
return super(IPv6_TCPServer, self).server_activate()
1234
779
def enable(self):
1235
780
self.enabled = True
1236
def add_pipe(self, pipe):
1237
# Call "handle_ipc" for both data and EOF events
1238
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1240
def handle_ipc(self, source, condition, file_objects={}):
1242
gobject.IO_IN: u"IN", # There is data to read.
1243
gobject.IO_OUT: u"OUT", # Data can be written (without
1245
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1246
gobject.IO_ERR: u"ERR", # Error condition.
1247
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1248
# broken, usually for pipes and
1251
conditions_string = ' | '.join(name
1253
condition_names.iteritems()
1254
if cond & condition)
1255
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1258
# Turn the pipe file descriptor into a Python file object
1259
if source not in file_objects:
1260
file_objects[source] = os.fdopen(source, u"r", 1)
1262
# Read a line from the file object
1263
cmdline = file_objects[source].readline()
1264
if not cmdline: # Empty line means end of file
1265
# close the IPC pipe
1266
file_objects[source].close()
1267
del file_objects[source]
1269
# Stop calling this function
1272
logger.debug(u"IPC command: %r", cmdline)
1274
# Parse and act on command
1275
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1277
if cmd == u"NOTFOUND":
1278
logger.warning(u"Client not found for fingerprint: %s",
1282
mandos_dbus_service.ClientNotFound(args)
1283
elif cmd == u"INVALID":
1284
for client in self.clients:
1285
if client.name == args:
1286
logger.warning(u"Client %s is invalid", args)
1292
logger.error(u"Unknown client %s is invalid", args)
1293
elif cmd == u"SENDING":
1294
for client in self.clients:
1295
if client.name == args:
1296
logger.info(u"Sending secret to %s", client.name)
1300
client.ReceivedSecret()
1303
logger.error(u"Sending secret to unknown client %s",
1306
logger.error(u"Unknown IPC command: %r", cmdline)
1308
# Keep calling this function
1312
783
def string_to_delta(interval):
1313
784
"""Parse a string and return a datetime.timedelta
1315
>>> string_to_delta(u'7d')
786
>>> string_to_delta('7d')
1316
787
datetime.timedelta(7)
1317
>>> string_to_delta(u'60s')
788
>>> string_to_delta('60s')
1318
789
datetime.timedelta(0, 60)
1319
>>> string_to_delta(u'60m')
790
>>> string_to_delta('60m')
1320
791
datetime.timedelta(0, 3600)
1321
>>> string_to_delta(u'24h')
792
>>> string_to_delta('24h')
1322
793
datetime.timedelta(1)
1323
794
>>> string_to_delta(u'1w')
1324
795
datetime.timedelta(7)
1325
>>> string_to_delta(u'5m 30s')
796
>>> string_to_delta('5m 30s')
1326
797
datetime.timedelta(0, 330)
1328
799
timevalue = datetime.timedelta(0)
1436
923
# Default values for config file for server-global settings
1437
server_defaults = { u"interface": u"",
1442
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1443
u"servicename": u"Mandos",
1444
u"use_dbus": u"True",
1445
u"use_ipv6": u"True",
924
server_defaults = { "interface": "",
929
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
930
"servicename": "Mandos",
1448
934
# Parse config file for server-global settings
1449
server_config = configparser.SafeConfigParser(server_defaults)
935
server_config = ConfigParser.SafeConfigParser(server_defaults)
1450
936
del server_defaults
1451
server_config.read(os.path.join(options.configdir,
937
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1453
938
# Convert the SafeConfigParser object to a dict
1454
939
server_settings = server_config.defaults()
1455
# Use the appropriate methods on the non-string config options
1456
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1457
server_settings[option] = server_config.getboolean(u"DEFAULT",
1459
if server_settings["port"]:
1460
server_settings["port"] = server_config.getint(u"DEFAULT",
940
# Use getboolean on the boolean config options
941
server_settings["debug"] = (server_config.getboolean
942
("DEFAULT", "debug"))
943
server_settings["use_dbus"] = (server_config.getboolean
944
("DEFAULT", "use_dbus"))
1462
945
del server_config
1464
947
# Override the settings from the config file with command line
1465
948
# options, if set.
1466
for option in (u"interface", u"address", u"port", u"debug",
1467
u"priority", u"servicename", u"configdir",
1468
u"use_dbus", u"use_ipv6"):
949
for option in ("interface", "address", "port", "debug",
950
"priority", "servicename", "configdir",
1469
952
value = getattr(options, option)
1470
953
if value is not None:
1471
954
server_settings[option] = value
1473
# Force all strings to be unicode
1474
for option in server_settings.keys():
1475
if type(server_settings[option]) is str:
1476
server_settings[option] = unicode(server_settings[option])
1477
956
# Now we have our good server settings in "server_settings"
1479
##################################################################
1481
958
# For convenience
1482
debug = server_settings[u"debug"]
1483
use_dbus = server_settings[u"use_dbus"]
1484
use_ipv6 = server_settings[u"use_ipv6"]
959
debug = server_settings["debug"]
960
use_dbus = server_settings["use_dbus"]
1487
963
syslogger.setLevel(logging.WARNING)
1488
964
console.setLevel(logging.WARNING)
1490
if server_settings[u"servicename"] != u"Mandos":
966
if server_settings["servicename"] != "Mandos":
1491
967
syslogger.setFormatter(logging.Formatter
1492
(u'Mandos (%s) [%%(process)d]:'
1493
u' %%(levelname)s: %%(message)s'
1494
% server_settings[u"servicename"]))
968
('Mandos (%s): %%(levelname)s:'
970
% server_settings["servicename"]))
1496
972
# Parse config file with clients
1497
client_defaults = { u"timeout": u"1h",
1499
u"checker": u"fping -q -- %%(host)s",
973
client_defaults = { "timeout": "1h",
975
"checker": "fping -q -- %%(host)s",
1502
client_config = configparser.SafeConfigParser(client_defaults)
1503
client_config.read(os.path.join(server_settings[u"configdir"],
1506
global mandos_dbus_service
1507
mandos_dbus_service = None
1509
tcp_server = MandosServer((server_settings[u"address"],
1510
server_settings[u"port"]),
1512
interface=server_settings[u"interface"],
1515
server_settings[u"priority"],
1517
pidfilename = u"/var/run/mandos.pid"
1519
pidfile = open(pidfilename, u"w")
1521
logger.error(u"Could not open file %r", pidfilename)
1524
uid = pwd.getpwnam(u"_mandos").pw_uid
1525
gid = pwd.getpwnam(u"_mandos").pw_gid
978
client_config = ConfigParser.SafeConfigParser(client_defaults)
979
client_config.read(os.path.join(server_settings["configdir"],
983
tcp_server = IPv6_TCPServer((server_settings["address"],
984
server_settings["port"]),
986
settings=server_settings,
988
pidfilename = "/var/run/mandos.pid"
990
pidfile = open(pidfilename, "w")
991
except IOError, error:
992
logger.error("Could not open file %r", pidfilename)
995
uid = pwd.getpwnam("_mandos").pw_uid
996
gid = pwd.getpwnam("_mandos").pw_gid
1526
997
except KeyError:
1528
uid = pwd.getpwnam(u"mandos").pw_uid
1529
gid = pwd.getpwnam(u"mandos").pw_gid
999
uid = pwd.getpwnam("mandos").pw_uid
1000
gid = pwd.getpwnam("mandos").pw_gid
1530
1001
except KeyError:
1532
uid = pwd.getpwnam(u"nobody").pw_uid
1533
gid = pwd.getpwnam(u"nobody").pw_gid
1003
uid = pwd.getpwnam("nobody").pw_uid
1004
gid = pwd.getpwnam("nogroup").pw_gid
1534
1005
except KeyError:
1540
1011
except OSError, error:
1541
1012
if error[0] != errno.EPERM:
1544
# Enable all possible GnuTLS debugging
1546
# "Use a log level over 10 to enable all debugging options."
1548
gnutls.library.functions.gnutls_global_set_log_level(11)
1550
@gnutls.library.types.gnutls_log_func
1551
def debug_gnutls(level, string):
1552
logger.debug(u"GnuTLS: %s", string[:-1])
1554
(gnutls.library.functions
1555
.gnutls_global_set_log_function(debug_gnutls))
1016
service = AvahiService(name = server_settings["servicename"],
1017
servicetype = "_mandos._tcp", )
1018
if server_settings["interface"]:
1019
service.interface = (if_nametoindex
1020
(server_settings["interface"]))
1557
1022
global main_loop
1558
1025
# From the Avahi example code
1559
1026
DBusGMainLoop(set_as_default=True )
1560
1027
main_loop = gobject.MainLoop()
1561
1028
bus = dbus.SystemBus()
1029
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1030
avahi.DBUS_PATH_SERVER),
1031
avahi.DBUS_INTERFACE_SERVER)
1562
1032
# End of Avahi example code
1564
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1565
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1566
service = AvahiService(name = server_settings[u"servicename"],
1567
servicetype = u"_mandos._tcp",
1568
protocol = protocol, bus = bus)
1569
if server_settings["interface"]:
1570
service.interface = (if_nametoindex
1571
(str(server_settings[u"interface"])))
1034
bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
1573
client_class = Client
1575
client_class = functools.partial(ClientDBus, bus = bus)
1576
tcp_server.clients.update(set(
1577
client_class(name = section,
1578
config= dict(client_config.items(section)))
1579
for section in client_config.sections()))
1580
if not tcp_server.clients:
1037
clients.update(Set(Client(name = section,
1039
= dict(client_config.items(section)),
1040
use_dbus = use_dbus)
1041
for section in client_config.sections()))
1581
1043
logger.warning(u"No clients defined")