423
494
if self.checker_callback_tag:
424
495
gobject.source_remove(self.checker_callback_tag)
425
496
self.checker_callback_tag = None
426
if getattr(self, "checker", None) is None:
497
if getattr(self, u"checker", None) is None:
428
499
logger.debug(u"Stopping checker for %(name)s", vars(self))
430
501
os.kill(self.checker.pid, signal.SIGTERM)
432
503
#if self.checker.poll() is None:
433
504
# os.kill(self.checker.pid, signal.SIGKILL)
434
505
except OSError, error:
435
506
if error.errno != errno.ESRCH: # No such process
437
508
self.checker = None
439
self.PropertyChanged(dbus.String(u"checker_running"),
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)
452
## D-Bus methods & signals
510
def dbus_service_property(dbus_interface, signature=u"v",
511
access=u"readwrite", byte_arrays=False):
512
"""Decorators for marking methods of a DBusObjectWithProperties to
513
become properties on the D-Bus.
515
The decorated method will be called with no arguments by "Get"
516
and with one argument by "Set".
518
The parameters, where they are supported, are the same as
519
dbus.service.method, except there is only "signature", since the
520
type from Get() and the type sent to Set() is the same.
522
# Encoding deeply encoded byte arrays is not supported yet by the
523
# "Set" method, so we fail early here:
524
if byte_arrays and signature != u"ay":
525
raise ValueError(u"Byte arrays not supported for non-'ay'"
526
u" signature %r" % signature)
528
func._dbus_is_property = True
529
func._dbus_interface = dbus_interface
530
func._dbus_signature = signature
531
func._dbus_access = access
532
func._dbus_name = func.__name__
533
if func._dbus_name.endswith(u"_dbus_property"):
534
func._dbus_name = func._dbus_name[:-14]
535
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
540
class DBusPropertyException(dbus.exceptions.DBusException):
541
"""A base class for D-Bus property-related exceptions
543
def __unicode__(self):
544
return unicode(str(self))
547
class DBusPropertyAccessException(DBusPropertyException):
548
"""A property's access permissions disallows an operation.
553
class DBusPropertyNotFound(DBusPropertyException):
554
"""An attempt was made to access a non-existing property.
559
class DBusObjectWithProperties(dbus.service.Object):
560
"""A D-Bus object with properties.
562
Classes inheriting from this can use the dbus_service_property
563
decorator to expose methods as D-Bus properties. It exposes the
564
standard Get(), Set(), and GetAll() methods on the D-Bus.
568
def _is_dbus_property(obj):
569
return getattr(obj, u"_dbus_is_property", False)
571
def _get_all_dbus_properties(self):
572
"""Returns a generator of (name, attribute) pairs
574
return ((prop._dbus_name, prop)
576
inspect.getmembers(self, self._is_dbus_property))
578
def _get_dbus_property(self, interface_name, property_name):
579
"""Returns a bound method if one exists which is a D-Bus
580
property with the specified name and interface.
582
for name in (property_name,
583
property_name + u"_dbus_property"):
584
prop = getattr(self, name, None)
586
or not self._is_dbus_property(prop)
587
or prop._dbus_name != property_name
588
or (interface_name and prop._dbus_interface
589
and interface_name != prop._dbus_interface)):
593
raise DBusPropertyNotFound(self.dbus_object_path + u":"
594
+ interface_name + u"."
597
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
599
def Get(self, interface_name, property_name):
600
"""Standard D-Bus property Get() method, see D-Bus standard.
602
prop = self._get_dbus_property(interface_name, property_name)
603
if prop._dbus_access == u"write":
604
raise DBusPropertyAccessException(property_name)
606
if not hasattr(value, u"variant_level"):
608
return type(value)(value, variant_level=value.variant_level+1)
610
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
611
def Set(self, interface_name, property_name, value):
612
"""Standard D-Bus property Set() method, see D-Bus standard.
614
prop = self._get_dbus_property(interface_name, property_name)
615
if prop._dbus_access == u"read":
616
raise DBusPropertyAccessException(property_name)
617
if prop._dbus_get_args_options[u"byte_arrays"]:
618
# The byte_arrays option is not supported yet on
619
# signatures other than "ay".
620
if prop._dbus_signature != u"ay":
622
value = dbus.ByteArray(''.join(unichr(byte)
626
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
627
out_signature=u"a{sv}")
628
def GetAll(self, interface_name):
629
"""Standard D-Bus property GetAll() method, see D-Bus
632
Note: Will not include properties with access="write".
635
for name, prop in self._get_all_dbus_properties():
637
and interface_name != prop._dbus_interface):
638
# Interface non-empty but did not match
640
# Ignore write-only properties
641
if prop._dbus_access == u"write":
644
if not hasattr(value, u"variant_level"):
647
all[name] = type(value)(value, variant_level=
648
value.variant_level+1)
649
return dbus.Dictionary(all, signature=u"sv")
651
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
653
path_keyword='object_path',
654
connection_keyword='connection')
655
def Introspect(self, object_path, connection):
656
"""Standard D-Bus method, overloaded to insert property tags.
658
xmlstring = dbus.service.Object.Introspect(self, object_path,
661
document = xml.dom.minidom.parseString(xmlstring)
662
def make_tag(document, name, prop):
663
e = document.createElement(u"property")
664
e.setAttribute(u"name", name)
665
e.setAttribute(u"type", prop._dbus_signature)
666
e.setAttribute(u"access", prop._dbus_access)
668
for if_tag in document.getElementsByTagName(u"interface"):
669
for tag in (make_tag(document, name, prop)
671
in self._get_all_dbus_properties()
672
if prop._dbus_interface
673
== if_tag.getAttribute(u"name")):
674
if_tag.appendChild(tag)
675
# Add the names to the return values for the
676
# "org.freedesktop.DBus.Properties" methods
677
if (if_tag.getAttribute(u"name")
678
== u"org.freedesktop.DBus.Properties"):
679
for cn in if_tag.getElementsByTagName(u"method"):
680
if cn.getAttribute(u"name") == u"Get":
681
for arg in cn.getElementsByTagName(u"arg"):
682
if (arg.getAttribute(u"direction")
684
arg.setAttribute(u"name", u"value")
685
elif cn.getAttribute(u"name") == u"GetAll":
686
for arg in cn.getElementsByTagName(u"arg"):
687
if (arg.getAttribute(u"direction")
689
arg.setAttribute(u"name", u"props")
690
xmlstring = document.toxml(u"utf-8")
692
except (AttributeError, xml.dom.DOMException,
693
xml.parsers.expat.ExpatError), error:
694
logger.error(u"Failed to override Introspection method",
699
class ClientDBus(Client, DBusObjectWithProperties):
700
"""A Client class using D-Bus
703
dbus_object_path: dbus.ObjectPath
704
bus: dbus.SystemBus()
706
# dbus.service.Object doesn't use super(), so we can't either.
708
def __init__(self, bus = None, *args, **kwargs):
709
self._approvals_pending = 0
711
Client.__init__(self, *args, **kwargs)
712
# Only now, when this client is initialized, can it show up on
714
self.dbus_object_path = (dbus.ObjectPath
716
+ self.name.replace(u".", u"_")))
717
DBusObjectWithProperties.__init__(self, self.bus,
718
self.dbus_object_path)
720
def _get_approvals_pending(self):
721
return self._approvals_pending
722
def _set_approvals_pending(self, value):
723
old_value = self._approvals_pending
724
self._approvals_pending = value
726
if (hasattr(self, "dbus_object_path")
727
and bval is not bool(old_value)):
728
dbus_bool = dbus.Boolean(bval, variant_level=1)
729
self.PropertyChanged(dbus.String(u"ApprovalPending"),
732
approvals_pending = property(_get_approvals_pending,
733
_set_approvals_pending)
734
del _get_approvals_pending, _set_approvals_pending
737
def _datetime_to_dbus(dt, variant_level=0):
738
"""Convert a UTC datetime.datetime() to a D-Bus type."""
739
return dbus.String(dt.isoformat(),
740
variant_level=variant_level)
743
oldstate = getattr(self, u"enabled", False)
744
r = Client.enable(self)
745
if oldstate != self.enabled:
747
self.PropertyChanged(dbus.String(u"Enabled"),
748
dbus.Boolean(True, variant_level=1))
749
self.PropertyChanged(
750
dbus.String(u"LastEnabled"),
751
self._datetime_to_dbus(self.last_enabled,
755
def disable(self, quiet = False):
756
oldstate = getattr(self, u"enabled", False)
757
r = Client.disable(self, quiet=quiet)
758
if not quiet and oldstate != self.enabled:
760
self.PropertyChanged(dbus.String(u"Enabled"),
761
dbus.Boolean(False, variant_level=1))
764
def __del__(self, *args, **kwargs):
766
self.remove_from_connection()
769
if hasattr(DBusObjectWithProperties, u"__del__"):
770
DBusObjectWithProperties.__del__(self, *args, **kwargs)
771
Client.__del__(self, *args, **kwargs)
773
def checker_callback(self, pid, condition, command,
775
self.checker_callback_tag = None
778
self.PropertyChanged(dbus.String(u"CheckerRunning"),
779
dbus.Boolean(False, variant_level=1))
780
if os.WIFEXITED(condition):
781
exitstatus = os.WEXITSTATUS(condition)
783
self.CheckerCompleted(dbus.Int16(exitstatus),
784
dbus.Int64(condition),
785
dbus.String(command))
788
self.CheckerCompleted(dbus.Int16(-1),
789
dbus.Int64(condition),
790
dbus.String(command))
792
return Client.checker_callback(self, pid, condition, command,
795
def checked_ok(self, *args, **kwargs):
796
r = Client.checked_ok(self, *args, **kwargs)
798
self.PropertyChanged(
799
dbus.String(u"LastCheckedOK"),
800
(self._datetime_to_dbus(self.last_checked_ok,
804
def start_checker(self, *args, **kwargs):
805
old_checker = self.checker
806
if self.checker is not None:
807
old_checker_pid = self.checker.pid
809
old_checker_pid = None
810
r = Client.start_checker(self, *args, **kwargs)
811
# Only if new checker process was started
812
if (self.checker is not None
813
and old_checker_pid != self.checker.pid):
815
self.CheckerStarted(self.current_checker_command)
816
self.PropertyChanged(
817
dbus.String(u"CheckerRunning"),
818
dbus.Boolean(True, variant_level=1))
821
def stop_checker(self, *args, **kwargs):
822
old_checker = getattr(self, u"checker", None)
823
r = Client.stop_checker(self, *args, **kwargs)
824
if (old_checker is not None
825
and getattr(self, u"checker", None) is None):
826
self.PropertyChanged(dbus.String(u"CheckerRunning"),
827
dbus.Boolean(False, variant_level=1))
830
def _reset_approved(self):
831
self._approved = None
834
def approve(self, value=True):
835
self.send_changedstate()
836
self._approved = value
837
gobject.timeout_add(self._timedelta_to_milliseconds
838
(self.approval_duration),
839
self._reset_approved)
842
## D-Bus methods, signals & properties
453
843
_interface = u"se.bsnet.fukt.Mandos.Client"
456
CheckedOK = dbus.service.method(_interface)(checked_ok)
457
CheckedOK.__name__ = "CheckedOK"
459
847
# CheckerCompleted - signal
460
@dbus.service.signal(_interface, signature="nxs")
848
@dbus.service.signal(_interface, signature=u"nxs")
461
849
def CheckerCompleted(self, exitcode, waitstatus, command):
465
853
# CheckerStarted - signal
466
@dbus.service.signal(_interface, signature="s")
854
@dbus.service.signal(_interface, signature=u"s")
467
855
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"
518
859
# PropertyChanged - signal
519
@dbus.service.signal(_interface, signature="sv")
860
@dbus.service.signal(_interface, signature=u"sv")
520
861
def PropertyChanged(self, property, value):
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(),
866
@dbus.service.signal(_interface)
869
Is sent after a successful transfer of secret from the Mandos
870
server to mandos-client
875
@dbus.service.signal(_interface, signature=u"s")
876
def Rejected(self, reason):
880
# NeedApproval - signal
881
@dbus.service.signal(_interface, signature=u"tb")
882
def NeedApproval(self, timeout, default):
889
@dbus.service.method(_interface, in_signature=u"b")
890
def Approve(self, value):
894
@dbus.service.method(_interface)
896
return self.checked_ok()
568
898
# Enable - method
569
Enable = dbus.service.method(_interface)(enable)
570
Enable.__name__ = "Enable"
899
@dbus.service.method(_interface)
572
904
# StartChecker - method
573
905
@dbus.service.method(_interface)
584
916
# StopChecker - method
585
StopChecker = dbus.service.method(_interface)(stop_checker)
586
StopChecker.__name__ = "StopChecker"
917
@dbus.service.method(_interface)
918
def StopChecker(self):
923
# ApprovalPending - property
924
@dbus_service_property(_interface, signature=u"b", access=u"read")
925
def ApprovalPending_dbus_property(self):
926
return dbus.Boolean(bool(self.approvals_pending))
928
# ApprovedByDefault - property
929
@dbus_service_property(_interface, signature=u"b",
931
def ApprovedByDefault_dbus_property(self, value=None):
932
if value is None: # get
933
return dbus.Boolean(self.approved_by_default)
934
self.approved_by_default = bool(value)
936
self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
937
dbus.Boolean(value, variant_level=1))
939
# ApprovalDelay - property
940
@dbus_service_property(_interface, signature=u"t",
942
def ApprovalDelay_dbus_property(self, value=None):
943
if value is None: # get
944
return dbus.UInt64(self.approval_delay_milliseconds())
945
self.approval_delay = datetime.timedelta(0, 0, 0, value)
947
self.PropertyChanged(dbus.String(u"ApprovalDelay"),
948
dbus.UInt64(value, variant_level=1))
950
# ApprovalDuration - property
951
@dbus_service_property(_interface, signature=u"t",
953
def ApprovalDuration_dbus_property(self, value=None):
954
if value is None: # get
955
return dbus.UInt64(self._timedelta_to_milliseconds(
956
self.approval_duration))
957
self.approval_duration = datetime.timedelta(0, 0, 0, value)
959
self.PropertyChanged(dbus.String(u"ApprovalDuration"),
960
dbus.UInt64(value, variant_level=1))
963
@dbus_service_property(_interface, signature=u"s", access=u"read")
964
def Name_dbus_property(self):
965
return dbus.String(self.name)
967
# Fingerprint - property
968
@dbus_service_property(_interface, signature=u"s", access=u"read")
969
def Fingerprint_dbus_property(self):
970
return dbus.String(self.fingerprint)
973
@dbus_service_property(_interface, signature=u"s",
975
def Host_dbus_property(self, value=None):
976
if value is None: # get
977
return dbus.String(self.host)
980
self.PropertyChanged(dbus.String(u"Host"),
981
dbus.String(value, variant_level=1))
984
@dbus_service_property(_interface, signature=u"s", access=u"read")
985
def Created_dbus_property(self):
986
return dbus.String(self._datetime_to_dbus(self.created))
988
# LastEnabled - property
989
@dbus_service_property(_interface, signature=u"s", access=u"read")
990
def LastEnabled_dbus_property(self):
991
if self.last_enabled is None:
992
return dbus.String(u"")
993
return dbus.String(self._datetime_to_dbus(self.last_enabled))
996
@dbus_service_property(_interface, signature=u"b",
998
def Enabled_dbus_property(self, value=None):
999
if value is None: # get
1000
return dbus.Boolean(self.enabled)
1006
# LastCheckedOK - property
1007
@dbus_service_property(_interface, signature=u"s",
1008
access=u"readwrite")
1009
def LastCheckedOK_dbus_property(self, value=None):
1010
if value is not None:
1013
if self.last_checked_ok is None:
1014
return dbus.String(u"")
1015
return dbus.String(self._datetime_to_dbus(self
1018
# Timeout - property
1019
@dbus_service_property(_interface, signature=u"t",
1020
access=u"readwrite")
1021
def Timeout_dbus_property(self, value=None):
1022
if value is None: # get
1023
return dbus.UInt64(self.timeout_milliseconds())
1024
self.timeout = datetime.timedelta(0, 0, 0, value)
1026
self.PropertyChanged(dbus.String(u"Timeout"),
1027
dbus.UInt64(value, variant_level=1))
1028
if getattr(self, u"disable_initiator_tag", None) is None:
1030
# Reschedule timeout
1031
gobject.source_remove(self.disable_initiator_tag)
1032
self.disable_initiator_tag = None
1033
time_to_die = (self.
1034
_timedelta_to_milliseconds((self
1039
if time_to_die <= 0:
1040
# The timeout has passed
1043
self.disable_initiator_tag = (gobject.timeout_add
1044
(time_to_die, self.disable))
1046
# Interval - property
1047
@dbus_service_property(_interface, signature=u"t",
1048
access=u"readwrite")
1049
def Interval_dbus_property(self, value=None):
1050
if value is None: # get
1051
return dbus.UInt64(self.interval_milliseconds())
1052
self.interval = datetime.timedelta(0, 0, 0, value)
1054
self.PropertyChanged(dbus.String(u"Interval"),
1055
dbus.UInt64(value, variant_level=1))
1056
if getattr(self, u"checker_initiator_tag", None) is None:
1058
# Reschedule checker run
1059
gobject.source_remove(self.checker_initiator_tag)
1060
self.checker_initiator_tag = (gobject.timeout_add
1061
(value, self.start_checker))
1062
self.start_checker() # Start one now, too
1064
# Checker - property
1065
@dbus_service_property(_interface, signature=u"s",
1066
access=u"readwrite")
1067
def Checker_dbus_property(self, value=None):
1068
if value is None: # get
1069
return dbus.String(self.checker_command)
1070
self.checker_command = value
1072
self.PropertyChanged(dbus.String(u"Checker"),
1073
dbus.String(self.checker_command,
1076
# CheckerRunning - property
1077
@dbus_service_property(_interface, signature=u"b",
1078
access=u"readwrite")
1079
def CheckerRunning_dbus_property(self, value=None):
1080
if value is None: # get
1081
return dbus.Boolean(self.checker is not None)
1083
self.start_checker()
1087
# ObjectPath - property
1088
@dbus_service_property(_interface, signature=u"o", access=u"read")
1089
def ObjectPath_dbus_property(self):
1090
return self.dbus_object_path # is already a dbus.ObjectPath
1093
@dbus_service_property(_interface, signature=u"ay",
1094
access=u"write", byte_arrays=True)
1095
def Secret_dbus_property(self, value):
1096
self.secret = str(value)
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.
1101
class ProxyClient(object):
1102
def __init__(self, child_pipe, fpr, address):
1103
self._pipe = child_pipe
1104
self._pipe.send(('init', fpr, address))
1105
if not self._pipe.recv():
1108
def __getattribute__(self, name):
1109
if(name == '_pipe'):
1110
return super(ProxyClient, self).__getattribute__(name)
1111
self._pipe.send(('getattr', name))
1112
data = self._pipe.recv()
1113
if data[0] == 'data':
1115
if data[0] == 'function':
1116
def func(*args, **kwargs):
1117
self._pipe.send(('funcall', name, args, kwargs))
1118
return self._pipe.recv()[1]
1121
def __setattr__(self, name, value):
1122
if(name == '_pipe'):
1123
return super(ProxyClient, self).__setattr__(name, value)
1124
self._pipe.send(('setattr', name, value))
1127
class ClientHandler(socketserver.BaseRequestHandler, object):
1128
"""A class to handle client connections.
1130
Instantiated once for each connection to handle it.
655
1131
Note: This will run in its own forked process."""
657
1133
def handle(self):
658
logger.info(u"TCP connection from: %s",
659
unicode(self.client_address))
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.
1134
with contextlib.closing(self.server.child_pipe) as child_pipe:
1135
logger.info(u"TCP connection from: %s",
1136
unicode(self.client_address))
1137
logger.debug(u"Pipe FD: %d",
1138
self.server.child_pipe.fileno())
1140
session = (gnutls.connection
1141
.ClientSession(self.request,
1143
.X509Credentials()))
1145
# Note: gnutls.connection.X509Credentials is really a
1146
# generic GnuTLS certificate credentials object so long as
1147
# no X.509 keys are added to it. Therefore, we can use it
1148
# here despite using OpenPGP certificates.
1150
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1151
# u"+AES-256-CBC", u"+SHA1",
1152
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1154
# Use a fallback default, since this MUST be set.
1155
priority = self.server.gnutls_priority
1156
if priority is None:
1157
priority = u"NORMAL"
1158
(gnutls.library.functions
1159
.gnutls_priority_set_direct(session._c_object,
1162
# Start communication using the Mandos protocol
1163
# Get protocol number
1164
line = self.request.makefile().readline()
1165
logger.debug(u"Protocol version: %r", line)
1167
if int(line.strip().split()[0]) > 1:
1169
except (ValueError, IndexError, RuntimeError), error:
1170
logger.error(u"Unknown protocol version: %s", error)
1173
# Start GnuTLS connection
1176
except gnutls.errors.GNUTLSError, error:
1177
logger.warning(u"Handshake failed: %s", error)
1178
# Do not run session.bye() here: the session is not
1179
# established. Just abandon the request.
1181
logger.debug(u"Handshake succeeded")
1183
approval_required = False
1186
fpr = self.fingerprint(self.peer_certificate
1188
except (TypeError, gnutls.errors.GNUTLSError), error:
1189
logger.warning(u"Bad certificate: %s", error)
1191
logger.debug(u"Fingerprint: %s", fpr)
1194
client = ProxyClient(child_pipe, fpr,
1195
self.client_address)
1199
if client.approval_delay:
1200
delay = client.approval_delay
1201
client.approvals_pending += 1
1202
approval_required = True
1205
if not client.enabled:
1206
logger.warning(u"Client %s is disabled",
1208
if self.server.use_dbus:
1210
client.Rejected("Disabled")
1213
if client._approved or not client.approval_delay:
1214
#We are approved or approval is disabled
1216
elif client._approved is None:
1217
logger.info(u"Client %s needs approval",
1219
if self.server.use_dbus:
1221
client.NeedApproval(
1222
client.approval_delay_milliseconds(),
1223
client.approved_by_default)
1225
logger.warning(u"Client %s was not approved",
1227
if self.server.use_dbus:
1229
client.Rejected("Denied")
1232
#wait until timeout or approved
1233
#x = float(client._timedelta_to_milliseconds(delay))
1234
time = datetime.datetime.now()
1235
client.changedstate.acquire()
1236
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1237
client.changedstate.release()
1238
time2 = datetime.datetime.now()
1239
if (time2 - time) >= delay:
1240
if not client.approved_by_default:
1241
logger.warning("Client %s timed out while"
1242
" waiting for approval",
1244
if self.server.use_dbus:
1246
client.Rejected("Timed out")
1251
delay -= time2 - time
1254
while sent_size < len(client.secret):
1256
sent = session.send(client.secret[sent_size:])
1257
except (gnutls.errors.GNUTLSError), error:
1258
logger.warning("gnutls send failed")
1260
logger.debug(u"Sent: %d, remaining: %d",
1261
sent, len(client.secret)
1262
- (sent_size + sent))
1265
logger.info(u"Sending secret to %s", client.name)
1266
# bump the timeout as if seen
1268
if self.server.use_dbus:
1273
if approval_required:
1274
client.approvals_pending -= 1
1277
except (gnutls.errors.GNUTLSError), error:
1278
logger.warning("GnuTLS bye failed")
1281
def peer_certificate(session):
1282
"Return the peer's OpenPGP certificate as a bytestring"
1283
# If not an OpenPGP certificate...
1284
if (gnutls.library.functions
1285
.gnutls_certificate_type_get(session._c_object)
1286
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1287
# ...do the normal thing
1288
return session.peer_certificate
1289
list_size = ctypes.c_uint(1)
1290
cert_list = (gnutls.library.functions
1291
.gnutls_certificate_get_peers
1292
(session._c_object, ctypes.byref(list_size)))
1293
if not bool(cert_list) and list_size.value != 0:
1294
raise gnutls.errors.GNUTLSError(u"error getting peer"
1296
if list_size.value == 0:
1299
return ctypes.string_at(cert.data, cert.size)
1302
def fingerprint(openpgp):
1303
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1304
# New GnuTLS "datum" with the OpenPGP public key
1305
datum = (gnutls.library.types
1306
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1309
ctypes.c_uint(len(openpgp))))
1310
# New empty GnuTLS certificate
1311
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1312
(gnutls.library.functions
1313
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1314
# Import the OpenPGP public key into the certificate
1315
(gnutls.library.functions
1316
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1317
gnutls.library.constants
1318
.GNUTLS_OPENPGP_FMT_RAW))
1319
# Verify the self signature in the key
1320
crtverify = ctypes.c_uint()
1321
(gnutls.library.functions
1322
.gnutls_openpgp_crt_verify_self(crt, 0,
1323
ctypes.byref(crtverify)))
1324
if crtverify.value != 0:
1325
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1326
raise (gnutls.errors.CertificateSecurityError
1328
# New buffer for the fingerprint
1329
buf = ctypes.create_string_buffer(20)
1330
buf_len = ctypes.c_size_t()
1331
# Get the fingerprint from the certificate into the buffer
1332
(gnutls.library.functions
1333
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1334
ctypes.byref(buf_len)))
1335
# Deinit the certificate
1336
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1337
# Convert the buffer to a Python bytestring
1338
fpr = ctypes.string_at(buf, buf_len.value)
1339
# Convert the bytestring to hexadecimal notation
1340
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1344
class MultiprocessingMixIn(object):
1345
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1346
def sub_process_main(self, request, address):
1348
self.finish_request(request, address)
1350
self.handle_error(request, address)
1351
self.close_request(request)
1353
def process_request(self, request, address):
1354
"""Start a new process to process the request."""
1355
multiprocessing.Process(target = self.sub_process_main,
1356
args = (request, address)).start()
1358
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1359
""" adds a pipe to the MixIn """
1360
def process_request(self, request, client_address):
1361
"""Overrides and wraps the original process_request().
1363
This function creates a new pipe in self.pipe
1365
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1367
super(MultiprocessingMixInWithPipe,
1368
self).process_request(request, client_address)
1369
self.child_pipe.close()
1370
self.add_pipe(parent_pipe)
1372
def add_pipe(self, parent_pipe):
1373
"""Dummy function; override as necessary"""
1376
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1377
socketserver.TCPServer, object):
1378
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
737
settings: Server settings
738
clients: Set() of Client objects
739
1381
enabled: Boolean; whether this server is activated yet
1382
interface: None or a network interface name (string)
1383
use_ipv6: Boolean; to use IPv6 or not
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)
1385
def __init__(self, server_address, RequestHandlerClass,
1386
interface=None, use_ipv6=True):
1387
self.interface = interface
1389
self.address_family = socket.AF_INET6
1390
socketserver.TCPServer.__init__(self, server_address,
1391
RequestHandlerClass)
751
1392
def server_bind(self):
752
1393
"""This overrides the normal server_bind() function
753
1394
to bind to an interface if one was specified, and also NOT to
754
1395
bind to an address or port if they were not specified."""
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"])
1396
if self.interface is not None:
1397
if SO_BINDTODEVICE is None:
1398
logger.error(u"SO_BINDTODEVICE does not exist;"
1399
u" cannot bind to interface %s",
1403
self.socket.setsockopt(socket.SOL_SOCKET,
1407
except socket.error, error:
1408
if error[0] == errno.EPERM:
1409
logger.error(u"No permission to"
1410
u" bind to interface %s",
1412
elif error[0] == errno.ENOPROTOOPT:
1413
logger.error(u"SO_BINDTODEVICE not available;"
1414
u" cannot bind to interface %s",
769
1418
# Only bind(2) the socket if we really need to.
770
1419
if self.server_address[0] or self.server_address[1]:
771
1420
if not self.server_address[0]:
773
self.server_address = (in6addr_any,
1421
if self.address_family == socket.AF_INET6:
1422
any_address = u"::" # in6addr_any
1424
any_address = socket.INADDR_ANY
1425
self.server_address = (any_address,
774
1426
self.server_address[1])
775
1427
elif not self.server_address[1]:
776
1428
self.server_address = (self.server_address[0],
778
# if self.settings["interface"]:
1430
# if self.interface:
779
1431
# self.server_address = (self.server_address[0],
782
1434
# if_nametoindex
785
return super(IPv6_TCPServer, self).server_bind()
1436
return socketserver.TCPServer.server_bind(self)
1439
class MandosServer(IPv6_TCPServer):
1443
clients: set of Client objects
1444
gnutls_priority GnuTLS priority string
1445
use_dbus: Boolean; to emit D-Bus signals or not
1447
Assumes a gobject.MainLoop event loop.
1449
def __init__(self, server_address, RequestHandlerClass,
1450
interface=None, use_ipv6=True, clients=None,
1451
gnutls_priority=None, use_dbus=True):
1452
self.enabled = False
1453
self.clients = clients
1454
if self.clients is None:
1455
self.clients = set()
1456
self.use_dbus = use_dbus
1457
self.gnutls_priority = gnutls_priority
1458
IPv6_TCPServer.__init__(self, server_address,
1459
RequestHandlerClass,
1460
interface = interface,
1461
use_ipv6 = use_ipv6)
786
1462
def server_activate(self):
787
1463
if self.enabled:
788
return super(IPv6_TCPServer, self).server_activate()
1464
return socketserver.TCPServer.server_activate(self)
789
1465
def enable(self):
790
1466
self.enabled = True
1467
def add_pipe(self, parent_pipe):
1468
# Call "handle_ipc" for both data and EOF events
1469
gobject.io_add_watch(parent_pipe.fileno(),
1470
gobject.IO_IN | gobject.IO_HUP,
1471
functools.partial(self.handle_ipc,
1472
parent_pipe = parent_pipe))
1474
def handle_ipc(self, source, condition, parent_pipe=None,
1475
client_object=None):
1477
gobject.IO_IN: u"IN", # There is data to read.
1478
gobject.IO_OUT: u"OUT", # Data can be written (without
1480
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1481
gobject.IO_ERR: u"ERR", # Error condition.
1482
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1483
# broken, usually for pipes and
1486
conditions_string = ' | '.join(name
1488
condition_names.iteritems()
1489
if cond & condition)
1490
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1493
# error or the other end of multiprocessing.Pipe has closed
1494
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1497
# Read a request from the child
1498
request = parent_pipe.recv()
1499
logger.debug(u"IPC request: %s", repr(request))
1500
command = request[0]
1502
if command == 'init':
1504
address = request[2]
1506
for c in self.clients:
1507
if c.fingerprint == fpr:
1511
logger.warning(u"Client not found for fingerprint: %s, ad"
1512
u"dress: %s", fpr, address)
1515
mandos_dbus_service.ClientNotFound(fpr, address)
1516
parent_pipe.send(False)
1519
gobject.io_add_watch(parent_pipe.fileno(),
1520
gobject.IO_IN | gobject.IO_HUP,
1521
functools.partial(self.handle_ipc,
1522
parent_pipe = parent_pipe,
1523
client_object = client))
1524
parent_pipe.send(True)
1525
# remove the old hook in favor of the new above hook on same fileno
1527
if command == 'funcall':
1528
funcname = request[1]
1532
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1534
if command == 'getattr':
1535
attrname = request[1]
1536
if callable(client_object.__getattribute__(attrname)):
1537
parent_pipe.send(('function',))
1539
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1541
if command == 'setattr':
1542
attrname = request[1]
1544
setattr(client_object, attrname, value)
793
1549
def string_to_delta(interval):
794
1550
"""Parse a string and return a datetime.timedelta
796
>>> string_to_delta('7d')
1552
>>> string_to_delta(u'7d')
797
1553
datetime.timedelta(7)
798
>>> string_to_delta('60s')
1554
>>> string_to_delta(u'60s')
799
1555
datetime.timedelta(0, 60)
800
>>> string_to_delta('60m')
1556
>>> string_to_delta(u'60m')
801
1557
datetime.timedelta(0, 3600)
802
>>> string_to_delta('24h')
1558
>>> string_to_delta(u'24h')
803
1559
datetime.timedelta(1)
804
1560
>>> string_to_delta(u'1w')
805
1561
datetime.timedelta(7)
806
>>> string_to_delta('5m 30s')
1562
>>> string_to_delta(u'5m 30s')
807
1563
datetime.timedelta(0, 330)
809
1565
timevalue = datetime.timedelta(0)