468
435
if error.errno != errno.ESRCH: # No such process
470
437
self.checker = None
439
self.PropertyChanged(dbus.String(u"checker_running"),
440
dbus.Boolean(False, variant_level=1))
472
442
def still_valid(self):
473
443
"""Has the timeout not yet passed for this client?"""
474
if not getattr(self, u"enabled", False):
444
if not getattr(self, "enabled", False):
476
446
now = datetime.datetime.utcnow()
477
447
if self.last_checked_ok is None:
478
448
return now < (self.created + self.timeout)
480
450
return now < (self.last_checked_ok + self.timeout)
483
def dbus_service_property(dbus_interface, signature=u"v",
484
access=u"readwrite", byte_arrays=False):
485
"""Decorators for marking methods of a DBusObjectWithProperties to
486
become properties on the D-Bus.
488
The decorated method will be called with no arguments by "Get"
489
and with one argument by "Set".
491
The parameters, where they are supported, are the same as
492
dbus.service.method, except there is only "signature", since the
493
type from Get() and the type sent to Set() is the same.
496
func._dbus_is_property = True
497
func._dbus_interface = dbus_interface
498
func._dbus_signature = signature
499
func._dbus_access = access
500
func._dbus_name = func.__name__
501
if func._dbus_name.endswith(u"_dbus_property"):
502
func._dbus_name = func._dbus_name[:-14]
503
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
508
class DBusPropertyException(dbus.exceptions.DBusException):
509
"""A base class for D-Bus property-related exceptions
511
def __unicode__(self):
512
return unicode(str(self))
515
class DBusPropertyAccessException(DBusPropertyException):
516
"""A property's access permissions disallows an operation.
521
class DBusPropertyNotFound(DBusPropertyException):
522
"""An attempt was made to access a non-existing property.
527
class DBusObjectWithProperties(dbus.service.Object):
528
"""A D-Bus object with properties.
530
Classes inheriting from this can use the dbus_service_property
531
decorator to expose methods as D-Bus properties. It exposes the
532
standard Get(), Set(), and GetAll() methods on the D-Bus.
536
def _is_dbus_property(obj):
537
return getattr(obj, u"_dbus_is_property", False)
539
def _get_all_dbus_properties(self):
540
"""Returns a generator of (name, attribute) pairs
542
return ((prop._dbus_name, prop)
544
inspect.getmembers(self, self._is_dbus_property))
546
def _get_dbus_property(self, interface_name, property_name):
547
"""Returns a bound method if one exists which is a D-Bus
548
property with the specified name and interface.
550
for name in (property_name,
551
property_name + u"_dbus_property"):
552
prop = getattr(self, name, None)
554
or not self._is_dbus_property(prop)
555
or prop._dbus_name != property_name
556
or (interface_name and prop._dbus_interface
557
and interface_name != prop._dbus_interface)):
561
raise DBusPropertyNotFound(self.dbus_object_path + u":"
562
+ interface_name + u"."
565
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
567
def Get(self, interface_name, property_name):
568
"""Standard D-Bus property Get() method, see D-Bus standard.
570
prop = self._get_dbus_property(interface_name, property_name)
571
if prop._dbus_access == u"write":
572
raise DBusPropertyAccessException(property_name)
574
if not hasattr(value, u"variant_level"):
576
return type(value)(value, variant_level=value.variant_level+1)
578
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
579
def Set(self, interface_name, property_name, value):
580
"""Standard D-Bus property Set() method, see D-Bus standard.
582
prop = self._get_dbus_property(interface_name, property_name)
583
if prop._dbus_access == u"read":
584
raise DBusPropertyAccessException(property_name)
585
if prop._dbus_get_args_options[u"byte_arrays"]:
586
value = dbus.ByteArray(''.join(unichr(byte)
590
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
591
out_signature=u"a{sv}")
592
def GetAll(self, interface_name):
593
"""Standard D-Bus property GetAll() method, see D-Bus
596
Note: Will not include properties with access="write".
599
for name, prop in self._get_all_dbus_properties():
601
and interface_name != prop._dbus_interface):
602
# Interface non-empty but did not match
604
# Ignore write-only properties
605
if prop._dbus_access == u"write":
608
if not hasattr(value, u"variant_level"):
611
all[name] = type(value)(value, variant_level=
612
value.variant_level+1)
613
return dbus.Dictionary(all, signature=u"sv")
615
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
617
path_keyword='object_path',
618
connection_keyword='connection')
619
def Introspect(self, object_path, connection):
620
"""Standard D-Bus method, overloaded to insert property tags.
622
xmlstring = dbus.service.Object.Introspect(self, object_path,
624
document = xml.dom.minidom.parseString(xmlstring)
626
def make_tag(document, name, prop):
627
e = document.createElement(u"property")
628
e.setAttribute(u"name", name)
629
e.setAttribute(u"type", prop._dbus_signature)
630
e.setAttribute(u"access", prop._dbus_access)
632
for if_tag in document.getElementsByTagName(u"interface"):
633
for tag in (make_tag(document, name, prop)
635
in self._get_all_dbus_properties()
636
if prop._dbus_interface
637
== if_tag.getAttribute(u"name")):
638
if_tag.appendChild(tag)
639
xmlstring = document.toxml(u"utf-8")
644
class ClientDBus(Client, DBusObjectWithProperties):
645
"""A Client class using D-Bus
648
dbus_object_path: dbus.ObjectPath
649
bus: dbus.SystemBus()
651
# dbus.service.Object doesn't use super(), so we can't either.
653
def __init__(self, bus = None, *args, **kwargs):
655
Client.__init__(self, *args, **kwargs)
656
# Only now, when this client is initialized, can it show up on
658
self.dbus_object_path = (dbus.ObjectPath
660
+ self.name.replace(u".", u"_")))
661
DBusObjectWithProperties.__init__(self, self.bus,
662
self.dbus_object_path)
665
def _datetime_to_dbus(dt, variant_level=0):
666
"""Convert a UTC datetime.datetime() to a D-Bus type."""
667
return dbus.String(dt.isoformat(),
668
variant_level=variant_level)
671
oldstate = getattr(self, u"enabled", False)
672
r = Client.enable(self)
673
if oldstate != self.enabled:
675
self.PropertyChanged(dbus.String(u"enabled"),
676
dbus.Boolean(True, variant_level=1))
677
self.PropertyChanged(
678
dbus.String(u"last_enabled"),
679
self._datetime_to_dbus(self.last_enabled,
683
def disable(self, signal = True):
684
oldstate = getattr(self, u"enabled", False)
685
r = Client.disable(self)
686
if signal and oldstate != self.enabled:
688
self.PropertyChanged(dbus.String(u"enabled"),
689
dbus.Boolean(False, variant_level=1))
692
def __del__(self, *args, **kwargs):
694
self.remove_from_connection()
697
if hasattr(DBusObjectWithProperties, u"__del__"):
698
DBusObjectWithProperties.__del__(self, *args, **kwargs)
699
Client.__del__(self, *args, **kwargs)
701
def checker_callback(self, pid, condition, command,
703
self.checker_callback_tag = None
706
self.PropertyChanged(dbus.String(u"checker_running"),
707
dbus.Boolean(False, variant_level=1))
708
if os.WIFEXITED(condition):
709
exitstatus = os.WEXITSTATUS(condition)
711
self.CheckerCompleted(dbus.Int16(exitstatus),
712
dbus.Int64(condition),
713
dbus.String(command))
716
self.CheckerCompleted(dbus.Int16(-1),
717
dbus.Int64(condition),
718
dbus.String(command))
720
return Client.checker_callback(self, pid, condition, command,
723
def checked_ok(self, *args, **kwargs):
724
r = Client.checked_ok(self, *args, **kwargs)
726
self.PropertyChanged(
727
dbus.String(u"last_checked_ok"),
728
(self._datetime_to_dbus(self.last_checked_ok,
732
def start_checker(self, *args, **kwargs):
733
old_checker = self.checker
734
if self.checker is not None:
735
old_checker_pid = self.checker.pid
737
old_checker_pid = None
738
r = Client.start_checker(self, *args, **kwargs)
739
# Only if new checker process was started
740
if (self.checker is not None
741
and old_checker_pid != self.checker.pid):
743
self.CheckerStarted(self.current_checker_command)
744
self.PropertyChanged(
745
dbus.String(u"checker_running"),
746
dbus.Boolean(True, variant_level=1))
749
def stop_checker(self, *args, **kwargs):
750
old_checker = getattr(self, u"checker", None)
751
r = Client.stop_checker(self, *args, **kwargs)
752
if (old_checker is not None
753
and getattr(self, u"checker", None) is None):
754
self.PropertyChanged(dbus.String(u"checker_running"),
755
dbus.Boolean(False, variant_level=1))
758
452
## D-Bus methods & signals
759
453
_interface = u"se.bsnet.fukt.Mandos.Client"
761
455
# CheckedOK - method
762
@dbus.service.method(_interface)
764
return self.checked_ok()
456
CheckedOK = dbus.service.method(_interface)(checked_ok)
457
CheckedOK.__name__ = "CheckedOK"
766
459
# CheckerCompleted - signal
767
@dbus.service.signal(_interface, signature=u"nxs")
460
@dbus.service.signal(_interface, signature="nxs")
768
461
def CheckerCompleted(self, exitcode, waitstatus, command):
772
465
# CheckerStarted - signal
773
@dbus.service.signal(_interface, signature=u"s")
466
@dbus.service.signal(_interface, signature="s")
774
467
def CheckerStarted(self, command):
471
# GetAllProperties - method
472
@dbus.service.method(_interface, out_signature="a{sv}")
473
def GetAllProperties(self):
475
return dbus.Dictionary({
477
dbus.String(self.name, variant_level=1),
478
dbus.String("fingerprint"):
479
dbus.String(self.fingerprint, variant_level=1),
481
dbus.String(self.host, variant_level=1),
482
dbus.String("created"):
483
_datetime_to_dbus(self.created, variant_level=1),
484
dbus.String("last_enabled"):
485
(_datetime_to_dbus(self.last_enabled,
487
if self.last_enabled is not None
488
else dbus.Boolean(False, variant_level=1)),
489
dbus.String("enabled"):
490
dbus.Boolean(self.enabled, variant_level=1),
491
dbus.String("last_checked_ok"):
492
(_datetime_to_dbus(self.last_checked_ok,
494
if self.last_checked_ok is not None
495
else dbus.Boolean (False, variant_level=1)),
496
dbus.String("timeout"):
497
dbus.UInt64(self.timeout_milliseconds(),
499
dbus.String("interval"):
500
dbus.UInt64(self.interval_milliseconds(),
502
dbus.String("checker"):
503
dbus.String(self.checker_command,
505
dbus.String("checker_running"):
506
dbus.Boolean(self.checker is not None,
508
dbus.String("object_path"):
509
dbus.ObjectPath(self.dbus_object_path,
513
# IsStillValid - method
514
IsStillValid = (dbus.service.method(_interface, out_signature="b")
516
IsStillValid.__name__ = "IsStillValid"
778
518
# PropertyChanged - signal
779
@dbus.service.signal(_interface, signature=u"sv")
519
@dbus.service.signal(_interface, signature="sv")
780
520
def PropertyChanged(self, property, value):
784
# ReceivedSecret - signal
785
@dbus.service.signal(_interface)
786
def ReceivedSecret(self):
791
@dbus.service.signal(_interface)
524
# SetChecker - method
525
@dbus.service.method(_interface, in_signature="s")
526
def SetChecker(self, checker):
527
"D-Bus setter method"
528
self.checker_command = checker
530
self.PropertyChanged(dbus.String(u"checker"),
531
dbus.String(self.checker_command,
535
@dbus.service.method(_interface, in_signature="s")
536
def SetHost(self, host):
537
"D-Bus setter method"
540
self.PropertyChanged(dbus.String(u"host"),
541
dbus.String(self.host, variant_level=1))
543
# SetInterval - method
544
@dbus.service.method(_interface, in_signature="t")
545
def SetInterval(self, milliseconds):
546
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
548
self.PropertyChanged(dbus.String(u"interval"),
549
(dbus.UInt64(self.interval_milliseconds(),
553
@dbus.service.method(_interface, in_signature="ay",
555
def SetSecret(self, secret):
556
"D-Bus setter method"
557
self.secret = str(secret)
559
# SetTimeout - method
560
@dbus.service.method(_interface, in_signature="t")
561
def SetTimeout(self, milliseconds):
562
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
564
self.PropertyChanged(dbus.String(u"timeout"),
565
(dbus.UInt64(self.timeout_milliseconds(),
796
568
# Enable - method
797
@dbus.service.method(_interface)
569
Enable = dbus.service.method(_interface)(enable)
570
Enable.__name__ = "Enable"
802
572
# StartChecker - method
803
573
@dbus.service.method(_interface)
814
584
# StopChecker - method
815
@dbus.service.method(_interface)
816
def StopChecker(self):
820
@dbus_service_property(_interface, signature=u"s", access=u"read")
821
def name_dbus_property(self):
822
return dbus.String(self.name)
824
# fingerprint - property
825
@dbus_service_property(_interface, signature=u"s", access=u"read")
826
def fingerprint_dbus_property(self):
827
return dbus.String(self.fingerprint)
830
@dbus_service_property(_interface, signature=u"s",
832
def host_dbus_property(self, value=None):
833
if value is None: # get
834
return dbus.String(self.host)
837
self.PropertyChanged(dbus.String(u"host"),
838
dbus.String(value, variant_level=1))
841
@dbus_service_property(_interface, signature=u"s", access=u"read")
842
def created_dbus_property(self):
843
return dbus.String(self._datetime_to_dbus(self.created))
845
# last_enabled - property
846
@dbus_service_property(_interface, signature=u"s", access=u"read")
847
def last_enabled_dbus_property(self):
848
if self.last_enabled is None:
849
return dbus.String(u"")
850
return dbus.String(self._datetime_to_dbus(self.last_enabled))
853
@dbus_service_property(_interface, signature=u"b",
855
def enabled_dbus_property(self, value=None):
856
if value is None: # get
857
return dbus.Boolean(self.enabled)
863
# last_checked_ok - property
864
@dbus_service_property(_interface, signature=u"s",
866
def last_checked_ok_dbus_property(self, value=None):
867
if value is not None:
870
if self.last_checked_ok is None:
871
return dbus.String(u"")
872
return dbus.String(self._datetime_to_dbus(self
876
@dbus_service_property(_interface, signature=u"t",
878
def timeout_dbus_property(self, value=None):
879
if value is None: # get
880
return dbus.UInt64(self.timeout_milliseconds())
881
self.timeout = datetime.timedelta(0, 0, 0, value)
883
self.PropertyChanged(dbus.String(u"timeout"),
884
dbus.UInt64(value, variant_level=1))
885
if getattr(self, u"disable_initiator_tag", None) is None:
888
gobject.source_remove(self.disable_initiator_tag)
889
self.disable_initiator_tag = None
891
_timedelta_to_milliseconds((self
897
# The timeout has passed
900
self.disable_initiator_tag = (gobject.timeout_add
901
(time_to_die, self.disable))
903
# interval - property
904
@dbus_service_property(_interface, signature=u"t",
906
def interval_dbus_property(self, value=None):
907
if value is None: # get
908
return dbus.UInt64(self.interval_milliseconds())
909
self.interval = datetime.timedelta(0, 0, 0, value)
911
self.PropertyChanged(dbus.String(u"interval"),
912
dbus.UInt64(value, variant_level=1))
913
if getattr(self, u"checker_initiator_tag", None) is None:
915
# Reschedule checker run
916
gobject.source_remove(self.checker_initiator_tag)
917
self.checker_initiator_tag = (gobject.timeout_add
918
(value, self.start_checker))
919
self.start_checker() # Start one now, too
922
@dbus_service_property(_interface, signature=u"s",
924
def checker_dbus_property(self, value=None):
925
if value is None: # get
926
return dbus.String(self.checker_command)
927
self.checker_command = value
929
self.PropertyChanged(dbus.String(u"checker"),
930
dbus.String(self.checker_command,
933
# checker_running - property
934
@dbus_service_property(_interface, signature=u"b",
936
def checker_running_dbus_property(self, value=None):
937
if value is None: # get
938
return dbus.Boolean(self.checker is not None)
944
# object_path - property
945
@dbus_service_property(_interface, signature=u"o", access=u"read")
946
def object_path_dbus_property(self):
947
return self.dbus_object_path # is already a dbus.ObjectPath
950
@dbus_service_property(_interface, signature=u"ay",
951
access=u"write", byte_arrays=True)
952
def secret_dbus_property(self, value):
953
self.secret = str(value)
585
StopChecker = dbus.service.method(_interface)(stop_checker)
586
StopChecker.__name__ = "StopChecker"
958
class ClientHandler(socketserver.BaseRequestHandler, object):
959
"""A class to handle client connections.
961
Instantiated once for each connection to handle it.
591
def peer_certificate(session):
592
"Return the peer's OpenPGP certificate as a bytestring"
593
# If not an OpenPGP certificate...
594
if (gnutls.library.functions
595
.gnutls_certificate_type_get(session._c_object)
596
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
597
# ...do the normal thing
598
return session.peer_certificate
599
list_size = ctypes.c_uint(1)
600
cert_list = (gnutls.library.functions
601
.gnutls_certificate_get_peers
602
(session._c_object, ctypes.byref(list_size)))
603
if not bool(cert_list) and list_size.value != 0:
604
raise gnutls.errors.GNUTLSError("error getting peer"
606
if list_size.value == 0:
609
return ctypes.string_at(cert.data, cert.size)
612
def fingerprint(openpgp):
613
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
614
# New GnuTLS "datum" with the OpenPGP public key
615
datum = (gnutls.library.types
616
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
619
ctypes.c_uint(len(openpgp))))
620
# New empty GnuTLS certificate
621
crt = gnutls.library.types.gnutls_openpgp_crt_t()
622
(gnutls.library.functions
623
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
624
# Import the OpenPGP public key into the certificate
625
(gnutls.library.functions
626
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
627
gnutls.library.constants
628
.GNUTLS_OPENPGP_FMT_RAW))
629
# Verify the self signature in the key
630
crtverify = ctypes.c_uint()
631
(gnutls.library.functions
632
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
633
if crtverify.value != 0:
634
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
635
raise gnutls.errors.CertificateSecurityError("Verify failed")
636
# New buffer for the fingerprint
637
buf = ctypes.create_string_buffer(20)
638
buf_len = ctypes.c_size_t()
639
# Get the fingerprint from the certificate into the buffer
640
(gnutls.library.functions
641
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
642
ctypes.byref(buf_len)))
643
# Deinit the certificate
644
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
645
# Convert the buffer to a Python bytestring
646
fpr = ctypes.string_at(buf, buf_len.value)
647
# Convert the bytestring to hexadecimal notation
648
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
652
class TCP_handler(SocketServer.BaseRequestHandler, object):
653
"""A TCP request handler class.
654
Instantiated by IPv6_TCPServer for each request to handle it.
962
655
Note: This will run in its own forked process."""
964
657
def handle(self):
965
658
logger.info(u"TCP connection from: %s",
966
659
unicode(self.client_address))
967
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
968
# Open IPC pipe to parent process
969
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
970
session = (gnutls.connection
971
.ClientSession(self.request,
975
line = self.request.makefile().readline()
976
logger.debug(u"Protocol version: %r", line)
978
if int(line.strip().split()[0]) > 1:
980
except (ValueError, IndexError, RuntimeError), error:
981
logger.error(u"Unknown protocol version: %s", error)
984
# Note: gnutls.connection.X509Credentials is really a
985
# generic GnuTLS certificate credentials object so long as
986
# no X.509 keys are added to it. Therefore, we can use it
987
# here despite using OpenPGP certificates.
989
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
990
# u"+AES-256-CBC", u"+SHA1",
991
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
993
# Use a fallback default, since this MUST be set.
994
priority = self.server.gnutls_priority
997
(gnutls.library.functions
998
.gnutls_priority_set_direct(session._c_object,
1003
except gnutls.errors.GNUTLSError, error:
1004
logger.warning(u"Handshake failed: %s", error)
1005
# Do not run session.bye() here: the session is not
1006
# established. Just abandon the request.
1008
logger.debug(u"Handshake succeeded")
1010
fpr = self.fingerprint(self.peer_certificate(session))
1011
except (TypeError, gnutls.errors.GNUTLSError), error:
1012
logger.warning(u"Bad certificate: %s", error)
1015
logger.debug(u"Fingerprint: %s", fpr)
1017
for c in self.server.clients:
1018
if c.fingerprint == fpr:
1022
ipc.write(u"NOTFOUND %s %s\n"
1023
% (fpr, unicode(self.client_address)))
1026
# Have to check if client.still_valid(), since it is
1027
# possible that the client timed out while establishing
1028
# the GnuTLS session.
1029
if not client.still_valid():
1030
ipc.write(u"INVALID %s\n" % client.name)
1033
ipc.write(u"SENDING %s\n" % client.name)
1035
while sent_size < len(client.secret):
1036
sent = session.send(client.secret[sent_size:])
1037
logger.debug(u"Sent: %d, remaining: %d",
1038
sent, len(client.secret)
1039
- (sent_size + sent))
1044
def peer_certificate(session):
1045
"Return the peer's OpenPGP certificate as a bytestring"
1046
# If not an OpenPGP certificate...
1047
if (gnutls.library.functions
1048
.gnutls_certificate_type_get(session._c_object)
1049
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1050
# ...do the normal thing
1051
return session.peer_certificate
1052
list_size = ctypes.c_uint(1)
1053
cert_list = (gnutls.library.functions
1054
.gnutls_certificate_get_peers
1055
(session._c_object, ctypes.byref(list_size)))
1056
if not bool(cert_list) and list_size.value != 0:
1057
raise gnutls.errors.GNUTLSError(u"error getting peer"
1059
if list_size.value == 0:
1062
return ctypes.string_at(cert.data, cert.size)
1065
def fingerprint(openpgp):
1066
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1067
# New GnuTLS "datum" with the OpenPGP public key
1068
datum = (gnutls.library.types
1069
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1072
ctypes.c_uint(len(openpgp))))
1073
# New empty GnuTLS certificate
1074
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1075
(gnutls.library.functions
1076
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1077
# Import the OpenPGP public key into the certificate
1078
(gnutls.library.functions
1079
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1080
gnutls.library.constants
1081
.GNUTLS_OPENPGP_FMT_RAW))
1082
# Verify the self signature in the key
1083
crtverify = ctypes.c_uint()
1084
(gnutls.library.functions
1085
.gnutls_openpgp_crt_verify_self(crt, 0,
1086
ctypes.byref(crtverify)))
1087
if crtverify.value != 0:
1088
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1089
raise (gnutls.errors.CertificateSecurityError
1091
# New buffer for the fingerprint
1092
buf = ctypes.create_string_buffer(20)
1093
buf_len = ctypes.c_size_t()
1094
# Get the fingerprint from the certificate into the buffer
1095
(gnutls.library.functions
1096
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1097
ctypes.byref(buf_len)))
1098
# Deinit the certificate
1099
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1100
# Convert the buffer to a Python bytestring
1101
fpr = ctypes.string_at(buf, buf_len.value)
1102
# Convert the bytestring to hexadecimal notation
1103
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1107
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1108
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1109
def process_request(self, request, client_address):
1110
"""Overrides and wraps the original process_request().
1112
This function creates a new pipe in self.pipe
1114
self.pipe = os.pipe()
1115
super(ForkingMixInWithPipe,
1116
self).process_request(request, client_address)
1117
os.close(self.pipe[1]) # close write end
1118
self.add_pipe(self.pipe[0])
1119
def add_pipe(self, pipe):
1120
"""Dummy function; override as necessary"""
1124
class IPv6_TCPServer(ForkingMixInWithPipe,
1125
socketserver.TCPServer, object):
1126
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
660
session = (gnutls.connection
661
.ClientSession(self.request,
665
line = self.request.makefile().readline()
666
logger.debug(u"Protocol version: %r", line)
668
if int(line.strip().split()[0]) > 1:
670
except (ValueError, IndexError, RuntimeError), error:
671
logger.error(u"Unknown protocol version: %s", error)
674
# Note: gnutls.connection.X509Credentials is really a generic
675
# GnuTLS certificate credentials object so long as no X.509
676
# keys are added to it. Therefore, we can use it here despite
677
# using OpenPGP certificates.
679
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
680
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
682
# Use a fallback default, since this MUST be set.
683
priority = self.server.settings.get("priority", "NORMAL")
684
(gnutls.library.functions
685
.gnutls_priority_set_direct(session._c_object,
690
except gnutls.errors.GNUTLSError, error:
691
logger.warning(u"Handshake failed: %s", error)
692
# Do not run session.bye() here: the session is not
693
# established. Just abandon the request.
695
logger.debug(u"Handshake succeeded")
697
fpr = fingerprint(peer_certificate(session))
698
except (TypeError, gnutls.errors.GNUTLSError), error:
699
logger.warning(u"Bad certificate: %s", error)
702
logger.debug(u"Fingerprint: %s", fpr)
704
for c in self.server.clients:
705
if c.fingerprint == fpr:
709
logger.warning(u"Client not found for fingerprint: %s",
713
# Have to check if client.still_valid(), since it is possible
714
# that the client timed out while establishing the GnuTLS
716
if not client.still_valid():
717
logger.warning(u"Client %(name)s is invalid",
721
## This won't work here, since we're in a fork.
722
# client.checked_ok()
724
while sent_size < len(client.secret):
725
sent = session.send(client.secret[sent_size:])
726
logger.debug(u"Sent: %d, remaining: %d",
727
sent, len(client.secret)
728
- (sent_size + sent))
733
class IPv6_TCPServer(SocketServer.ForkingMixIn,
734
SocketServer.TCPServer, object):
735
"""IPv6 TCP server. Accepts 'None' as address and/or port.
737
settings: Server settings
738
clients: Set() of Client objects
1129
739
enabled: Boolean; whether this server is activated yet
1130
interface: None or a network interface name (string)
1131
use_ipv6: Boolean; to use IPv6 or not
1133
def __init__(self, server_address, RequestHandlerClass,
1134
interface=None, use_ipv6=True):
1135
self.interface = interface
1137
self.address_family = socket.AF_INET6
1138
socketserver.TCPServer.__init__(self, server_address,
1139
RequestHandlerClass)
741
address_family = socket.AF_INET6
742
def __init__(self, *args, **kwargs):
743
if "settings" in kwargs:
744
self.settings = kwargs["settings"]
745
del kwargs["settings"]
746
if "clients" in kwargs:
747
self.clients = kwargs["clients"]
748
del kwargs["clients"]
750
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
1140
751
def server_bind(self):
1141
752
"""This overrides the normal server_bind() function
1142
753
to bind to an interface if one was specified, and also NOT to
1143
754
bind to an address or port if they were not specified."""
1144
if self.interface is not None:
1145
if SO_BINDTODEVICE is None:
1146
logger.error(u"SO_BINDTODEVICE does not exist;"
1147
u" cannot bind to interface %s",
1151
self.socket.setsockopt(socket.SOL_SOCKET,
1155
except socket.error, error:
1156
if error[0] == errno.EPERM:
1157
logger.error(u"No permission to"
1158
u" bind to interface %s",
1160
elif error[0] == errno.ENOPROTOOPT:
1161
logger.error(u"SO_BINDTODEVICE not available;"
1162
u" cannot bind to interface %s",
755
if self.settings["interface"]:
756
# 25 is from /usr/include/asm-i486/socket.h
757
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
759
self.socket.setsockopt(socket.SOL_SOCKET,
761
self.settings["interface"])
762
except socket.error, error:
763
if error[0] == errno.EPERM:
764
logger.error(u"No permission to"
765
u" bind to interface %s",
766
self.settings["interface"])
1166
769
# Only bind(2) the socket if we really need to.
1167
770
if self.server_address[0] or self.server_address[1]:
1168
771
if not self.server_address[0]:
1169
if self.address_family == socket.AF_INET6:
1170
any_address = u"::" # in6addr_any
1172
any_address = socket.INADDR_ANY
1173
self.server_address = (any_address,
773
self.server_address = (in6addr_any,
1174
774
self.server_address[1])
1175
775
elif not self.server_address[1]:
1176
776
self.server_address = (self.server_address[0],
1178
# if self.interface:
778
# if self.settings["interface"]:
1179
779
# self.server_address = (self.server_address[0],
1182
782
# if_nametoindex
1184
return socketserver.TCPServer.server_bind(self)
1187
class MandosServer(IPv6_TCPServer):
1191
clients: set of Client objects
1192
gnutls_priority GnuTLS priority string
1193
use_dbus: Boolean; to emit D-Bus signals or not
1195
Assumes a gobject.MainLoop event loop.
1197
def __init__(self, server_address, RequestHandlerClass,
1198
interface=None, use_ipv6=True, clients=None,
1199
gnutls_priority=None, use_dbus=True):
1200
self.enabled = False
1201
self.clients = clients
1202
if self.clients is None:
1203
self.clients = set()
1204
self.use_dbus = use_dbus
1205
self.gnutls_priority = gnutls_priority
1206
IPv6_TCPServer.__init__(self, server_address,
1207
RequestHandlerClass,
1208
interface = interface,
1209
use_ipv6 = use_ipv6)
785
return super(IPv6_TCPServer, self).server_bind()
1210
786
def server_activate(self):
1211
787
if self.enabled:
1212
return socketserver.TCPServer.server_activate(self)
788
return super(IPv6_TCPServer, self).server_activate()
1213
789
def enable(self):
1214
790
self.enabled = True
1215
def add_pipe(self, pipe):
1216
# Call "handle_ipc" for both data and EOF events
1217
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1219
def handle_ipc(self, source, condition, file_objects={}):
1221
gobject.IO_IN: u"IN", # There is data to read.
1222
gobject.IO_OUT: u"OUT", # Data can be written (without
1224
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1225
gobject.IO_ERR: u"ERR", # Error condition.
1226
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1227
# broken, usually for pipes and
1230
conditions_string = ' | '.join(name
1232
condition_names.iteritems()
1233
if cond & condition)
1234
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1237
# Turn the pipe file descriptor into a Python file object
1238
if source not in file_objects:
1239
file_objects[source] = os.fdopen(source, u"r", 1)
1241
# Read a line from the file object
1242
cmdline = file_objects[source].readline()
1243
if not cmdline: # Empty line means end of file
1244
# close the IPC pipe
1245
file_objects[source].close()
1246
del file_objects[source]
1248
# Stop calling this function
1251
logger.debug(u"IPC command: %r", cmdline)
1253
# Parse and act on command
1254
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1256
if cmd == u"NOTFOUND":
1257
logger.warning(u"Client not found for fingerprint: %s",
1261
mandos_dbus_service.ClientNotFound(args)
1262
elif cmd == u"INVALID":
1263
for client in self.clients:
1264
if client.name == args:
1265
logger.warning(u"Client %s is invalid", args)
1271
logger.error(u"Unknown client %s is invalid", args)
1272
elif cmd == u"SENDING":
1273
for client in self.clients:
1274
if client.name == args:
1275
logger.info(u"Sending secret to %s", client.name)
1279
client.ReceivedSecret()
1282
logger.error(u"Sending secret to unknown client %s",
1285
logger.error(u"Unknown IPC command: %r", cmdline)
1287
# Keep calling this function
1291
793
def string_to_delta(interval):
1292
794
"""Parse a string and return a datetime.timedelta
1294
>>> string_to_delta(u'7d')
796
>>> string_to_delta('7d')
1295
797
datetime.timedelta(7)
1296
>>> string_to_delta(u'60s')
798
>>> string_to_delta('60s')
1297
799
datetime.timedelta(0, 60)
1298
>>> string_to_delta(u'60m')
800
>>> string_to_delta('60m')
1299
801
datetime.timedelta(0, 3600)
1300
>>> string_to_delta(u'24h')
802
>>> string_to_delta('24h')
1301
803
datetime.timedelta(1)
1302
804
>>> string_to_delta(u'1w')
1303
805
datetime.timedelta(7)
1304
>>> string_to_delta(u'5m 30s')
806
>>> string_to_delta('5m 30s')
1305
807
datetime.timedelta(0, 330)
1307
809
timevalue = datetime.timedelta(0)
1415
933
# Default values for config file for server-global settings
1416
server_defaults = { u"interface": u"",
1421
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1422
u"servicename": u"Mandos",
1423
u"use_dbus": u"True",
1424
u"use_ipv6": u"True",
934
server_defaults = { "interface": "",
939
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
940
"servicename": "Mandos",
1427
944
# Parse config file for server-global settings
1428
server_config = configparser.SafeConfigParser(server_defaults)
945
server_config = ConfigParser.SafeConfigParser(server_defaults)
1429
946
del server_defaults
1430
server_config.read(os.path.join(options.configdir,
947
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1432
948
# Convert the SafeConfigParser object to a dict
1433
949
server_settings = server_config.defaults()
1434
950
# Use the appropriate methods on the non-string config options
1435
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1436
server_settings[option] = server_config.getboolean(u"DEFAULT",
951
server_settings["debug"] = server_config.getboolean("DEFAULT",
953
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1438
955
if server_settings["port"]:
1439
server_settings["port"] = server_config.getint(u"DEFAULT",
956
server_settings["port"] = server_config.getint("DEFAULT",
1441
958
del server_config
1443
960
# Override the settings from the config file with command line
1444
961
# options, if set.
1445
for option in (u"interface", u"address", u"port", u"debug",
1446
u"priority", u"servicename", u"configdir",
1447
u"use_dbus", u"use_ipv6"):
962
for option in ("interface", "address", "port", "debug",
963
"priority", "servicename", "configdir",
1448
965
value = getattr(options, option)
1449
966
if value is not None:
1450
967
server_settings[option] = value
1452
# Force all strings to be unicode
1453
for option in server_settings.keys():
1454
if type(server_settings[option]) is str:
1455
server_settings[option] = unicode(server_settings[option])
1456
969
# Now we have our good server settings in "server_settings"
1458
##################################################################
1460
971
# For convenience
1461
debug = server_settings[u"debug"]
1462
use_dbus = server_settings[u"use_dbus"]
1463
use_ipv6 = server_settings[u"use_ipv6"]
972
debug = server_settings["debug"]
973
use_dbus = server_settings["use_dbus"]
1466
976
syslogger.setLevel(logging.WARNING)
1467
977
console.setLevel(logging.WARNING)
1469
if server_settings[u"servicename"] != u"Mandos":
979
if server_settings["servicename"] != "Mandos":
1470
980
syslogger.setFormatter(logging.Formatter
1471
(u'Mandos (%s) [%%(process)d]:'
1472
u' %%(levelname)s: %%(message)s'
1473
% server_settings[u"servicename"]))
981
('Mandos (%s): %%(levelname)s:'
983
% server_settings["servicename"]))
1475
985
# Parse config file with clients
1476
client_defaults = { u"timeout": u"1h",
1478
u"checker": u"fping -q -- %%(host)s",
986
client_defaults = { "timeout": "1h",
988
"checker": "fping -q -- %%(host)s",
1481
client_config = configparser.SafeConfigParser(client_defaults)
1482
client_config.read(os.path.join(server_settings[u"configdir"],
1485
global mandos_dbus_service
1486
mandos_dbus_service = None
1488
tcp_server = MandosServer((server_settings[u"address"],
1489
server_settings[u"port"]),
1491
interface=server_settings[u"interface"],
1494
server_settings[u"priority"],
1496
pidfilename = u"/var/run/mandos.pid"
991
client_config = ConfigParser.SafeConfigParser(client_defaults)
992
client_config.read(os.path.join(server_settings["configdir"],
996
tcp_server = IPv6_TCPServer((server_settings["address"],
997
server_settings["port"]),
999
settings=server_settings,
1001
pidfilename = "/var/run/mandos.pid"
1498
pidfile = open(pidfilename, u"w")
1003
pidfile = open(pidfilename, "w")
1499
1004
except IOError:
1500
logger.error(u"Could not open file %r", pidfilename)
1005
logger.error("Could not open file %r", pidfilename)
1503
uid = pwd.getpwnam(u"_mandos").pw_uid
1504
gid = pwd.getpwnam(u"_mandos").pw_gid
1008
uid = pwd.getpwnam("_mandos").pw_uid
1009
gid = pwd.getpwnam("_mandos").pw_gid
1505
1010
except KeyError:
1507
uid = pwd.getpwnam(u"mandos").pw_uid
1508
gid = pwd.getpwnam(u"mandos").pw_gid
1012
uid = pwd.getpwnam("mandos").pw_uid
1013
gid = pwd.getpwnam("mandos").pw_gid
1509
1014
except KeyError:
1511
uid = pwd.getpwnam(u"nobody").pw_uid
1512
gid = pwd.getpwnam(u"nobody").pw_gid
1016
uid = pwd.getpwnam("nobody").pw_uid
1017
gid = pwd.getpwnam("nogroup").pw_gid
1513
1018
except KeyError: