465
422
if self.checker_callback_tag:
466
423
gobject.source_remove(self.checker_callback_tag)
467
424
self.checker_callback_tag = None
468
if getattr(self, u"checker", None) is None:
425
if getattr(self, "checker", None) is None:
470
427
logger.debug(u"Stopping checker for %(name)s", vars(self))
472
429
os.kill(self.checker.pid, signal.SIGTERM)
474
431
#if self.checker.poll() is None:
475
432
# os.kill(self.checker.pid, signal.SIGKILL)
476
433
except OSError, error:
477
434
if error.errno != errno.ESRCH: # No such process
479
436
self.checker = None
482
def dbus_service_property(dbus_interface, signature=u"v",
483
access=u"readwrite", byte_arrays=False):
484
"""Decorators for marking methods of a DBusObjectWithProperties to
485
become properties on the D-Bus.
487
The decorated method will be called with no arguments by "Get"
488
and with one argument by "Set".
490
The parameters, where they are supported, are the same as
491
dbus.service.method, except there is only "signature", since the
492
type from Get() and the type sent to Set() is the same.
494
# Encoding deeply encoded byte arrays is not supported yet by the
495
# "Set" method, so we fail early here:
496
if byte_arrays and signature != u"ay":
497
raise ValueError(u"Byte arrays not supported for non-'ay'"
498
u" signature %r" % signature)
500
func._dbus_is_property = True
501
func._dbus_interface = dbus_interface
502
func._dbus_signature = signature
503
func._dbus_access = access
504
func._dbus_name = func.__name__
505
if func._dbus_name.endswith(u"_dbus_property"):
506
func._dbus_name = func._dbus_name[:-14]
507
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
512
class DBusPropertyException(dbus.exceptions.DBusException):
513
"""A base class for D-Bus property-related exceptions
515
def __unicode__(self):
516
return unicode(str(self))
519
class DBusPropertyAccessException(DBusPropertyException):
520
"""A property's access permissions disallows an operation.
525
class DBusPropertyNotFound(DBusPropertyException):
526
"""An attempt was made to access a non-existing property.
531
class DBusObjectWithProperties(dbus.service.Object):
532
"""A D-Bus object with properties.
534
Classes inheriting from this can use the dbus_service_property
535
decorator to expose methods as D-Bus properties. It exposes the
536
standard Get(), Set(), and GetAll() methods on the D-Bus.
540
def _is_dbus_property(obj):
541
return getattr(obj, u"_dbus_is_property", False)
543
def _get_all_dbus_properties(self):
544
"""Returns a generator of (name, attribute) pairs
546
return ((prop._dbus_name, prop)
548
inspect.getmembers(self, self._is_dbus_property))
550
def _get_dbus_property(self, interface_name, property_name):
551
"""Returns a bound method if one exists which is a D-Bus
552
property with the specified name and interface.
554
for name in (property_name,
555
property_name + u"_dbus_property"):
556
prop = getattr(self, name, None)
558
or not self._is_dbus_property(prop)
559
or prop._dbus_name != property_name
560
or (interface_name and prop._dbus_interface
561
and interface_name != prop._dbus_interface)):
565
raise DBusPropertyNotFound(self.dbus_object_path + u":"
566
+ interface_name + u"."
569
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
571
def Get(self, interface_name, property_name):
572
"""Standard D-Bus property Get() method, see D-Bus standard.
574
prop = self._get_dbus_property(interface_name, property_name)
575
if prop._dbus_access == u"write":
576
raise DBusPropertyAccessException(property_name)
578
if not hasattr(value, u"variant_level"):
580
return type(value)(value, variant_level=value.variant_level+1)
582
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
583
def Set(self, interface_name, property_name, value):
584
"""Standard D-Bus property Set() method, see D-Bus standard.
586
prop = self._get_dbus_property(interface_name, property_name)
587
if prop._dbus_access == u"read":
588
raise DBusPropertyAccessException(property_name)
589
if prop._dbus_get_args_options[u"byte_arrays"]:
590
# The byte_arrays option is not supported yet on
591
# signatures other than "ay".
592
if prop._dbus_signature != u"ay":
594
value = dbus.ByteArray(''.join(unichr(byte)
598
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
599
out_signature=u"a{sv}")
600
def GetAll(self, interface_name):
601
"""Standard D-Bus property GetAll() method, see D-Bus
604
Note: Will not include properties with access="write".
607
for name, prop in self._get_all_dbus_properties():
609
and interface_name != prop._dbus_interface):
610
# Interface non-empty but did not match
612
# Ignore write-only properties
613
if prop._dbus_access == u"write":
616
if not hasattr(value, u"variant_level"):
619
all[name] = type(value)(value, variant_level=
620
value.variant_level+1)
621
return dbus.Dictionary(all, signature=u"sv")
623
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
625
path_keyword='object_path',
626
connection_keyword='connection')
627
def Introspect(self, object_path, connection):
628
"""Standard D-Bus method, overloaded to insert property tags.
630
xmlstring = dbus.service.Object.Introspect(self, object_path,
633
document = xml.dom.minidom.parseString(xmlstring)
634
def make_tag(document, name, prop):
635
e = document.createElement(u"property")
636
e.setAttribute(u"name", name)
637
e.setAttribute(u"type", prop._dbus_signature)
638
e.setAttribute(u"access", prop._dbus_access)
640
for if_tag in document.getElementsByTagName(u"interface"):
641
for tag in (make_tag(document, name, prop)
643
in self._get_all_dbus_properties()
644
if prop._dbus_interface
645
== if_tag.getAttribute(u"name")):
646
if_tag.appendChild(tag)
647
# Add the names to the return values for the
648
# "org.freedesktop.DBus.Properties" methods
649
if (if_tag.getAttribute(u"name")
650
== u"org.freedesktop.DBus.Properties"):
651
for cn in if_tag.getElementsByTagName(u"method"):
652
if cn.getAttribute(u"name") == u"Get":
653
for arg in cn.getElementsByTagName(u"arg"):
654
if (arg.getAttribute(u"direction")
656
arg.setAttribute(u"name", u"value")
657
elif cn.getAttribute(u"name") == u"GetAll":
658
for arg in cn.getElementsByTagName(u"arg"):
659
if (arg.getAttribute(u"direction")
661
arg.setAttribute(u"name", u"props")
662
xmlstring = document.toxml(u"utf-8")
664
except (AttributeError, xml.dom.DOMException,
665
xml.parsers.expat.ExpatError), error:
666
logger.error(u"Failed to override Introspection method",
671
class ClientDBus(Client, DBusObjectWithProperties):
672
"""A Client class using D-Bus
675
dbus_object_path: dbus.ObjectPath
676
bus: dbus.SystemBus()
678
# dbus.service.Object doesn't use super(), so we can't either.
680
def __init__(self, bus = None, *args, **kwargs):
682
Client.__init__(self, *args, **kwargs)
683
# Only now, when this client is initialized, can it show up on
685
self.dbus_object_path = (dbus.ObjectPath
687
+ self.name.replace(u".", u"_")))
688
DBusObjectWithProperties.__init__(self, self.bus,
689
self.dbus_object_path)
692
def _datetime_to_dbus(dt, variant_level=0):
693
"""Convert a UTC datetime.datetime() to a D-Bus type."""
694
return dbus.String(dt.isoformat(),
695
variant_level=variant_level)
698
oldstate = getattr(self, u"enabled", False)
699
r = Client.enable(self)
700
if oldstate != self.enabled:
702
self.PropertyChanged(dbus.String(u"enabled"),
703
dbus.Boolean(True, variant_level=1))
704
self.PropertyChanged(
705
dbus.String(u"last_enabled"),
706
self._datetime_to_dbus(self.last_enabled,
710
def disable(self, quiet = False):
711
oldstate = getattr(self, u"enabled", False)
712
r = Client.disable(self, quiet=quiet)
713
if not quiet and oldstate != self.enabled:
715
self.PropertyChanged(dbus.String(u"enabled"),
716
dbus.Boolean(False, variant_level=1))
719
def __del__(self, *args, **kwargs):
721
self.remove_from_connection()
724
if hasattr(DBusObjectWithProperties, u"__del__"):
725
DBusObjectWithProperties.__del__(self, *args, **kwargs)
726
Client.__del__(self, *args, **kwargs)
728
def checker_callback(self, pid, condition, command,
730
self.checker_callback_tag = None
733
self.PropertyChanged(dbus.String(u"checker_running"),
734
dbus.Boolean(False, variant_level=1))
735
if os.WIFEXITED(condition):
736
exitstatus = os.WEXITSTATUS(condition)
738
self.CheckerCompleted(dbus.Int16(exitstatus),
739
dbus.Int64(condition),
740
dbus.String(command))
743
self.CheckerCompleted(dbus.Int16(-1),
744
dbus.Int64(condition),
745
dbus.String(command))
747
return Client.checker_callback(self, pid, condition, command,
750
def checked_ok(self, *args, **kwargs):
751
r = Client.checked_ok(self, *args, **kwargs)
753
self.PropertyChanged(
754
dbus.String(u"last_checked_ok"),
755
(self._datetime_to_dbus(self.last_checked_ok,
759
def start_checker(self, *args, **kwargs):
760
old_checker = self.checker
761
if self.checker is not None:
762
old_checker_pid = self.checker.pid
764
old_checker_pid = None
765
r = Client.start_checker(self, *args, **kwargs)
766
# Only if new checker process was started
767
if (self.checker is not None
768
and old_checker_pid != self.checker.pid):
770
self.CheckerStarted(self.current_checker_command)
771
self.PropertyChanged(
772
dbus.String(u"checker_running"),
773
dbus.Boolean(True, variant_level=1))
776
def stop_checker(self, *args, **kwargs):
777
old_checker = getattr(self, u"checker", None)
778
r = Client.stop_checker(self, *args, **kwargs)
779
if (old_checker is not None
780
and getattr(self, u"checker", None) is None):
781
438
self.PropertyChanged(dbus.String(u"checker_running"),
782
439
dbus.Boolean(False, variant_level=1))
785
## D-Bus methods, signals & properties
441
def still_valid(self):
442
"""Has the timeout not yet passed for this client?"""
443
if not getattr(self, "enabled", False):
445
now = datetime.datetime.utcnow()
446
if self.last_checked_ok is None:
447
return now < (self.created + self.timeout)
449
return now < (self.last_checked_ok + self.timeout)
451
## D-Bus methods & signals
786
452
_interface = u"se.bsnet.fukt.Mandos.Client"
455
CheckedOK = dbus.service.method(_interface)(checked_ok)
456
CheckedOK.__name__ = "CheckedOK"
790
458
# CheckerCompleted - signal
791
@dbus.service.signal(_interface, signature=u"nxs")
459
@dbus.service.signal(_interface, signature="nxs")
792
460
def CheckerCompleted(self, exitcode, waitstatus, command):
796
464
# CheckerStarted - signal
797
@dbus.service.signal(_interface, signature=u"s")
465
@dbus.service.signal(_interface, signature="s")
798
466
def CheckerStarted(self, command):
470
# GetAllProperties - method
471
@dbus.service.method(_interface, out_signature="a{sv}")
472
def GetAllProperties(self):
474
return dbus.Dictionary({
476
dbus.String(self.name, variant_level=1),
477
dbus.String("fingerprint"):
478
dbus.String(self.fingerprint, variant_level=1),
480
dbus.String(self.host, variant_level=1),
481
dbus.String("created"):
482
_datetime_to_dbus(self.created, variant_level=1),
483
dbus.String("last_enabled"):
484
(_datetime_to_dbus(self.last_enabled,
486
if self.last_enabled is not None
487
else dbus.Boolean(False, variant_level=1)),
488
dbus.String("enabled"):
489
dbus.Boolean(self.enabled, variant_level=1),
490
dbus.String("last_checked_ok"):
491
(_datetime_to_dbus(self.last_checked_ok,
493
if self.last_checked_ok is not None
494
else dbus.Boolean (False, variant_level=1)),
495
dbus.String("timeout"):
496
dbus.UInt64(self.timeout_milliseconds(),
498
dbus.String("interval"):
499
dbus.UInt64(self.interval_milliseconds(),
501
dbus.String("checker"):
502
dbus.String(self.checker_command,
504
dbus.String("checker_running"):
505
dbus.Boolean(self.checker is not None,
507
dbus.String("object_path"):
508
dbus.ObjectPath(self.dbus_object_path,
512
# IsStillValid - method
513
IsStillValid = (dbus.service.method(_interface, out_signature="b")
515
IsStillValid.__name__ = "IsStillValid"
802
517
# PropertyChanged - signal
803
@dbus.service.signal(_interface, signature=u"sv")
518
@dbus.service.signal(_interface, signature="sv")
804
519
def PropertyChanged(self, property, value):
809
@dbus.service.signal(_interface)
815
@dbus.service.signal(_interface)
823
@dbus.service.method(_interface)
825
return self.checked_ok()
523
# SetChecker - method
524
@dbus.service.method(_interface, in_signature="s")
525
def SetChecker(self, checker):
526
"D-Bus setter method"
527
self.checker_command = checker
529
self.PropertyChanged(dbus.String(u"checker"),
530
dbus.String(self.checker_command,
534
@dbus.service.method(_interface, in_signature="s")
535
def SetHost(self, host):
536
"D-Bus setter method"
539
self.PropertyChanged(dbus.String(u"host"),
540
dbus.String(self.host, variant_level=1))
542
# SetInterval - method
543
@dbus.service.method(_interface, in_signature="t")
544
def SetInterval(self, milliseconds):
545
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
547
self.PropertyChanged(dbus.String(u"interval"),
548
(dbus.UInt64(self.interval_milliseconds(),
552
@dbus.service.method(_interface, in_signature="ay",
554
def SetSecret(self, secret):
555
"D-Bus setter method"
556
self.secret = str(secret)
558
# SetTimeout - method
559
@dbus.service.method(_interface, in_signature="t")
560
def SetTimeout(self, milliseconds):
561
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
563
self.PropertyChanged(dbus.String(u"timeout"),
564
(dbus.UInt64(self.timeout_milliseconds(),
827
567
# Enable - method
828
@dbus.service.method(_interface)
568
Enable = dbus.service.method(_interface)(enable)
569
Enable.__name__ = "Enable"
833
571
# StartChecker - method
834
572
@dbus.service.method(_interface)
845
583
# StopChecker - method
846
@dbus.service.method(_interface)
847
def StopChecker(self):
853
@dbus_service_property(_interface, signature=u"s", access=u"read")
854
def name_dbus_property(self):
855
return dbus.String(self.name)
857
# fingerprint - property
858
@dbus_service_property(_interface, signature=u"s", access=u"read")
859
def fingerprint_dbus_property(self):
860
return dbus.String(self.fingerprint)
863
@dbus_service_property(_interface, signature=u"s",
865
def host_dbus_property(self, value=None):
866
if value is None: # get
867
return dbus.String(self.host)
870
self.PropertyChanged(dbus.String(u"host"),
871
dbus.String(value, variant_level=1))
874
@dbus_service_property(_interface, signature=u"s", access=u"read")
875
def created_dbus_property(self):
876
return dbus.String(self._datetime_to_dbus(self.created))
878
# last_enabled - property
879
@dbus_service_property(_interface, signature=u"s", access=u"read")
880
def last_enabled_dbus_property(self):
881
if self.last_enabled is None:
882
return dbus.String(u"")
883
return dbus.String(self._datetime_to_dbus(self.last_enabled))
886
@dbus_service_property(_interface, signature=u"b",
888
def enabled_dbus_property(self, value=None):
889
if value is None: # get
890
return dbus.Boolean(self.enabled)
896
# last_checked_ok - property
897
@dbus_service_property(_interface, signature=u"s",
899
def last_checked_ok_dbus_property(self, value=None):
900
if value is not None:
903
if self.last_checked_ok is None:
904
return dbus.String(u"")
905
return dbus.String(self._datetime_to_dbus(self
909
@dbus_service_property(_interface, signature=u"t",
911
def timeout_dbus_property(self, value=None):
912
if value is None: # get
913
return dbus.UInt64(self.timeout_milliseconds())
914
self.timeout = datetime.timedelta(0, 0, 0, value)
916
self.PropertyChanged(dbus.String(u"timeout"),
917
dbus.UInt64(value, variant_level=1))
918
if getattr(self, u"disable_initiator_tag", None) is None:
921
gobject.source_remove(self.disable_initiator_tag)
922
self.disable_initiator_tag = None
924
_timedelta_to_milliseconds((self
930
# The timeout has passed
933
self.disable_initiator_tag = (gobject.timeout_add
934
(time_to_die, self.disable))
936
# interval - property
937
@dbus_service_property(_interface, signature=u"t",
939
def interval_dbus_property(self, value=None):
940
if value is None: # get
941
return dbus.UInt64(self.interval_milliseconds())
942
self.interval = datetime.timedelta(0, 0, 0, value)
944
self.PropertyChanged(dbus.String(u"interval"),
945
dbus.UInt64(value, variant_level=1))
946
if getattr(self, u"checker_initiator_tag", None) is None:
948
# Reschedule checker run
949
gobject.source_remove(self.checker_initiator_tag)
950
self.checker_initiator_tag = (gobject.timeout_add
951
(value, self.start_checker))
952
self.start_checker() # Start one now, too
955
@dbus_service_property(_interface, signature=u"s",
957
def checker_dbus_property(self, value=None):
958
if value is None: # get
959
return dbus.String(self.checker_command)
960
self.checker_command = value
962
self.PropertyChanged(dbus.String(u"checker"),
963
dbus.String(self.checker_command,
966
# checker_running - property
967
@dbus_service_property(_interface, signature=u"b",
969
def checker_running_dbus_property(self, value=None):
970
if value is None: # get
971
return dbus.Boolean(self.checker is not None)
977
# object_path - property
978
@dbus_service_property(_interface, signature=u"o", access=u"read")
979
def object_path_dbus_property(self):
980
return self.dbus_object_path # is already a dbus.ObjectPath
983
@dbus_service_property(_interface, signature=u"ay",
984
access=u"write", byte_arrays=True)
985
def secret_dbus_property(self, value):
986
self.secret = str(value)
584
StopChecker = dbus.service.method(_interface)(stop_checker)
585
StopChecker.__name__ = "StopChecker"
991
class ClientHandler(socketserver.BaseRequestHandler, object):
992
"""A class to handle client connections.
994
Instantiated once for each connection to handle it.
590
def peer_certificate(session):
591
"Return the peer's OpenPGP certificate as a bytestring"
592
# If not an OpenPGP certificate...
593
if (gnutls.library.functions
594
.gnutls_certificate_type_get(session._c_object)
595
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
596
# ...do the normal thing
597
return session.peer_certificate
598
list_size = ctypes.c_uint(1)
599
cert_list = (gnutls.library.functions
600
.gnutls_certificate_get_peers
601
(session._c_object, ctypes.byref(list_size)))
602
if not bool(cert_list) and list_size.value != 0:
603
raise gnutls.errors.GNUTLSError("error getting peer"
605
if list_size.value == 0:
608
return ctypes.string_at(cert.data, cert.size)
611
def fingerprint(openpgp):
612
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
613
# New GnuTLS "datum" with the OpenPGP public key
614
datum = (gnutls.library.types
615
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
618
ctypes.c_uint(len(openpgp))))
619
# New empty GnuTLS certificate
620
crt = gnutls.library.types.gnutls_openpgp_crt_t()
621
(gnutls.library.functions
622
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
623
# Import the OpenPGP public key into the certificate
624
(gnutls.library.functions
625
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
626
gnutls.library.constants
627
.GNUTLS_OPENPGP_FMT_RAW))
628
# Verify the self signature in the key
629
crtverify = ctypes.c_uint()
630
(gnutls.library.functions
631
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
632
if crtverify.value != 0:
633
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
634
raise gnutls.errors.CertificateSecurityError("Verify failed")
635
# New buffer for the fingerprint
636
buf = ctypes.create_string_buffer(20)
637
buf_len = ctypes.c_size_t()
638
# Get the fingerprint from the certificate into the buffer
639
(gnutls.library.functions
640
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
641
ctypes.byref(buf_len)))
642
# Deinit the certificate
643
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
644
# Convert the buffer to a Python bytestring
645
fpr = ctypes.string_at(buf, buf_len.value)
646
# Convert the bytestring to hexadecimal notation
647
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
651
class TCP_handler(SocketServer.BaseRequestHandler, object):
652
"""A TCP request handler class.
653
Instantiated by IPv6_TCPServer for each request to handle it.
995
654
Note: This will run in its own forked process."""
997
656
def handle(self):
998
657
logger.info(u"TCP connection from: %s",
999
658
unicode(self.client_address))
1000
logger.debug(u"IPC Pipe FD: %d",
1001
self.server.child_pipe[1].fileno())
1002
# Open IPC pipe to parent process
1003
with contextlib.nested(self.server.child_pipe[1],
1004
self.server.parent_pipe[0]
1005
) as (ipc, ipc_return):
1006
session = (gnutls.connection
1007
.ClientSession(self.request,
1009
.X509Credentials()))
1011
# Note: gnutls.connection.X509Credentials is really a
1012
# generic GnuTLS certificate credentials object so long as
1013
# no X.509 keys are added to it. Therefore, we can use it
1014
# here despite using OpenPGP certificates.
1016
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1017
# u"+AES-256-CBC", u"+SHA1",
1018
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1020
# Use a fallback default, since this MUST be set.
1021
priority = self.server.gnutls_priority
1022
if priority is None:
1023
priority = u"NORMAL"
1024
(gnutls.library.functions
1025
.gnutls_priority_set_direct(session._c_object,
1028
# Start communication using the Mandos protocol
1029
# Get protocol number
1030
line = self.request.makefile().readline()
1031
logger.debug(u"Protocol version: %r", line)
1033
if int(line.strip().split()[0]) > 1:
1035
except (ValueError, IndexError, RuntimeError), error:
1036
logger.error(u"Unknown protocol version: %s", error)
1039
# Start GnuTLS connection
1042
except gnutls.errors.GNUTLSError, error:
1043
logger.warning(u"Handshake failed: %s", error)
1044
# Do not run session.bye() here: the session is not
1045
# established. Just abandon the request.
1047
logger.debug(u"Handshake succeeded")
1050
fpr = self.fingerprint(self.peer_certificate
1052
except (TypeError, gnutls.errors.GNUTLSError), error:
1053
logger.warning(u"Bad certificate: %s", error)
1055
logger.debug(u"Fingerprint: %s", fpr)
1057
for c in self.server.clients:
1058
if c.fingerprint == fpr:
1062
ipc.write(u"NOTFOUND %s %s\n"
1063
% (fpr, unicode(self.client_address)))
1066
class ClientProxy(object):
1067
"""Client proxy object. Not for calling methods."""
1068
def __init__(self, client):
1069
self.client = client
1070
def __getattr__(self, name):
1071
if name.startswith("ipc_"):
1073
ipc.write("%s %s\n" % (name[4:].upper(),
1076
if not hasattr(self.client, name):
1077
raise AttributeError
1078
ipc.write(u"GETATTR %s %s\n"
1079
% (name, self.client.fingerprint))
1080
return pickle.load(ipc_return)
1081
clientproxy = ClientProxy(client)
1082
# Have to check if client.enabled, since it is
1083
# possible that the client was disabled since the
1084
# GnuTLS session was established.
1085
if not clientproxy.enabled:
1086
clientproxy.ipc_disabled()
1089
clientproxy.ipc_sending()
1091
while sent_size < len(client.secret):
1092
sent = session.send(client.secret[sent_size:])
1093
logger.debug(u"Sent: %d, remaining: %d",
1094
sent, len(client.secret)
1095
- (sent_size + sent))
1101
def peer_certificate(session):
1102
"Return the peer's OpenPGP certificate as a bytestring"
1103
# If not an OpenPGP certificate...
1104
if (gnutls.library.functions
1105
.gnutls_certificate_type_get(session._c_object)
1106
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1107
# ...do the normal thing
1108
return session.peer_certificate
1109
list_size = ctypes.c_uint(1)
1110
cert_list = (gnutls.library.functions
1111
.gnutls_certificate_get_peers
1112
(session._c_object, ctypes.byref(list_size)))
1113
if not bool(cert_list) and list_size.value != 0:
1114
raise gnutls.errors.GNUTLSError(u"error getting peer"
1116
if list_size.value == 0:
1119
return ctypes.string_at(cert.data, cert.size)
1122
def fingerprint(openpgp):
1123
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1124
# New GnuTLS "datum" with the OpenPGP public key
1125
datum = (gnutls.library.types
1126
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1129
ctypes.c_uint(len(openpgp))))
1130
# New empty GnuTLS certificate
1131
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1132
(gnutls.library.functions
1133
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1134
# Import the OpenPGP public key into the certificate
1135
(gnutls.library.functions
1136
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1137
gnutls.library.constants
1138
.GNUTLS_OPENPGP_FMT_RAW))
1139
# Verify the self signature in the key
1140
crtverify = ctypes.c_uint()
1141
(gnutls.library.functions
1142
.gnutls_openpgp_crt_verify_self(crt, 0,
1143
ctypes.byref(crtverify)))
1144
if crtverify.value != 0:
1145
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1146
raise (gnutls.errors.CertificateSecurityError
1148
# New buffer for the fingerprint
1149
buf = ctypes.create_string_buffer(20)
1150
buf_len = ctypes.c_size_t()
1151
# Get the fingerprint from the certificate into the buffer
1152
(gnutls.library.functions
1153
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1154
ctypes.byref(buf_len)))
1155
# Deinit the certificate
1156
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1157
# Convert the buffer to a Python bytestring
1158
fpr = ctypes.string_at(buf, buf_len.value)
1159
# Convert the bytestring to hexadecimal notation
1160
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1164
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
1165
"""Like socketserver.ForkingMixIn, but also pass a pipe pair."""
1166
def process_request(self, request, client_address):
1167
"""Overrides and wraps the original process_request().
1169
This function creates a new pipe in self.pipe
1171
# Child writes to child_pipe
1172
self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1173
# Parent writes to parent_pipe
1174
self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1175
super(ForkingMixInWithPipes,
1176
self).process_request(request, client_address)
1177
# Close unused ends for parent
1178
self.parent_pipe[0].close() # close read end
1179
self.child_pipe[1].close() # close write end
1180
self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
1181
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1182
"""Dummy function; override as necessary"""
1183
child_pipe_fd.close()
1184
parent_pipe_fd.close()
1187
class IPv6_TCPServer(ForkingMixInWithPipes,
1188
socketserver.TCPServer, object):
1189
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
659
session = (gnutls.connection
660
.ClientSession(self.request,
664
line = self.request.makefile().readline()
665
logger.debug(u"Protocol version: %r", line)
667
if int(line.strip().split()[0]) > 1:
669
except (ValueError, IndexError, RuntimeError), error:
670
logger.error(u"Unknown protocol version: %s", error)
673
# Note: gnutls.connection.X509Credentials is really a generic
674
# GnuTLS certificate credentials object so long as no X.509
675
# keys are added to it. Therefore, we can use it here despite
676
# using OpenPGP certificates.
678
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
679
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
681
# Use a fallback default, since this MUST be set.
682
priority = self.server.settings.get("priority", "NORMAL")
683
(gnutls.library.functions
684
.gnutls_priority_set_direct(session._c_object,
689
except gnutls.errors.GNUTLSError, error:
690
logger.warning(u"Handshake failed: %s", error)
691
# Do not run session.bye() here: the session is not
692
# established. Just abandon the request.
694
logger.debug(u"Handshake succeeded")
696
fpr = fingerprint(peer_certificate(session))
697
except (TypeError, gnutls.errors.GNUTLSError), error:
698
logger.warning(u"Bad certificate: %s", error)
701
logger.debug(u"Fingerprint: %s", fpr)
702
for c in self.server.clients:
703
if c.fingerprint == fpr:
707
logger.warning(u"Client not found for fingerprint: %s",
711
# Have to check if client.still_valid(), since it is possible
712
# that the client timed out while establishing the GnuTLS
714
if not client.still_valid():
715
logger.warning(u"Client %(name)s is invalid",
719
## This won't work here, since we're in a fork.
720
# client.checked_ok()
722
while sent_size < len(client.secret):
723
sent = session.send(client.secret[sent_size:])
724
logger.debug(u"Sent: %d, remaining: %d",
725
sent, len(client.secret)
726
- (sent_size + sent))
731
class IPv6_TCPServer(SocketServer.ForkingMixIn,
732
SocketServer.TCPServer, object):
733
"""IPv6 TCP server. Accepts 'None' as address and/or port.
735
settings: Server settings
736
clients: Set() of Client objects
1192
737
enabled: Boolean; whether this server is activated yet
1193
interface: None or a network interface name (string)
1194
use_ipv6: Boolean; to use IPv6 or not
1196
def __init__(self, server_address, RequestHandlerClass,
1197
interface=None, use_ipv6=True):
1198
self.interface = interface
1200
self.address_family = socket.AF_INET6
1201
socketserver.TCPServer.__init__(self, server_address,
1202
RequestHandlerClass)
739
address_family = socket.AF_INET6
740
def __init__(self, *args, **kwargs):
741
if "settings" in kwargs:
742
self.settings = kwargs["settings"]
743
del kwargs["settings"]
744
if "clients" in kwargs:
745
self.clients = kwargs["clients"]
746
del kwargs["clients"]
748
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
1203
749
def server_bind(self):
1204
750
"""This overrides the normal server_bind() function
1205
751
to bind to an interface if one was specified, and also NOT to
1206
752
bind to an address or port if they were not specified."""
1207
if self.interface is not None:
1208
if SO_BINDTODEVICE is None:
1209
logger.error(u"SO_BINDTODEVICE does not exist;"
1210
u" cannot bind to interface %s",
1214
self.socket.setsockopt(socket.SOL_SOCKET,
1218
except socket.error, error:
1219
if error[0] == errno.EPERM:
1220
logger.error(u"No permission to"
1221
u" bind to interface %s",
1223
elif error[0] == errno.ENOPROTOOPT:
1224
logger.error(u"SO_BINDTODEVICE not available;"
1225
u" cannot bind to interface %s",
753
if self.settings["interface"]:
754
# 25 is from /usr/include/asm-i486/socket.h
755
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
757
self.socket.setsockopt(socket.SOL_SOCKET,
759
self.settings["interface"])
760
except socket.error, error:
761
if error[0] == errno.EPERM:
762
logger.error(u"No permission to"
763
u" bind to interface %s",
764
self.settings["interface"])
1229
767
# Only bind(2) the socket if we really need to.
1230
768
if self.server_address[0] or self.server_address[1]:
1231
769
if not self.server_address[0]:
1232
if self.address_family == socket.AF_INET6:
1233
any_address = u"::" # in6addr_any
1235
any_address = socket.INADDR_ANY
1236
self.server_address = (any_address,
771
self.server_address = (in6addr_any,
1237
772
self.server_address[1])
1238
773
elif not self.server_address[1]:
1239
774
self.server_address = (self.server_address[0],
1241
# if self.interface:
776
# if self.settings["interface"]:
1242
777
# self.server_address = (self.server_address[0],
1245
780
# if_nametoindex
1247
return socketserver.TCPServer.server_bind(self)
1250
class MandosServer(IPv6_TCPServer):
1254
clients: set of Client objects
1255
gnutls_priority GnuTLS priority string
1256
use_dbus: Boolean; to emit D-Bus signals or not
1258
Assumes a gobject.MainLoop event loop.
1260
def __init__(self, server_address, RequestHandlerClass,
1261
interface=None, use_ipv6=True, clients=None,
1262
gnutls_priority=None, use_dbus=True):
1263
self.enabled = False
1264
self.clients = clients
1265
if self.clients is None:
1266
self.clients = set()
1267
self.use_dbus = use_dbus
1268
self.gnutls_priority = gnutls_priority
1269
IPv6_TCPServer.__init__(self, server_address,
1270
RequestHandlerClass,
1271
interface = interface,
1272
use_ipv6 = use_ipv6)
783
return super(IPv6_TCPServer, self).server_bind()
1273
784
def server_activate(self):
1274
785
if self.enabled:
1275
return socketserver.TCPServer.server_activate(self)
786
return super(IPv6_TCPServer, self).server_activate()
1276
787
def enable(self):
1277
788
self.enabled = True
1278
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1279
# Call "handle_ipc" for both data and EOF events
1280
gobject.io_add_watch(child_pipe_fd.fileno(),
1281
gobject.IO_IN | gobject.IO_HUP,
1282
functools.partial(self.handle_ipc,
1283
reply = parent_pipe_fd,
1284
sender= child_pipe_fd))
1285
def handle_ipc(self, source, condition, reply=None, sender=None):
1287
gobject.IO_IN: u"IN", # There is data to read.
1288
gobject.IO_OUT: u"OUT", # Data can be written (without
1290
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1291
gobject.IO_ERR: u"ERR", # Error condition.
1292
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1293
# broken, usually for pipes and
1296
conditions_string = ' | '.join(name
1298
condition_names.iteritems()
1299
if cond & condition)
1300
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1303
# Read a line from the file object
1304
cmdline = sender.readline()
1305
if not cmdline: # Empty line means end of file
1306
# close the IPC pipes
1310
# Stop calling this function
1313
logger.debug(u"IPC command: %r", cmdline)
1315
# Parse and act on command
1316
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1318
if cmd == u"NOTFOUND":
1319
fpr, address = args.split(None, 1)
1320
logger.warning(u"Client not found for fingerprint: %s, ad"
1321
u"dress: %s", fpr, address)
1324
mandos_dbus_service.ClientNotFound(fpr, address)
1325
elif cmd == u"DISABLED":
1326
for client in self.clients:
1327
if client.name == args:
1328
logger.warning(u"Client %s is disabled", args)
1334
logger.error(u"Unknown client %s is disabled", args)
1335
elif cmd == u"SENDING":
1336
for client in self.clients:
1337
if client.name == args:
1338
logger.info(u"Sending secret to %s", client.name)
1345
logger.error(u"Sending secret to unknown client %s",
1347
elif cmd == u"GETATTR":
1348
attr_name, fpr = args.split(None, 1)
1349
for client in self.clients:
1350
if client.fingerprint == fpr:
1351
attr_value = getattr(client, attr_name, None)
1352
logger.debug("IPC reply: %r", attr_value)
1353
pickle.dump(attr_value, reply)
1356
logger.error(u"Client %s on address %s requesting "
1357
u"attribute %s not found", fpr, address,
1359
pickle.dump(None, reply)
1361
logger.error(u"Unknown IPC command: %r", cmdline)
1363
# Keep calling this function
1367
791
def string_to_delta(interval):
1368
792
"""Parse a string and return a datetime.timedelta
1370
>>> string_to_delta(u'7d')
794
>>> string_to_delta('7d')
1371
795
datetime.timedelta(7)
1372
>>> string_to_delta(u'60s')
796
>>> string_to_delta('60s')
1373
797
datetime.timedelta(0, 60)
1374
>>> string_to_delta(u'60m')
798
>>> string_to_delta('60m')
1375
799
datetime.timedelta(0, 3600)
1376
>>> string_to_delta(u'24h')
800
>>> string_to_delta('24h')
1377
801
datetime.timedelta(1)
1378
802
>>> string_to_delta(u'1w')
1379
803
datetime.timedelta(7)
1380
>>> string_to_delta(u'5m 30s')
804
>>> string_to_delta('5m 30s')
1381
805
datetime.timedelta(0, 330)
1383
807
timevalue = datetime.timedelta(0)
1492
931
# Default values for config file for server-global settings
1493
server_defaults = { u"interface": u"",
1498
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1499
u"servicename": u"Mandos",
1500
u"use_dbus": u"True",
1501
u"use_ipv6": u"True",
932
server_defaults = { "interface": "",
937
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
938
"servicename": "Mandos",
1504
942
# Parse config file for server-global settings
1505
server_config = configparser.SafeConfigParser(server_defaults)
943
server_config = ConfigParser.SafeConfigParser(server_defaults)
1506
944
del server_defaults
1507
server_config.read(os.path.join(options.configdir,
945
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1509
946
# Convert the SafeConfigParser object to a dict
1510
947
server_settings = server_config.defaults()
1511
948
# Use the appropriate methods on the non-string config options
1512
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1513
server_settings[option] = server_config.getboolean(u"DEFAULT",
949
server_settings["debug"] = server_config.getboolean("DEFAULT",
951
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1515
953
if server_settings["port"]:
1516
server_settings["port"] = server_config.getint(u"DEFAULT",
954
server_settings["port"] = server_config.getint("DEFAULT",
1518
956
del server_config
1520
958
# Override the settings from the config file with command line
1521
959
# options, if set.
1522
for option in (u"interface", u"address", u"port", u"debug",
1523
u"priority", u"servicename", u"configdir",
1524
u"use_dbus", u"use_ipv6"):
960
for option in ("interface", "address", "port", "debug",
961
"priority", "servicename", "configdir",
1525
963
value = getattr(options, option)
1526
964
if value is not None:
1527
965
server_settings[option] = value
1529
# Force all strings to be unicode
1530
for option in server_settings.keys():
1531
if type(server_settings[option]) is str:
1532
server_settings[option] = unicode(server_settings[option])
1533
967
# Now we have our good server settings in "server_settings"
1535
##################################################################
1537
969
# For convenience
1538
debug = server_settings[u"debug"]
1539
use_dbus = server_settings[u"use_dbus"]
1540
use_ipv6 = server_settings[u"use_ipv6"]
970
debug = server_settings["debug"]
971
use_dbus = server_settings["use_dbus"]
1543
974
syslogger.setLevel(logging.WARNING)
1544
975
console.setLevel(logging.WARNING)
1546
if server_settings[u"servicename"] != u"Mandos":
977
if server_settings["servicename"] != "Mandos":
1547
978
syslogger.setFormatter(logging.Formatter
1548
(u'Mandos (%s) [%%(process)d]:'
1549
u' %%(levelname)s: %%(message)s'
1550
% server_settings[u"servicename"]))
979
('Mandos (%s): %%(levelname)s:'
981
% server_settings["servicename"]))
1552
983
# Parse config file with clients
1553
client_defaults = { u"timeout": u"1h",
1555
u"checker": u"fping -q -- %%(host)s",
984
client_defaults = { "timeout": "1h",
986
"checker": "fping -q -- %%(host)s",
1558
client_config = configparser.SafeConfigParser(client_defaults)
1559
client_config.read(os.path.join(server_settings[u"configdir"],
1562
global mandos_dbus_service
1563
mandos_dbus_service = None
1565
tcp_server = MandosServer((server_settings[u"address"],
1566
server_settings[u"port"]),
1568
interface=(server_settings[u"interface"]
1572
server_settings[u"priority"],
1574
pidfilename = u"/var/run/mandos.pid"
1576
pidfile = open(pidfilename, u"w")
1578
logger.error(u"Could not open file %r", pidfilename)
1581
uid = pwd.getpwnam(u"_mandos").pw_uid
1582
gid = pwd.getpwnam(u"_mandos").pw_gid
989
client_config = ConfigParser.SafeConfigParser(client_defaults)
990
client_config.read(os.path.join(server_settings["configdir"],
994
tcp_server = IPv6_TCPServer((server_settings["address"],
995
server_settings["port"]),
997
settings=server_settings,
999
pidfilename = "/var/run/mandos.pid"
1001
pidfile = open(pidfilename, "w")
1002
except IOError, error:
1003
logger.error("Could not open file %r", pidfilename)
1006
uid = pwd.getpwnam("_mandos").pw_uid
1007
gid = pwd.getpwnam("_mandos").pw_gid
1583
1008
except KeyError:
1585
uid = pwd.getpwnam(u"mandos").pw_uid
1586
gid = pwd.getpwnam(u"mandos").pw_gid
1010
uid = pwd.getpwnam("mandos").pw_uid
1011
gid = pwd.getpwnam("mandos").pw_gid
1587
1012
except KeyError:
1589
uid = pwd.getpwnam(u"nobody").pw_uid
1590
gid = pwd.getpwnam(u"nobody").pw_gid
1014
uid = pwd.getpwnam("nobody").pw_uid
1015
gid = pwd.getpwnam("nogroup").pw_gid
1591
1016
except KeyError:
1597
1022
except OSError, error:
1598
1023
if error[0] != errno.EPERM:
1601
# Enable all possible GnuTLS debugging
1603
# "Use a log level over 10 to enable all debugging options."
1605
gnutls.library.functions.gnutls_global_set_log_level(11)
1607
@gnutls.library.types.gnutls_log_func
1608
def debug_gnutls(level, string):
1609
logger.debug(u"GnuTLS: %s", string[:-1])
1611
(gnutls.library.functions
1612
.gnutls_global_set_log_function(debug_gnutls))
1027
service = AvahiService(name = server_settings["servicename"],
1028
servicetype = "_mandos._tcp", )
1029
if server_settings["interface"]:
1030
service.interface = (if_nametoindex
1031
(server_settings["interface"]))
1614
1033
global main_loop
1615
1036
# From the Avahi example code
1616
1037
DBusGMainLoop(set_as_default=True )
1617
1038
main_loop = gobject.MainLoop()
1618
1039
bus = dbus.SystemBus()
1040
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1041
avahi.DBUS_PATH_SERVER),
1042
avahi.DBUS_INTERFACE_SERVER)
1619
1043
# End of Avahi example code
1622
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1623
bus, do_not_queue=True)
1624
except dbus.exceptions.NameExistsException, e:
1625
logger.error(unicode(e) + u", disabling D-Bus")
1627
server_settings[u"use_dbus"] = False
1628
tcp_server.use_dbus = False
1629
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1630
service = AvahiService(name = server_settings[u"servicename"],
1631
servicetype = u"_mandos._tcp",
1632
protocol = protocol, bus = bus)
1633
if server_settings["interface"]:
1634
service.interface = (if_nametoindex
1635
(str(server_settings[u"interface"])))
1045
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1637
client_class = Client
1639
client_class = functools.partial(ClientDBus, bus = bus)
1640
tcp_server.clients.update(set(
1641
client_class(name = section,
1642
config= dict(client_config.items(section)))
1643
for section in client_config.sections()))
1644
if not tcp_server.clients:
1047
clients.update(Set(Client(name = section,
1049
= dict(client_config.items(section)),
1050
use_dbus = use_dbus)
1051
for section in client_config.sections()))
1645
1053
logger.warning(u"No clients defined")