463
423
if self.checker_callback_tag:
464
424
gobject.source_remove(self.checker_callback_tag)
465
425
self.checker_callback_tag = None
466
if getattr(self, u"checker", None) is None:
426
if getattr(self, "checker", None) is None:
468
428
logger.debug(u"Stopping checker for %(name)s", vars(self))
470
430
os.kill(self.checker.pid, signal.SIGTERM)
472
432
#if self.checker.poll() is None:
473
433
# os.kill(self.checker.pid, signal.SIGKILL)
474
434
except OSError, error:
475
435
if error.errno != errno.ESRCH: # No such process
477
437
self.checker = None
480
def dbus_service_property(dbus_interface, signature=u"v",
481
access=u"readwrite", byte_arrays=False):
482
"""Decorators for marking methods of a DBusObjectWithProperties to
483
become properties on the D-Bus.
485
The decorated method will be called with no arguments by "Get"
486
and with one argument by "Set".
488
The parameters, where they are supported, are the same as
489
dbus.service.method, except there is only "signature", since the
490
type from Get() and the type sent to Set() is the same.
492
# Encoding deeply encoded byte arrays is not supported yet by the
493
# "Set" method, so we fail early here:
494
if byte_arrays and signature != u"ay":
495
raise ValueError(u"Byte arrays not supported for non-'ay'"
496
u" signature %r" % signature)
498
func._dbus_is_property = True
499
func._dbus_interface = dbus_interface
500
func._dbus_signature = signature
501
func._dbus_access = access
502
func._dbus_name = func.__name__
503
if func._dbus_name.endswith(u"_dbus_property"):
504
func._dbus_name = func._dbus_name[:-14]
505
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
510
class DBusPropertyException(dbus.exceptions.DBusException):
511
"""A base class for D-Bus property-related exceptions
513
def __unicode__(self):
514
return unicode(str(self))
517
class DBusPropertyAccessException(DBusPropertyException):
518
"""A property's access permissions disallows an operation.
523
class DBusPropertyNotFound(DBusPropertyException):
524
"""An attempt was made to access a non-existing property.
529
class DBusObjectWithProperties(dbus.service.Object):
530
"""A D-Bus object with properties.
532
Classes inheriting from this can use the dbus_service_property
533
decorator to expose methods as D-Bus properties. It exposes the
534
standard Get(), Set(), and GetAll() methods on the D-Bus.
538
def _is_dbus_property(obj):
539
return getattr(obj, u"_dbus_is_property", False)
541
def _get_all_dbus_properties(self):
542
"""Returns a generator of (name, attribute) pairs
544
return ((prop._dbus_name, prop)
546
inspect.getmembers(self, self._is_dbus_property))
548
def _get_dbus_property(self, interface_name, property_name):
549
"""Returns a bound method if one exists which is a D-Bus
550
property with the specified name and interface.
552
for name in (property_name,
553
property_name + u"_dbus_property"):
554
prop = getattr(self, name, None)
556
or not self._is_dbus_property(prop)
557
or prop._dbus_name != property_name
558
or (interface_name and prop._dbus_interface
559
and interface_name != prop._dbus_interface)):
563
raise DBusPropertyNotFound(self.dbus_object_path + u":"
564
+ interface_name + u"."
567
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
569
def Get(self, interface_name, property_name):
570
"""Standard D-Bus property Get() method, see D-Bus standard.
572
prop = self._get_dbus_property(interface_name, property_name)
573
if prop._dbus_access == u"write":
574
raise DBusPropertyAccessException(property_name)
576
if not hasattr(value, u"variant_level"):
578
return type(value)(value, variant_level=value.variant_level+1)
580
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
581
def Set(self, interface_name, property_name, value):
582
"""Standard D-Bus property Set() method, see D-Bus standard.
584
prop = self._get_dbus_property(interface_name, property_name)
585
if prop._dbus_access == u"read":
586
raise DBusPropertyAccessException(property_name)
587
if prop._dbus_get_args_options[u"byte_arrays"]:
588
# The byte_arrays option is not supported yet on
589
# signatures other than "ay".
590
if prop._dbus_signature != u"ay":
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,
631
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")
662
except (AttributeError, xml.dom.DOMException,
663
xml.parsers.expat.ExpatError), error:
664
logger.error(u"Failed to override Introspection method",
669
class ClientDBus(Client, DBusObjectWithProperties):
670
"""A Client class using D-Bus
673
dbus_object_path: dbus.ObjectPath
674
bus: dbus.SystemBus()
676
# dbus.service.Object doesn't use super(), so we can't either.
678
def __init__(self, bus = None, *args, **kwargs):
680
Client.__init__(self, *args, **kwargs)
681
# Only now, when this client is initialized, can it show up on
683
self.dbus_object_path = (dbus.ObjectPath
685
+ self.name.replace(u".", u"_")))
686
DBusObjectWithProperties.__init__(self, self.bus,
687
self.dbus_object_path)
690
def _datetime_to_dbus(dt, variant_level=0):
691
"""Convert a UTC datetime.datetime() to a D-Bus type."""
692
return dbus.String(dt.isoformat(),
693
variant_level=variant_level)
696
oldstate = getattr(self, u"enabled", False)
697
r = Client.enable(self)
698
if oldstate != self.enabled:
700
self.PropertyChanged(dbus.String(u"enabled"),
701
dbus.Boolean(True, variant_level=1))
702
self.PropertyChanged(
703
dbus.String(u"last_enabled"),
704
self._datetime_to_dbus(self.last_enabled,
708
def disable(self, quiet = False):
709
oldstate = getattr(self, u"enabled", False)
710
r = Client.disable(self, quiet=quiet)
711
if not quiet and oldstate != self.enabled:
713
self.PropertyChanged(dbus.String(u"enabled"),
714
dbus.Boolean(False, variant_level=1))
717
def __del__(self, *args, **kwargs):
719
self.remove_from_connection()
722
if hasattr(DBusObjectWithProperties, u"__del__"):
723
DBusObjectWithProperties.__del__(self, *args, **kwargs)
724
Client.__del__(self, *args, **kwargs)
726
def checker_callback(self, pid, condition, command,
728
self.checker_callback_tag = None
731
self.PropertyChanged(dbus.String(u"checker_running"),
732
dbus.Boolean(False, variant_level=1))
733
if os.WIFEXITED(condition):
734
exitstatus = os.WEXITSTATUS(condition)
736
self.CheckerCompleted(dbus.Int16(exitstatus),
737
dbus.Int64(condition),
738
dbus.String(command))
741
self.CheckerCompleted(dbus.Int16(-1),
742
dbus.Int64(condition),
743
dbus.String(command))
745
return Client.checker_callback(self, pid, condition, command,
748
def checked_ok(self, *args, **kwargs):
749
r = Client.checked_ok(self, *args, **kwargs)
751
self.PropertyChanged(
752
dbus.String(u"last_checked_ok"),
753
(self._datetime_to_dbus(self.last_checked_ok,
757
def start_checker(self, *args, **kwargs):
758
old_checker = self.checker
759
if self.checker is not None:
760
old_checker_pid = self.checker.pid
762
old_checker_pid = None
763
r = Client.start_checker(self, *args, **kwargs)
764
# Only if new checker process was started
765
if (self.checker is not None
766
and old_checker_pid != self.checker.pid):
768
self.CheckerStarted(self.current_checker_command)
769
self.PropertyChanged(
770
dbus.String(u"checker_running"),
771
dbus.Boolean(True, variant_level=1))
774
def stop_checker(self, *args, **kwargs):
775
old_checker = getattr(self, u"checker", None)
776
r = Client.stop_checker(self, *args, **kwargs)
777
if (old_checker is not None
778
and getattr(self, u"checker", None) is None):
779
439
self.PropertyChanged(dbus.String(u"checker_running"),
780
440
dbus.Boolean(False, variant_level=1))
442
def still_valid(self):
443
"""Has the timeout not yet passed for this client?"""
444
if not getattr(self, "enabled", False):
446
now = datetime.datetime.utcnow()
447
if self.last_checked_ok is None:
448
return now < (self.created + self.timeout)
450
return now < (self.last_checked_ok + self.timeout)
783
452
## D-Bus methods & signals
784
453
_interface = u"se.bsnet.fukt.Mandos.Client"
786
455
# CheckedOK - method
787
@dbus.service.method(_interface)
789
return self.checked_ok()
456
CheckedOK = dbus.service.method(_interface)(checked_ok)
457
CheckedOK.__name__ = "CheckedOK"
791
459
# CheckerCompleted - signal
792
@dbus.service.signal(_interface, signature=u"nxs")
460
@dbus.service.signal(_interface, signature="nxs")
793
461
def CheckerCompleted(self, exitcode, waitstatus, command):
797
465
# CheckerStarted - signal
798
@dbus.service.signal(_interface, signature=u"s")
466
@dbus.service.signal(_interface, signature="s")
799
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"
803
518
# PropertyChanged - signal
804
@dbus.service.signal(_interface, signature=u"sv")
519
@dbus.service.signal(_interface, signature="sv")
805
520
def PropertyChanged(self, property, value):
810
@dbus.service.signal(_interface)
816
@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(),
821
568
# Enable - method
822
@dbus.service.method(_interface)
569
Enable = dbus.service.method(_interface)(enable)
570
Enable.__name__ = "Enable"
827
572
# StartChecker - method
828
573
@dbus.service.method(_interface)
839
584
# StopChecker - method
840
@dbus.service.method(_interface)
841
def StopChecker(self):
845
@dbus_service_property(_interface, signature=u"s", access=u"read")
846
def name_dbus_property(self):
847
return dbus.String(self.name)
849
# fingerprint - property
850
@dbus_service_property(_interface, signature=u"s", access=u"read")
851
def fingerprint_dbus_property(self):
852
return dbus.String(self.fingerprint)
855
@dbus_service_property(_interface, signature=u"s",
857
def host_dbus_property(self, value=None):
858
if value is None: # get
859
return dbus.String(self.host)
862
self.PropertyChanged(dbus.String(u"host"),
863
dbus.String(value, variant_level=1))
866
@dbus_service_property(_interface, signature=u"s", access=u"read")
867
def created_dbus_property(self):
868
return dbus.String(self._datetime_to_dbus(self.created))
870
# last_enabled - property
871
@dbus_service_property(_interface, signature=u"s", access=u"read")
872
def last_enabled_dbus_property(self):
873
if self.last_enabled is None:
874
return dbus.String(u"")
875
return dbus.String(self._datetime_to_dbus(self.last_enabled))
878
@dbus_service_property(_interface, signature=u"b",
880
def enabled_dbus_property(self, value=None):
881
if value is None: # get
882
return dbus.Boolean(self.enabled)
888
# last_checked_ok - property
889
@dbus_service_property(_interface, signature=u"s",
891
def last_checked_ok_dbus_property(self, value=None):
892
if value is not None:
895
if self.last_checked_ok is None:
896
return dbus.String(u"")
897
return dbus.String(self._datetime_to_dbus(self
901
@dbus_service_property(_interface, signature=u"t",
903
def timeout_dbus_property(self, value=None):
904
if value is None: # get
905
return dbus.UInt64(self.timeout_milliseconds())
906
self.timeout = datetime.timedelta(0, 0, 0, value)
908
self.PropertyChanged(dbus.String(u"timeout"),
909
dbus.UInt64(value, variant_level=1))
910
if getattr(self, u"disable_initiator_tag", None) is None:
913
gobject.source_remove(self.disable_initiator_tag)
914
self.disable_initiator_tag = None
916
_timedelta_to_milliseconds((self
922
# The timeout has passed
925
self.disable_initiator_tag = (gobject.timeout_add
926
(time_to_die, self.disable))
928
# interval - property
929
@dbus_service_property(_interface, signature=u"t",
931
def interval_dbus_property(self, value=None):
932
if value is None: # get
933
return dbus.UInt64(self.interval_milliseconds())
934
self.interval = datetime.timedelta(0, 0, 0, value)
936
self.PropertyChanged(dbus.String(u"interval"),
937
dbus.UInt64(value, variant_level=1))
938
if getattr(self, u"checker_initiator_tag", None) is None:
940
# Reschedule checker run
941
gobject.source_remove(self.checker_initiator_tag)
942
self.checker_initiator_tag = (gobject.timeout_add
943
(value, self.start_checker))
944
self.start_checker() # Start one now, too
947
@dbus_service_property(_interface, signature=u"s",
949
def checker_dbus_property(self, value=None):
950
if value is None: # get
951
return dbus.String(self.checker_command)
952
self.checker_command = value
954
self.PropertyChanged(dbus.String(u"checker"),
955
dbus.String(self.checker_command,
958
# checker_running - property
959
@dbus_service_property(_interface, signature=u"b",
961
def checker_running_dbus_property(self, value=None):
962
if value is None: # get
963
return dbus.Boolean(self.checker is not None)
969
# object_path - property
970
@dbus_service_property(_interface, signature=u"o", access=u"read")
971
def object_path_dbus_property(self):
972
return self.dbus_object_path # is already a dbus.ObjectPath
975
@dbus_service_property(_interface, signature=u"ay",
976
access=u"write", byte_arrays=True)
977
def secret_dbus_property(self, value):
978
self.secret = str(value)
585
StopChecker = dbus.service.method(_interface)(stop_checker)
586
StopChecker.__name__ = "StopChecker"
983
class ClientHandler(socketserver.BaseRequestHandler, object):
984
"""A class to handle client connections.
986
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.
987
655
Note: This will run in its own forked process."""
989
657
def handle(self):
990
658
logger.info(u"TCP connection from: %s",
991
659
unicode(self.client_address))
992
logger.debug(u"IPC Pipe FD: %d", self.server.child_pipe[1])
993
# Open IPC pipe to parent process
994
with contextlib.nested(os.fdopen(self.server.child_pipe[1],
996
os.fdopen(self.server.parent_pipe[0],
999
session = (gnutls.connection
1000
.ClientSession(self.request,
1002
.X509Credentials()))
1004
line = self.request.makefile().readline()
1005
logger.debug(u"Protocol version: %r", line)
1007
if int(line.strip().split()[0]) > 1:
1009
except (ValueError, IndexError, RuntimeError), error:
1010
logger.error(u"Unknown protocol version: %s", error)
1013
# Note: gnutls.connection.X509Credentials is really a
1014
# generic GnuTLS certificate credentials object so long as
1015
# no X.509 keys are added to it. Therefore, we can use it
1016
# here despite using OpenPGP certificates.
1018
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1019
# u"+AES-256-CBC", u"+SHA1",
1020
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1022
# Use a fallback default, since this MUST be set.
1023
priority = self.server.gnutls_priority
1024
if priority is None:
1025
priority = u"NORMAL"
1026
(gnutls.library.functions
1027
.gnutls_priority_set_direct(session._c_object,
1032
except gnutls.errors.GNUTLSError, error:
1033
logger.warning(u"Handshake failed: %s", error)
1034
# Do not run session.bye() here: the session is not
1035
# established. Just abandon the request.
1037
logger.debug(u"Handshake succeeded")
1040
fpr = self.fingerprint(self.peer_certificate
1042
except (TypeError, gnutls.errors.GNUTLSError), error:
1043
logger.warning(u"Bad certificate: %s", error)
1045
logger.debug(u"Fingerprint: %s", fpr)
1047
for c in self.server.clients:
1048
if c.fingerprint == fpr:
1052
ipc.write(u"NOTFOUND %s %s\n"
1053
% (fpr, unicode(self.client_address)))
1055
# Have to check if client.enabled, since it is
1056
# possible that the client was disabled since the
1057
# GnuTLS session was established.
1058
ipc.write(u"GETATTR enabled %s\n" % fpr)
1059
enabled = pickle.load(ipc_return)
1061
ipc.write(u"DISABLED %s\n" % client.name)
1063
ipc.write(u"SENDING %s\n" % client.name)
1065
while sent_size < len(client.secret):
1066
sent = session.send(client.secret[sent_size:])
1067
logger.debug(u"Sent: %d, remaining: %d",
1068
sent, len(client.secret)
1069
- (sent_size + sent))
1075
def peer_certificate(session):
1076
"Return the peer's OpenPGP certificate as a bytestring"
1077
# If not an OpenPGP certificate...
1078
if (gnutls.library.functions
1079
.gnutls_certificate_type_get(session._c_object)
1080
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1081
# ...do the normal thing
1082
return session.peer_certificate
1083
list_size = ctypes.c_uint(1)
1084
cert_list = (gnutls.library.functions
1085
.gnutls_certificate_get_peers
1086
(session._c_object, ctypes.byref(list_size)))
1087
if not bool(cert_list) and list_size.value != 0:
1088
raise gnutls.errors.GNUTLSError(u"error getting peer"
1090
if list_size.value == 0:
1093
return ctypes.string_at(cert.data, cert.size)
1096
def fingerprint(openpgp):
1097
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1098
# New GnuTLS "datum" with the OpenPGP public key
1099
datum = (gnutls.library.types
1100
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1103
ctypes.c_uint(len(openpgp))))
1104
# New empty GnuTLS certificate
1105
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1106
(gnutls.library.functions
1107
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1108
# Import the OpenPGP public key into the certificate
1109
(gnutls.library.functions
1110
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1111
gnutls.library.constants
1112
.GNUTLS_OPENPGP_FMT_RAW))
1113
# Verify the self signature in the key
1114
crtverify = ctypes.c_uint()
1115
(gnutls.library.functions
1116
.gnutls_openpgp_crt_verify_self(crt, 0,
1117
ctypes.byref(crtverify)))
1118
if crtverify.value != 0:
1119
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1120
raise (gnutls.errors.CertificateSecurityError
1122
# New buffer for the fingerprint
1123
buf = ctypes.create_string_buffer(20)
1124
buf_len = ctypes.c_size_t()
1125
# Get the fingerprint from the certificate into the buffer
1126
(gnutls.library.functions
1127
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1128
ctypes.byref(buf_len)))
1129
# Deinit the certificate
1130
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1131
# Convert the buffer to a Python bytestring
1132
fpr = ctypes.string_at(buf, buf_len.value)
1133
# Convert the bytestring to hexadecimal notation
1134
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1138
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
1139
"""Like socketserver.ForkingMixIn, but also pass a pipe pair."""
1140
def process_request(self, request, client_address):
1141
"""Overrides and wraps the original process_request().
1143
This function creates a new pipe in self.pipe
1145
self.child_pipe = os.pipe() # Child writes here
1146
self.parent_pipe = os.pipe() # Parent writes here
1147
super(ForkingMixInWithPipes,
1148
self).process_request(request, client_address)
1149
# Close unused ends for parent
1150
os.close(self.parent_pipe[0]) # close read end
1151
os.close(self.child_pipe[1]) # close write end
1152
self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
1153
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1154
"""Dummy function; override as necessary"""
1155
os.close(child_pipe_fd)
1156
os.close(parent_pipe_fd)
1159
class IPv6_TCPServer(ForkingMixInWithPipes,
1160
socketserver.TCPServer, object):
1161
"""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
1164
739
enabled: Boolean; whether this server is activated yet
1165
interface: None or a network interface name (string)
1166
use_ipv6: Boolean; to use IPv6 or not
1168
def __init__(self, server_address, RequestHandlerClass,
1169
interface=None, use_ipv6=True):
1170
self.interface = interface
1172
self.address_family = socket.AF_INET6
1173
socketserver.TCPServer.__init__(self, server_address,
1174
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)
1175
751
def server_bind(self):
1176
752
"""This overrides the normal server_bind() function
1177
753
to bind to an interface if one was specified, and also NOT to
1178
754
bind to an address or port if they were not specified."""
1179
if self.interface is not None:
1180
if SO_BINDTODEVICE is None:
1181
logger.error(u"SO_BINDTODEVICE does not exist;"
1182
u" cannot bind to interface %s",
1186
self.socket.setsockopt(socket.SOL_SOCKET,
1190
except socket.error, error:
1191
if error[0] == errno.EPERM:
1192
logger.error(u"No permission to"
1193
u" bind to interface %s",
1195
elif error[0] == errno.ENOPROTOOPT:
1196
logger.error(u"SO_BINDTODEVICE not available;"
1197
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"])
1201
769
# Only bind(2) the socket if we really need to.
1202
770
if self.server_address[0] or self.server_address[1]:
1203
771
if not self.server_address[0]:
1204
if self.address_family == socket.AF_INET6:
1205
any_address = u"::" # in6addr_any
1207
any_address = socket.INADDR_ANY
1208
self.server_address = (any_address,
773
self.server_address = (in6addr_any,
1209
774
self.server_address[1])
1210
775
elif not self.server_address[1]:
1211
776
self.server_address = (self.server_address[0],
1213
# if self.interface:
778
# if self.settings["interface"]:
1214
779
# self.server_address = (self.server_address[0],
1217
782
# if_nametoindex
1219
return socketserver.TCPServer.server_bind(self)
1222
class MandosServer(IPv6_TCPServer):
1226
clients: set of Client objects
1227
gnutls_priority GnuTLS priority string
1228
use_dbus: Boolean; to emit D-Bus signals or not
1230
Assumes a gobject.MainLoop event loop.
1232
def __init__(self, server_address, RequestHandlerClass,
1233
interface=None, use_ipv6=True, clients=None,
1234
gnutls_priority=None, use_dbus=True):
1235
self.enabled = False
1236
self.clients = clients
1237
if self.clients is None:
1238
self.clients = set()
1239
self.use_dbus = use_dbus
1240
self.gnutls_priority = gnutls_priority
1241
IPv6_TCPServer.__init__(self, server_address,
1242
RequestHandlerClass,
1243
interface = interface,
1244
use_ipv6 = use_ipv6)
785
return super(IPv6_TCPServer, self).server_bind()
1245
786
def server_activate(self):
1246
787
if self.enabled:
1247
return socketserver.TCPServer.server_activate(self)
788
return super(IPv6_TCPServer, self).server_activate()
1248
789
def enable(self):
1249
790
self.enabled = True
1250
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1251
# Call "handle_ipc" for both data and EOF events
1252
gobject.io_add_watch(child_pipe_fd,
1253
gobject.IO_IN | gobject.IO_HUP,
1254
functools.partial(self.handle_ipc,
1257
def handle_ipc(self, source, condition, reply_fd=None,
1260
gobject.IO_IN: u"IN", # There is data to read.
1261
gobject.IO_OUT: u"OUT", # Data can be written (without
1263
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1264
gobject.IO_ERR: u"ERR", # Error condition.
1265
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1266
# broken, usually for pipes and
1269
conditions_string = ' | '.join(name
1271
condition_names.iteritems()
1272
if cond & condition)
1273
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1276
# Turn the pipe file descriptors into Python file objects
1277
if source not in file_objects:
1278
file_objects[source] = os.fdopen(source, u"r", 1)
1279
if reply_fd not in file_objects:
1280
file_objects[reply_fd] = os.fdopen(reply_fd, u"w", 0)
1282
# Read a line from the file object
1283
cmdline = file_objects[source].readline()
1284
if not cmdline: # Empty line means end of file
1285
# close the IPC pipes
1286
file_objects[source].close()
1287
del file_objects[source]
1288
file_objects[reply_fd].close()
1289
del file_objects[reply_fd]
1291
# Stop calling this function
1294
logger.debug(u"IPC command: %r", cmdline)
1296
# Parse and act on command
1297
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1299
if cmd == u"NOTFOUND":
1300
fpr, address = args.split(None, 1)
1301
logger.warning(u"Client not found for fingerprint: %s, ad"
1302
u"dress: %s", fpr, address)
1305
mandos_dbus_service.ClientNotFound(fpr, address)
1306
elif cmd == u"DISABLED":
1307
for client in self.clients:
1308
if client.name == args:
1309
logger.warning(u"Client %s is disabled", args)
1315
logger.error(u"Unknown client %s is disabled", args)
1316
elif cmd == u"SENDING":
1317
for client in self.clients:
1318
if client.name == args:
1319
logger.info(u"Sending secret to %s", client.name)
1326
logger.error(u"Sending secret to unknown client %s",
1328
elif cmd == u"GETATTR":
1329
attr_name, fpr = args.split(None, 1)
1330
for client in self.clients:
1331
if client.fingerprint == fpr:
1332
attr_value = getattr(client, attr_name, None)
1333
logger.debug("IPC reply: %r", attr_value)
1334
pickle.dump(attr_value, file_objects[reply_fd])
1337
logger.error(u"Client %s on address %s requesting "
1338
u"attribute %s not found", fpr, address,
1340
pickle.dump(None, file_objects[reply_fd])
1342
logger.error(u"Unknown IPC command: %r", cmdline)
1344
# Keep calling this function
1348
793
def string_to_delta(interval):
1349
794
"""Parse a string and return a datetime.timedelta
1351
>>> string_to_delta(u'7d')
796
>>> string_to_delta('7d')
1352
797
datetime.timedelta(7)
1353
>>> string_to_delta(u'60s')
798
>>> string_to_delta('60s')
1354
799
datetime.timedelta(0, 60)
1355
>>> string_to_delta(u'60m')
800
>>> string_to_delta('60m')
1356
801
datetime.timedelta(0, 3600)
1357
>>> string_to_delta(u'24h')
802
>>> string_to_delta('24h')
1358
803
datetime.timedelta(1)
1359
804
>>> string_to_delta(u'1w')
1360
805
datetime.timedelta(7)
1361
>>> string_to_delta(u'5m 30s')
806
>>> string_to_delta('5m 30s')
1362
807
datetime.timedelta(0, 330)
1364
809
timevalue = datetime.timedelta(0)
1473
933
# Default values for config file for server-global settings
1474
server_defaults = { u"interface": u"",
1479
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1480
u"servicename": u"Mandos",
1481
u"use_dbus": u"True",
1482
u"use_ipv6": u"True",
934
server_defaults = { "interface": "",
939
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
940
"servicename": "Mandos",
1485
944
# Parse config file for server-global settings
1486
server_config = configparser.SafeConfigParser(server_defaults)
945
server_config = ConfigParser.SafeConfigParser(server_defaults)
1487
946
del server_defaults
1488
server_config.read(os.path.join(options.configdir,
947
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1490
948
# Convert the SafeConfigParser object to a dict
1491
949
server_settings = server_config.defaults()
1492
950
# Use the appropriate methods on the non-string config options
1493
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1494
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",
1496
955
if server_settings["port"]:
1497
server_settings["port"] = server_config.getint(u"DEFAULT",
956
server_settings["port"] = server_config.getint("DEFAULT",
1499
958
del server_config
1501
960
# Override the settings from the config file with command line
1502
961
# options, if set.
1503
for option in (u"interface", u"address", u"port", u"debug",
1504
u"priority", u"servicename", u"configdir",
1505
u"use_dbus", u"use_ipv6"):
962
for option in ("interface", "address", "port", "debug",
963
"priority", "servicename", "configdir",
1506
965
value = getattr(options, option)
1507
966
if value is not None:
1508
967
server_settings[option] = value
1510
# Force all strings to be unicode
1511
for option in server_settings.keys():
1512
if type(server_settings[option]) is str:
1513
server_settings[option] = unicode(server_settings[option])
1514
969
# Now we have our good server settings in "server_settings"
1516
##################################################################
1518
971
# For convenience
1519
debug = server_settings[u"debug"]
1520
use_dbus = server_settings[u"use_dbus"]
1521
use_ipv6 = server_settings[u"use_ipv6"]
972
debug = server_settings["debug"]
973
use_dbus = server_settings["use_dbus"]
1524
976
syslogger.setLevel(logging.WARNING)
1525
977
console.setLevel(logging.WARNING)
1527
if server_settings[u"servicename"] != u"Mandos":
979
if server_settings["servicename"] != "Mandos":
1528
980
syslogger.setFormatter(logging.Formatter
1529
(u'Mandos (%s) [%%(process)d]:'
1530
u' %%(levelname)s: %%(message)s'
1531
% server_settings[u"servicename"]))
981
('Mandos (%s): %%(levelname)s:'
983
% server_settings["servicename"]))
1533
985
# Parse config file with clients
1534
client_defaults = { u"timeout": u"1h",
1536
u"checker": u"fping -q -- %%(host)s",
986
client_defaults = { "timeout": "1h",
988
"checker": "fping -q -- %%(host)s",
1539
client_config = configparser.SafeConfigParser(client_defaults)
1540
client_config.read(os.path.join(server_settings[u"configdir"],
1543
global mandos_dbus_service
1544
mandos_dbus_service = None
1546
tcp_server = MandosServer((server_settings[u"address"],
1547
server_settings[u"port"]),
1549
interface=server_settings[u"interface"],
1552
server_settings[u"priority"],
1554
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"
1556
pidfile = open(pidfilename, u"w")
1003
pidfile = open(pidfilename, "w")
1557
1004
except IOError:
1558
logger.error(u"Could not open file %r", pidfilename)
1005
logger.error("Could not open file %r", pidfilename)
1561
uid = pwd.getpwnam(u"_mandos").pw_uid
1562
gid = pwd.getpwnam(u"_mandos").pw_gid
1008
uid = pwd.getpwnam("_mandos").pw_uid
1009
gid = pwd.getpwnam("_mandos").pw_gid
1563
1010
except KeyError:
1565
uid = pwd.getpwnam(u"mandos").pw_uid
1566
gid = pwd.getpwnam(u"mandos").pw_gid
1012
uid = pwd.getpwnam("mandos").pw_uid
1013
gid = pwd.getpwnam("mandos").pw_gid
1567
1014
except KeyError:
1569
uid = pwd.getpwnam(u"nobody").pw_uid
1570
gid = pwd.getpwnam(u"nobody").pw_gid
1016
uid = pwd.getpwnam("nobody").pw_uid
1017
gid = pwd.getpwnam("nogroup").pw_gid
1571
1018
except KeyError: