463
421
if self.checker_callback_tag:
464
422
gobject.source_remove(self.checker_callback_tag)
465
423
self.checker_callback_tag = None
466
if getattr(self, u"checker", None) is None:
424
if getattr(self, "checker", None) is None:
468
426
logger.debug(u"Stopping checker for %(name)s", vars(self))
470
428
os.kill(self.checker.pid, signal.SIGTERM)
472
430
#if self.checker.poll() is None:
473
431
# os.kill(self.checker.pid, signal.SIGKILL)
474
432
except OSError, error:
475
433
if error.errno != errno.ESRCH: # No such process
477
435
self.checker = None
437
self.PropertyChanged(dbus.String(u"checker_running"),
438
dbus.Boolean(False, variant_level=1))
479
440
def still_valid(self):
480
441
"""Has the timeout not yet passed for this client?"""
481
if not getattr(self, u"enabled", False):
442
if not getattr(self, "enabled", False):
483
444
now = datetime.datetime.utcnow()
484
445
if self.last_checked_ok is None:
485
446
return now < (self.created + self.timeout)
487
448
return now < (self.last_checked_ok + self.timeout)
490
def dbus_service_property(dbus_interface, signature=u"v",
491
access=u"readwrite", byte_arrays=False):
492
"""Decorators for marking methods of a DBusObjectWithProperties to
493
become properties on the D-Bus.
495
The decorated method will be called with no arguments by "Get"
496
and with one argument by "Set".
498
The parameters, where they are supported, are the same as
499
dbus.service.method, except there is only "signature", since the
500
type from Get() and the type sent to Set() is the same.
503
func._dbus_is_property = True
504
func._dbus_interface = dbus_interface
505
func._dbus_signature = signature
506
func._dbus_access = access
507
func._dbus_name = func.__name__
508
if func._dbus_name.endswith(u"_dbus_property"):
509
func._dbus_name = func._dbus_name[:-14]
510
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
515
class DBusPropertyException(dbus.exceptions.DBusException):
516
"""A base class for D-Bus property-related exceptions
518
def __unicode__(self):
519
return unicode(str(self))
522
class DBusPropertyAccessException(DBusPropertyException):
523
"""A property's access permissions disallows an operation.
528
class DBusPropertyNotFound(DBusPropertyException):
529
"""An attempt was made to access a non-existing property.
534
class DBusObjectWithProperties(dbus.service.Object):
535
"""A D-Bus object with properties.
537
Classes inheriting from this can use the dbus_service_property
538
decorator to expose methods as D-Bus properties. It exposes the
539
standard Get(), Set(), and GetAll() methods on the D-Bus.
543
def _is_dbus_property(obj):
544
return getattr(obj, u"_dbus_is_property", False)
546
def _get_all_dbus_properties(self):
547
"""Returns a generator of (name, attribute) pairs
549
return ((prop._dbus_name, prop)
551
inspect.getmembers(self, self._is_dbus_property))
553
def _get_dbus_property(self, interface_name, property_name):
554
"""Returns a bound method if one exists which is a D-Bus
555
property with the specified name and interface.
557
for name in (property_name,
558
property_name + u"_dbus_property"):
559
prop = getattr(self, name, None)
561
or not self._is_dbus_property(prop)
562
or prop._dbus_name != property_name
563
or (interface_name and prop._dbus_interface
564
and interface_name != prop._dbus_interface)):
568
raise DBusPropertyNotFound(self.dbus_object_path + u":"
569
+ interface_name + u"."
572
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
574
def Get(self, interface_name, property_name):
575
"""Standard D-Bus property Get() method, see D-Bus standard.
577
prop = self._get_dbus_property(interface_name, property_name)
578
if prop._dbus_access == u"write":
579
raise DBusPropertyAccessException(property_name)
581
if not hasattr(value, u"variant_level"):
583
return type(value)(value, variant_level=value.variant_level+1)
585
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
586
def Set(self, interface_name, property_name, value):
587
"""Standard D-Bus property Set() method, see D-Bus standard.
589
prop = self._get_dbus_property(interface_name, property_name)
590
if prop._dbus_access == u"read":
591
raise DBusPropertyAccessException(property_name)
592
if prop._dbus_get_args_options[u"byte_arrays"]:
593
value = dbus.ByteArray(''.join(unichr(byte)
597
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
598
out_signature=u"a{sv}")
599
def GetAll(self, interface_name):
600
"""Standard D-Bus property GetAll() method, see D-Bus
603
Note: Will not include properties with access="write".
606
for name, prop in self._get_all_dbus_properties():
608
and interface_name != prop._dbus_interface):
609
# Interface non-empty but did not match
611
# Ignore write-only properties
612
if prop._dbus_access == u"write":
615
if not hasattr(value, u"variant_level"):
618
all[name] = type(value)(value, variant_level=
619
value.variant_level+1)
620
return dbus.Dictionary(all, signature=u"sv")
622
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
624
path_keyword='object_path',
625
connection_keyword='connection')
626
def Introspect(self, object_path, connection):
627
"""Standard D-Bus method, overloaded to insert property tags.
629
xmlstring = dbus.service.Object.Introspect(self, object_path,
632
document = xml.dom.minidom.parseString(xmlstring)
633
def make_tag(document, name, prop):
634
e = document.createElement(u"property")
635
e.setAttribute(u"name", name)
636
e.setAttribute(u"type", prop._dbus_signature)
637
e.setAttribute(u"access", prop._dbus_access)
639
for if_tag in document.getElementsByTagName(u"interface"):
640
for tag in (make_tag(document, name, prop)
642
in self._get_all_dbus_properties()
643
if prop._dbus_interface
644
== if_tag.getAttribute(u"name")):
645
if_tag.appendChild(tag)
646
# Add the names to the return values for the
647
# "org.freedesktop.DBus.Properties" methods
648
if (if_tag.getAttribute(u"name")
649
== u"org.freedesktop.DBus.Properties"):
650
for cn in if_tag.getElementsByTagName(u"method"):
651
if cn.getAttribute(u"name") == u"Get":
652
for arg in cn.getElementsByTagName(u"arg"):
653
if (arg.getAttribute(u"direction")
655
arg.setAttribute(u"name", u"value")
656
elif cn.getAttribute(u"name") == u"GetAll":
657
for arg in cn.getElementsByTagName(u"arg"):
658
if (arg.getAttribute(u"direction")
660
arg.setAttribute(u"name", u"props")
661
xmlstring = document.toxml(u"utf-8")
663
except (AttributeError, xml.dom.DOMException,
664
xml.parsers.expat.ExpatError), error:
665
logger.error(u"Failed to override Introspection method",
670
class ClientDBus(Client, DBusObjectWithProperties):
671
"""A Client class using D-Bus
674
dbus_object_path: dbus.ObjectPath
675
bus: dbus.SystemBus()
677
# dbus.service.Object doesn't use super(), so we can't either.
679
def __init__(self, bus = None, *args, **kwargs):
681
Client.__init__(self, *args, **kwargs)
682
# Only now, when this client is initialized, can it show up on
684
self.dbus_object_path = (dbus.ObjectPath
686
+ self.name.replace(u".", u"_")))
687
DBusObjectWithProperties.__init__(self, self.bus,
688
self.dbus_object_path)
691
def _datetime_to_dbus(dt, variant_level=0):
692
"""Convert a UTC datetime.datetime() to a D-Bus type."""
693
return dbus.String(dt.isoformat(),
694
variant_level=variant_level)
697
oldstate = getattr(self, u"enabled", False)
698
r = Client.enable(self)
699
if oldstate != self.enabled:
701
self.PropertyChanged(dbus.String(u"enabled"),
702
dbus.Boolean(True, variant_level=1))
703
self.PropertyChanged(
704
dbus.String(u"last_enabled"),
705
self._datetime_to_dbus(self.last_enabled,
709
def disable(self, signal = True):
710
oldstate = getattr(self, u"enabled", False)
711
r = Client.disable(self, log=signal)
712
if signal and oldstate != self.enabled:
714
self.PropertyChanged(dbus.String(u"enabled"),
715
dbus.Boolean(False, variant_level=1))
718
def __del__(self, *args, **kwargs):
720
self.remove_from_connection()
723
if hasattr(DBusObjectWithProperties, u"__del__"):
724
DBusObjectWithProperties.__del__(self, *args, **kwargs)
725
Client.__del__(self, *args, **kwargs)
727
def checker_callback(self, pid, condition, command,
729
self.checker_callback_tag = None
732
self.PropertyChanged(dbus.String(u"checker_running"),
733
dbus.Boolean(False, variant_level=1))
734
if os.WIFEXITED(condition):
735
exitstatus = os.WEXITSTATUS(condition)
737
self.CheckerCompleted(dbus.Int16(exitstatus),
738
dbus.Int64(condition),
739
dbus.String(command))
742
self.CheckerCompleted(dbus.Int16(-1),
743
dbus.Int64(condition),
744
dbus.String(command))
746
return Client.checker_callback(self, pid, condition, command,
749
def checked_ok(self, *args, **kwargs):
750
r = Client.checked_ok(self, *args, **kwargs)
752
self.PropertyChanged(
753
dbus.String(u"last_checked_ok"),
754
(self._datetime_to_dbus(self.last_checked_ok,
758
def start_checker(self, *args, **kwargs):
759
old_checker = self.checker
760
if self.checker is not None:
761
old_checker_pid = self.checker.pid
763
old_checker_pid = None
764
r = Client.start_checker(self, *args, **kwargs)
765
# Only if new checker process was started
766
if (self.checker is not None
767
and old_checker_pid != self.checker.pid):
769
self.CheckerStarted(self.current_checker_command)
770
self.PropertyChanged(
771
dbus.String(u"checker_running"),
772
dbus.Boolean(True, variant_level=1))
775
def stop_checker(self, *args, **kwargs):
776
old_checker = getattr(self, u"checker", None)
777
r = Client.stop_checker(self, *args, **kwargs)
778
if (old_checker is not None
779
and getattr(self, u"checker", None) is None):
780
self.PropertyChanged(dbus.String(u"checker_running"),
781
dbus.Boolean(False, variant_level=1))
784
450
## D-Bus methods & signals
785
_interface = u"se.bsnet.fukt.Mandos.Client"
451
_interface = u"org.mandos_system.Mandos.Client"
788
@dbus.service.method(_interface)
790
return self.checked_ok()
453
# BumpTimeout - method
454
BumpTimeout = dbus.service.method(_interface)(bump_timeout)
455
BumpTimeout.__name__ = "BumpTimeout"
792
457
# CheckerCompleted - signal
793
@dbus.service.signal(_interface, signature=u"nxs")
794
def CheckerCompleted(self, exitcode, waitstatus, command):
458
@dbus.service.signal(_interface, signature="bqs")
459
def CheckerCompleted(self, success, condition, command):
798
463
# CheckerStarted - signal
799
@dbus.service.signal(_interface, signature=u"s")
464
@dbus.service.signal(_interface, signature="s")
800
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"
804
513
# PropertyChanged - signal
805
@dbus.service.signal(_interface, signature=u"sv")
514
@dbus.service.signal(_interface, signature="sv")
806
515
def PropertyChanged(self, property, value):
811
@dbus.service.signal(_interface)
817
@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(),
822
563
# Enable - method
823
@dbus.service.method(_interface)
564
Enable = dbus.service.method(_interface)(enable)
565
Enable.__name__ = "Enable"
828
567
# StartChecker - method
829
568
@dbus.service.method(_interface)
840
579
# StopChecker - method
841
@dbus.service.method(_interface)
842
def StopChecker(self):
846
@dbus_service_property(_interface, signature=u"s", access=u"read")
847
def name_dbus_property(self):
848
return dbus.String(self.name)
850
# fingerprint - property
851
@dbus_service_property(_interface, signature=u"s", access=u"read")
852
def fingerprint_dbus_property(self):
853
return dbus.String(self.fingerprint)
856
@dbus_service_property(_interface, signature=u"s",
858
def host_dbus_property(self, value=None):
859
if value is None: # get
860
return dbus.String(self.host)
863
self.PropertyChanged(dbus.String(u"host"),
864
dbus.String(value, variant_level=1))
867
@dbus_service_property(_interface, signature=u"s", access=u"read")
868
def created_dbus_property(self):
869
return dbus.String(self._datetime_to_dbus(self.created))
871
# last_enabled - property
872
@dbus_service_property(_interface, signature=u"s", access=u"read")
873
def last_enabled_dbus_property(self):
874
if self.last_enabled is None:
875
return dbus.String(u"")
876
return dbus.String(self._datetime_to_dbus(self.last_enabled))
879
@dbus_service_property(_interface, signature=u"b",
881
def enabled_dbus_property(self, value=None):
882
if value is None: # get
883
return dbus.Boolean(self.enabled)
889
# last_checked_ok - property
890
@dbus_service_property(_interface, signature=u"s",
892
def last_checked_ok_dbus_property(self, value=None):
893
if value is not None:
896
if self.last_checked_ok is None:
897
return dbus.String(u"")
898
return dbus.String(self._datetime_to_dbus(self
902
@dbus_service_property(_interface, signature=u"t",
904
def timeout_dbus_property(self, value=None):
905
if value is None: # get
906
return dbus.UInt64(self.timeout_milliseconds())
907
self.timeout = datetime.timedelta(0, 0, 0, value)
909
self.PropertyChanged(dbus.String(u"timeout"),
910
dbus.UInt64(value, variant_level=1))
911
if getattr(self, u"disable_initiator_tag", None) is None:
914
gobject.source_remove(self.disable_initiator_tag)
915
self.disable_initiator_tag = None
917
_timedelta_to_milliseconds((self
923
# The timeout has passed
926
self.disable_initiator_tag = (gobject.timeout_add
927
(time_to_die, self.disable))
929
# interval - property
930
@dbus_service_property(_interface, signature=u"t",
932
def interval_dbus_property(self, value=None):
933
if value is None: # get
934
return dbus.UInt64(self.interval_milliseconds())
935
self.interval = datetime.timedelta(0, 0, 0, value)
937
self.PropertyChanged(dbus.String(u"interval"),
938
dbus.UInt64(value, variant_level=1))
939
if getattr(self, u"checker_initiator_tag", None) is None:
941
# Reschedule checker run
942
gobject.source_remove(self.checker_initiator_tag)
943
self.checker_initiator_tag = (gobject.timeout_add
944
(value, self.start_checker))
945
self.start_checker() # Start one now, too
948
@dbus_service_property(_interface, signature=u"s",
950
def checker_dbus_property(self, value=None):
951
if value is None: # get
952
return dbus.String(self.checker_command)
953
self.checker_command = value
955
self.PropertyChanged(dbus.String(u"checker"),
956
dbus.String(self.checker_command,
959
# checker_running - property
960
@dbus_service_property(_interface, signature=u"b",
962
def checker_running_dbus_property(self, value=None):
963
if value is None: # get
964
return dbus.Boolean(self.checker is not None)
970
# object_path - property
971
@dbus_service_property(_interface, signature=u"o", access=u"read")
972
def object_path_dbus_property(self):
973
return self.dbus_object_path # is already a dbus.ObjectPath
976
@dbus_service_property(_interface, signature=u"ay",
977
access=u"write", byte_arrays=True)
978
def secret_dbus_property(self, value):
979
self.secret = str(value)
580
StopChecker = dbus.service.method(_interface)(stop_checker)
581
StopChecker.__name__ = "StopChecker"
984
class ClientHandler(socketserver.BaseRequestHandler, object):
985
"""A class to handle client connections.
987
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.
988
647
Note: This will run in its own forked process."""
990
649
def handle(self):
991
650
logger.info(u"TCP connection from: %s",
992
651
unicode(self.client_address))
993
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
994
# Open IPC pipe to parent process
995
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
996
session = (gnutls.connection
997
.ClientSession(self.request,
1001
line = self.request.makefile().readline()
1002
logger.debug(u"Protocol version: %r", line)
1004
if int(line.strip().split()[0]) > 1:
1006
except (ValueError, IndexError, RuntimeError), error:
1007
logger.error(u"Unknown protocol version: %s", error)
1010
# Note: gnutls.connection.X509Credentials is really a
1011
# generic GnuTLS certificate credentials object so long as
1012
# no X.509 keys are added to it. Therefore, we can use it
1013
# here despite using OpenPGP certificates.
1015
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1016
# u"+AES-256-CBC", u"+SHA1",
1017
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1019
# Use a fallback default, since this MUST be set.
1020
priority = self.server.gnutls_priority
1021
if priority is None:
1022
priority = u"NORMAL"
1023
(gnutls.library.functions
1024
.gnutls_priority_set_direct(session._c_object,
1029
except gnutls.errors.GNUTLSError, error:
1030
logger.warning(u"Handshake failed: %s", error)
1031
# Do not run session.bye() here: the session is not
1032
# established. Just abandon the request.
1034
logger.debug(u"Handshake succeeded")
1036
fpr = self.fingerprint(self.peer_certificate(session))
1037
except (TypeError, gnutls.errors.GNUTLSError), error:
1038
logger.warning(u"Bad certificate: %s", error)
1041
logger.debug(u"Fingerprint: %s", fpr)
1043
for c in self.server.clients:
1044
if c.fingerprint == fpr:
1048
ipc.write(u"NOTFOUND %s %s\n"
1049
% (fpr, unicode(self.client_address)))
1052
# Have to check if client.still_valid(), since it is
1053
# possible that the client timed out while establishing
1054
# the GnuTLS session.
1055
if not client.still_valid():
1056
ipc.write(u"INVALID %s\n" % client.name)
1059
ipc.write(u"SENDING %s\n" % client.name)
1061
while sent_size < len(client.secret):
1062
sent = session.send(client.secret[sent_size:])
1063
logger.debug(u"Sent: %d, remaining: %d",
1064
sent, len(client.secret)
1065
- (sent_size + sent))
1070
def peer_certificate(session):
1071
"Return the peer's OpenPGP certificate as a bytestring"
1072
# If not an OpenPGP certificate...
1073
if (gnutls.library.functions
1074
.gnutls_certificate_type_get(session._c_object)
1075
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1076
# ...do the normal thing
1077
return session.peer_certificate
1078
list_size = ctypes.c_uint(1)
1079
cert_list = (gnutls.library.functions
1080
.gnutls_certificate_get_peers
1081
(session._c_object, ctypes.byref(list_size)))
1082
if not bool(cert_list) and list_size.value != 0:
1083
raise gnutls.errors.GNUTLSError(u"error getting peer"
1085
if list_size.value == 0:
1088
return ctypes.string_at(cert.data, cert.size)
1091
def fingerprint(openpgp):
1092
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1093
# New GnuTLS "datum" with the OpenPGP public key
1094
datum = (gnutls.library.types
1095
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1098
ctypes.c_uint(len(openpgp))))
1099
# New empty GnuTLS certificate
1100
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1101
(gnutls.library.functions
1102
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1103
# Import the OpenPGP public key into the certificate
1104
(gnutls.library.functions
1105
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1106
gnutls.library.constants
1107
.GNUTLS_OPENPGP_FMT_RAW))
1108
# Verify the self signature in the key
1109
crtverify = ctypes.c_uint()
1110
(gnutls.library.functions
1111
.gnutls_openpgp_crt_verify_self(crt, 0,
1112
ctypes.byref(crtverify)))
1113
if crtverify.value != 0:
1114
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1115
raise (gnutls.errors.CertificateSecurityError
1117
# New buffer for the fingerprint
1118
buf = ctypes.create_string_buffer(20)
1119
buf_len = ctypes.c_size_t()
1120
# Get the fingerprint from the certificate into the buffer
1121
(gnutls.library.functions
1122
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1123
ctypes.byref(buf_len)))
1124
# Deinit the certificate
1125
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1126
# Convert the buffer to a Python bytestring
1127
fpr = ctypes.string_at(buf, buf_len.value)
1128
# Convert the bytestring to hexadecimal notation
1129
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1133
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1134
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1135
def process_request(self, request, client_address):
1136
"""Overrides and wraps the original process_request().
1138
This function creates a new pipe in self.pipe
1140
self.pipe = os.pipe()
1141
super(ForkingMixInWithPipe,
1142
self).process_request(request, client_address)
1143
os.close(self.pipe[1]) # close write end
1144
self.add_pipe(self.pipe[0])
1145
def add_pipe(self, pipe):
1146
"""Dummy function; override as necessary"""
1150
class IPv6_TCPServer(ForkingMixInWithPipe,
1151
socketserver.TCPServer, object):
1152
"""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
1155
729
enabled: Boolean; whether this server is activated yet
1156
interface: None or a network interface name (string)
1157
use_ipv6: Boolean; to use IPv6 or not
1159
def __init__(self, server_address, RequestHandlerClass,
1160
interface=None, use_ipv6=True):
1161
self.interface = interface
1163
self.address_family = socket.AF_INET6
1164
socketserver.TCPServer.__init__(self, server_address,
1165
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)
1166
741
def server_bind(self):
1167
742
"""This overrides the normal server_bind() function
1168
743
to bind to an interface if one was specified, and also NOT to
1169
744
bind to an address or port if they were not specified."""
1170
if self.interface is not None:
1171
if SO_BINDTODEVICE is None:
1172
logger.error(u"SO_BINDTODEVICE does not exist;"
1173
u" cannot bind to interface %s",
1177
self.socket.setsockopt(socket.SOL_SOCKET,
1181
except socket.error, error:
1182
if error[0] == errno.EPERM:
1183
logger.error(u"No permission to"
1184
u" bind to interface %s",
1186
elif error[0] == errno.ENOPROTOOPT:
1187
logger.error(u"SO_BINDTODEVICE not available;"
1188
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"])
1192
759
# Only bind(2) the socket if we really need to.
1193
760
if self.server_address[0] or self.server_address[1]:
1194
761
if not self.server_address[0]:
1195
if self.address_family == socket.AF_INET6:
1196
any_address = u"::" # in6addr_any
1198
any_address = socket.INADDR_ANY
1199
self.server_address = (any_address,
763
self.server_address = (in6addr_any,
1200
764
self.server_address[1])
1201
765
elif not self.server_address[1]:
1202
766
self.server_address = (self.server_address[0],
1204
# if self.interface:
768
# if self.settings["interface"]:
1205
769
# self.server_address = (self.server_address[0],
1208
772
# if_nametoindex
1210
return socketserver.TCPServer.server_bind(self)
1213
class MandosServer(IPv6_TCPServer):
1217
clients: set of Client objects
1218
gnutls_priority GnuTLS priority string
1219
use_dbus: Boolean; to emit D-Bus signals or not
1221
Assumes a gobject.MainLoop event loop.
1223
def __init__(self, server_address, RequestHandlerClass,
1224
interface=None, use_ipv6=True, clients=None,
1225
gnutls_priority=None, use_dbus=True):
1226
self.enabled = False
1227
self.clients = clients
1228
if self.clients is None:
1229
self.clients = set()
1230
self.use_dbus = use_dbus
1231
self.gnutls_priority = gnutls_priority
1232
IPv6_TCPServer.__init__(self, server_address,
1233
RequestHandlerClass,
1234
interface = interface,
1235
use_ipv6 = use_ipv6)
775
return super(IPv6_TCPServer, self).server_bind()
1236
776
def server_activate(self):
1237
777
if self.enabled:
1238
return socketserver.TCPServer.server_activate(self)
778
return super(IPv6_TCPServer, self).server_activate()
1239
779
def enable(self):
1240
780
self.enabled = True
1241
def add_pipe(self, pipe):
1242
# Call "handle_ipc" for both data and EOF events
1243
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1245
def handle_ipc(self, source, condition, file_objects={}):
1247
gobject.IO_IN: u"IN", # There is data to read.
1248
gobject.IO_OUT: u"OUT", # Data can be written (without
1250
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1251
gobject.IO_ERR: u"ERR", # Error condition.
1252
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1253
# broken, usually for pipes and
1256
conditions_string = ' | '.join(name
1258
condition_names.iteritems()
1259
if cond & condition)
1260
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1263
# Turn the pipe file descriptor into a Python file object
1264
if source not in file_objects:
1265
file_objects[source] = os.fdopen(source, u"r", 1)
1267
# Read a line from the file object
1268
cmdline = file_objects[source].readline()
1269
if not cmdline: # Empty line means end of file
1270
# close the IPC pipe
1271
file_objects[source].close()
1272
del file_objects[source]
1274
# Stop calling this function
1277
logger.debug(u"IPC command: %r", cmdline)
1279
# Parse and act on command
1280
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1282
if cmd == u"NOTFOUND":
1283
logger.warning(u"Client not found for fingerprint: %s",
1287
mandos_dbus_service.ClientNotFound(args)
1288
elif cmd == u"INVALID":
1289
for client in self.clients:
1290
if client.name == args:
1291
logger.warning(u"Client %s is invalid", args)
1297
logger.error(u"Unknown client %s is invalid", args)
1298
elif cmd == u"SENDING":
1299
for client in self.clients:
1300
if client.name == args:
1301
logger.info(u"Sending secret to %s", client.name)
1308
logger.error(u"Sending secret to unknown client %s",
1311
logger.error(u"Unknown IPC command: %r", cmdline)
1313
# Keep calling this function
1317
783
def string_to_delta(interval):
1318
784
"""Parse a string and return a datetime.timedelta
1320
>>> string_to_delta(u'7d')
786
>>> string_to_delta('7d')
1321
787
datetime.timedelta(7)
1322
>>> string_to_delta(u'60s')
788
>>> string_to_delta('60s')
1323
789
datetime.timedelta(0, 60)
1324
>>> string_to_delta(u'60m')
790
>>> string_to_delta('60m')
1325
791
datetime.timedelta(0, 3600)
1326
>>> string_to_delta(u'24h')
792
>>> string_to_delta('24h')
1327
793
datetime.timedelta(1)
1328
794
>>> string_to_delta(u'1w')
1329
795
datetime.timedelta(7)
1330
>>> string_to_delta(u'5m 30s')
796
>>> string_to_delta('5m 30s')
1331
797
datetime.timedelta(0, 330)
1333
799
timevalue = datetime.timedelta(0)
1442
923
# Default values for config file for server-global settings
1443
server_defaults = { u"interface": u"",
1448
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1449
u"servicename": u"Mandos",
1450
u"use_dbus": u"True",
1451
u"use_ipv6": u"True",
924
server_defaults = { "interface": "",
929
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
930
"servicename": "Mandos",
1454
934
# Parse config file for server-global settings
1455
server_config = configparser.SafeConfigParser(server_defaults)
935
server_config = ConfigParser.SafeConfigParser(server_defaults)
1456
936
del server_defaults
1457
server_config.read(os.path.join(options.configdir,
937
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1459
938
# Convert the SafeConfigParser object to a dict
1460
939
server_settings = server_config.defaults()
1461
# Use the appropriate methods on the non-string config options
1462
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1463
server_settings[option] = server_config.getboolean(u"DEFAULT",
1465
if server_settings["port"]:
1466
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"))
1468
945
del server_config
1470
947
# Override the settings from the config file with command line
1471
948
# options, if set.
1472
for option in (u"interface", u"address", u"port", u"debug",
1473
u"priority", u"servicename", u"configdir",
1474
u"use_dbus", u"use_ipv6"):
949
for option in ("interface", "address", "port", "debug",
950
"priority", "servicename", "configdir",
1475
952
value = getattr(options, option)
1476
953
if value is not None:
1477
954
server_settings[option] = value
1479
# Force all strings to be unicode
1480
for option in server_settings.keys():
1481
if type(server_settings[option]) is str:
1482
server_settings[option] = unicode(server_settings[option])
1483
956
# Now we have our good server settings in "server_settings"
1485
##################################################################
1487
958
# For convenience
1488
debug = server_settings[u"debug"]
1489
use_dbus = server_settings[u"use_dbus"]
1490
use_ipv6 = server_settings[u"use_ipv6"]
959
debug = server_settings["debug"]
960
use_dbus = server_settings["use_dbus"]
1493
963
syslogger.setLevel(logging.WARNING)
1494
964
console.setLevel(logging.WARNING)
1496
if server_settings[u"servicename"] != u"Mandos":
966
if server_settings["servicename"] != "Mandos":
1497
967
syslogger.setFormatter(logging.Formatter
1498
(u'Mandos (%s) [%%(process)d]:'
1499
u' %%(levelname)s: %%(message)s'
1500
% server_settings[u"servicename"]))
968
('Mandos (%s): %%(levelname)s:'
970
% server_settings["servicename"]))
1502
972
# Parse config file with clients
1503
client_defaults = { u"timeout": u"1h",
1505
u"checker": u"fping -q -- %%(host)s",
973
client_defaults = { "timeout": "1h",
975
"checker": "fping -q -- %(host)s",
1508
client_config = configparser.SafeConfigParser(client_defaults)
1509
client_config.read(os.path.join(server_settings[u"configdir"],
1512
global mandos_dbus_service
1513
mandos_dbus_service = None
1515
tcp_server = MandosServer((server_settings[u"address"],
1516
server_settings[u"port"]),
1518
interface=server_settings[u"interface"],
1521
server_settings[u"priority"],
1523
pidfilename = u"/var/run/mandos.pid"
1525
pidfile = open(pidfilename, u"w")
1527
logger.error(u"Could not open file %r", pidfilename)
1530
uid = pwd.getpwnam(u"_mandos").pw_uid
1531
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
1532
996
except KeyError:
1534
uid = pwd.getpwnam(u"mandos").pw_uid
1535
gid = pwd.getpwnam(u"mandos").pw_gid
998
uid = pwd.getpwnam("mandos").pw_uid
1536
999
except KeyError:
1538
uid = pwd.getpwnam(u"nobody").pw_uid
1539
gid = pwd.getpwnam(u"nobody").pw_gid
1001
uid = pwd.getpwnam("nobody").pw_uid
1540
1002
except KeyError:
1005
gid = pwd.getpwnam("_mandos").pw_gid
1008
gid = pwd.getpwnam("mandos").pw_gid
1011
gid = pwd.getpwnam("nogroup").pw_gid
1546
1017
except OSError, error:
1547
1018
if error[0] != errno.EPERM:
1550
# Enable all possible GnuTLS debugging
1552
# "Use a log level over 10 to enable all debugging options."
1554
gnutls.library.functions.gnutls_global_set_log_level(11)
1556
@gnutls.library.types.gnutls_log_func
1557
def debug_gnutls(level, string):
1558
logger.debug(u"GnuTLS: %s", string[:-1])
1560
(gnutls.library.functions
1561
.gnutls_global_set_log_function(debug_gnutls))
1022
service = AvahiService(name = server_settings["servicename"],
1023
servicetype = "_mandos._tcp", )
1024
if server_settings["interface"]:
1025
service.interface = (if_nametoindex
1026
(server_settings["interface"]))
1563
1028
global main_loop
1564
1031
# From the Avahi example code
1565
1032
DBusGMainLoop(set_as_default=True )
1566
1033
main_loop = gobject.MainLoop()
1567
1034
bus = dbus.SystemBus()
1035
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1036
avahi.DBUS_PATH_SERVER),
1037
avahi.DBUS_INTERFACE_SERVER)
1568
1038
# End of Avahi example code
1570
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1571
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1572
service = AvahiService(name = server_settings[u"servicename"],
1573
servicetype = u"_mandos._tcp",
1574
protocol = protocol, bus = bus)
1575
if server_settings["interface"]:
1576
service.interface = (if_nametoindex
1577
(str(server_settings[u"interface"])))
1040
bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
1579
client_class = Client
1581
client_class = functools.partial(ClientDBus, bus = bus)
1582
tcp_server.clients.update(set(
1583
client_class(name = section,
1584
config= dict(client_config.items(section)))
1585
for section in client_config.sections()))
1586
if not tcp_server.clients:
1043
clients.update(Set(Client(name = section,
1045
= dict(client_config.items(section)),
1046
use_dbus = use_dbus)
1047
for section in client_config.sections()))
1587
1049
logger.warning(u"No clients defined")