246
240
last_checked_ok: datetime.datetime(); (UTC) or None
247
241
timeout: datetime.timedelta(); How long from last_checked_ok
248
until this client is disabled
242
until this client is invalid
249
243
interval: datetime.timedelta(); How often to start a new checker
250
244
disable_hook: If set, called by disable() as disable_hook(self)
251
245
checker: subprocess.Popen(); a running checker process used
252
246
to see if the client lives.
253
247
'None' if no process is running.
254
248
checker_initiator_tag: a gobject event source tag, or None
255
disable_initiator_tag: - '' -
249
disable_initiator_tag: - '' -
256
250
checker_callback_tag: - '' -
257
251
checker_command: string; External command which is run to check if
258
252
client lives. %() expansions are done at
259
253
runtime with vars(self) as dict, so that for
260
254
instance %(name)s can be used in the command.
261
255
current_checker_command: string; current running checker_command
262
approved_delay: datetime.timedelta(); Time to wait for approval
263
_approved: bool(); 'None' if not yet approved/disapproved
264
approved_duration: datetime.timedelta(); Duration of one approval
268
def _timedelta_to_milliseconds(td):
269
"Convert a datetime.timedelta() to milliseconds"
270
return ((td.days * 24 * 60 * 60 * 1000)
271
+ (td.seconds * 1000)
272
+ (td.microseconds // 1000))
259
def _datetime_to_milliseconds(dt):
260
"Convert a datetime.datetime() to milliseconds"
261
return ((dt.days * 24 * 60 * 60 * 1000)
262
+ (dt.seconds * 1000)
263
+ (dt.microseconds // 1000))
274
265
def timeout_milliseconds(self):
275
266
"Return the 'timeout' attribute in milliseconds"
276
return self._timedelta_to_milliseconds(self.timeout)
267
return self._datetime_to_milliseconds(self.timeout)
278
269
def interval_milliseconds(self):
279
270
"Return the 'interval' attribute in milliseconds"
280
return self._timedelta_to_milliseconds(self.interval)
282
def approved_delay_milliseconds(self):
283
return self._timedelta_to_milliseconds(self.approved_delay)
271
return self._datetime_to_milliseconds(self.interval)
285
273
def __init__(self, name = None, disable_hook=None, config=None):
286
274
"""Note: the 'checker' key in 'config' sets the
322
309
self.checker_command = config[u"checker"]
323
310
self.current_checker_command = None
324
311
self.last_connect = None
325
self._approved = None
326
self.approved_by_default = config.get(u"approved_by_default",
328
self.approvals_pending = 0
329
self.approved_delay = string_to_delta(
330
config[u"approved_delay"])
331
self.approved_duration = string_to_delta(
332
config[u"approved_duration"])
333
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
335
def send_changedstate(self):
336
self.changedstate.acquire()
337
self.changedstate.notify_all()
338
self.changedstate.release()
340
313
def enable(self):
341
314
"""Start this client's checker and timeout hooks"""
342
315
if getattr(self, u"enabled", False):
343
316
# Already enabled
345
self.send_changedstate()
346
318
self.last_enabled = datetime.datetime.utcnow()
347
319
# Schedule a new checker to be started an 'interval' from now,
348
320
# and every interval from then on.
349
321
self.checker_initiator_tag = (gobject.timeout_add
350
322
(self.interval_milliseconds(),
351
323
self.start_checker))
324
# Also start a new checker *right now*.
352
326
# Schedule a disable() when 'timeout' has passed
353
327
self.disable_initiator_tag = (gobject.timeout_add
354
328
(self.timeout_milliseconds(),
356
330
self.enabled = True
357
# Also start a new checker *right now*.
360
def disable(self, quiet=True):
361
333
"""Disable this client."""
362
334
if not getattr(self, "enabled", False):
365
self.send_changedstate()
367
logger.info(u"Disabling client %s", self.name)
336
logger.info(u"Disabling client %s", self.name)
368
337
if getattr(self, u"disable_initiator_tag", False):
369
338
gobject.source_remove(self.disable_initiator_tag)
370
339
self.disable_initiator_tag = None
494
458
logger.debug(u"Stopping checker for %(name)s", vars(self))
496
460
os.kill(self.checker.pid, signal.SIGTERM)
498
462
#if self.checker.poll() is None:
499
463
# os.kill(self.checker.pid, signal.SIGKILL)
500
464
except OSError, error:
501
465
if error.errno != errno.ESRCH: # No such process
503
467
self.checker = None
505
def dbus_service_property(dbus_interface, signature=u"v",
506
access=u"readwrite", byte_arrays=False):
507
"""Decorators for marking methods of a DBusObjectWithProperties to
508
become properties on the D-Bus.
510
The decorated method will be called with no arguments by "Get"
511
and with one argument by "Set".
513
The parameters, where they are supported, are the same as
514
dbus.service.method, except there is only "signature", since the
515
type from Get() and the type sent to Set() is the same.
517
# Encoding deeply encoded byte arrays is not supported yet by the
518
# "Set" method, so we fail early here:
519
if byte_arrays and signature != u"ay":
520
raise ValueError(u"Byte arrays not supported for non-'ay'"
521
u" signature %r" % signature)
523
func._dbus_is_property = True
524
func._dbus_interface = dbus_interface
525
func._dbus_signature = signature
526
func._dbus_access = access
527
func._dbus_name = func.__name__
528
if func._dbus_name.endswith(u"_dbus_property"):
529
func._dbus_name = func._dbus_name[:-14]
530
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
535
class DBusPropertyException(dbus.exceptions.DBusException):
536
"""A base class for D-Bus property-related exceptions
538
def __unicode__(self):
539
return unicode(str(self))
542
class DBusPropertyAccessException(DBusPropertyException):
543
"""A property's access permissions disallows an operation.
548
class DBusPropertyNotFound(DBusPropertyException):
549
"""An attempt was made to access a non-existing property.
554
class DBusObjectWithProperties(dbus.service.Object):
555
"""A D-Bus object with properties.
557
Classes inheriting from this can use the dbus_service_property
558
decorator to expose methods as D-Bus properties. It exposes the
559
standard Get(), Set(), and GetAll() methods on the D-Bus.
563
def _is_dbus_property(obj):
564
return getattr(obj, u"_dbus_is_property", False)
566
def _get_all_dbus_properties(self):
567
"""Returns a generator of (name, attribute) pairs
569
return ((prop._dbus_name, prop)
571
inspect.getmembers(self, self._is_dbus_property))
573
def _get_dbus_property(self, interface_name, property_name):
574
"""Returns a bound method if one exists which is a D-Bus
575
property with the specified name and interface.
577
for name in (property_name,
578
property_name + u"_dbus_property"):
579
prop = getattr(self, name, None)
581
or not self._is_dbus_property(prop)
582
or prop._dbus_name != property_name
583
or (interface_name and prop._dbus_interface
584
and interface_name != prop._dbus_interface)):
588
raise DBusPropertyNotFound(self.dbus_object_path + u":"
589
+ interface_name + u"."
592
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
594
def Get(self, interface_name, property_name):
595
"""Standard D-Bus property Get() method, see D-Bus standard.
597
prop = self._get_dbus_property(interface_name, property_name)
598
if prop._dbus_access == u"write":
599
raise DBusPropertyAccessException(property_name)
601
if not hasattr(value, u"variant_level"):
603
return type(value)(value, variant_level=value.variant_level+1)
605
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
606
def Set(self, interface_name, property_name, value):
607
"""Standard D-Bus property Set() method, see D-Bus standard.
609
prop = self._get_dbus_property(interface_name, property_name)
610
if prop._dbus_access == u"read":
611
raise DBusPropertyAccessException(property_name)
612
if prop._dbus_get_args_options[u"byte_arrays"]:
613
# The byte_arrays option is not supported yet on
614
# signatures other than "ay".
615
if prop._dbus_signature != u"ay":
617
value = dbus.ByteArray(''.join(unichr(byte)
621
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
622
out_signature=u"a{sv}")
623
def GetAll(self, interface_name):
624
"""Standard D-Bus property GetAll() method, see D-Bus
627
Note: Will not include properties with access="write".
630
for name, prop in self._get_all_dbus_properties():
632
and interface_name != prop._dbus_interface):
633
# Interface non-empty but did not match
635
# Ignore write-only properties
636
if prop._dbus_access == u"write":
639
if not hasattr(value, u"variant_level"):
642
all[name] = type(value)(value, variant_level=
643
value.variant_level+1)
644
return dbus.Dictionary(all, signature=u"sv")
646
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
648
path_keyword='object_path',
649
connection_keyword='connection')
650
def Introspect(self, object_path, connection):
651
"""Standard D-Bus method, overloaded to insert property tags.
653
xmlstring = dbus.service.Object.Introspect(self, object_path,
656
document = xml.dom.minidom.parseString(xmlstring)
657
def make_tag(document, name, prop):
658
e = document.createElement(u"property")
659
e.setAttribute(u"name", name)
660
e.setAttribute(u"type", prop._dbus_signature)
661
e.setAttribute(u"access", prop._dbus_access)
663
for if_tag in document.getElementsByTagName(u"interface"):
664
for tag in (make_tag(document, name, prop)
666
in self._get_all_dbus_properties()
667
if prop._dbus_interface
668
== if_tag.getAttribute(u"name")):
669
if_tag.appendChild(tag)
670
# Add the names to the return values for the
671
# "org.freedesktop.DBus.Properties" methods
672
if (if_tag.getAttribute(u"name")
673
== u"org.freedesktop.DBus.Properties"):
674
for cn in if_tag.getElementsByTagName(u"method"):
675
if cn.getAttribute(u"name") == u"Get":
676
for arg in cn.getElementsByTagName(u"arg"):
677
if (arg.getAttribute(u"direction")
679
arg.setAttribute(u"name", u"value")
680
elif cn.getAttribute(u"name") == u"GetAll":
681
for arg in cn.getElementsByTagName(u"arg"):
682
if (arg.getAttribute(u"direction")
684
arg.setAttribute(u"name", u"props")
685
xmlstring = document.toxml(u"utf-8")
687
except (AttributeError, xml.dom.DOMException,
688
xml.parsers.expat.ExpatError), error:
689
logger.error(u"Failed to override Introspection method",
694
class ClientDBus(Client, DBusObjectWithProperties):
469
def still_valid(self):
470
"""Has the timeout not yet passed for this client?"""
471
if not getattr(self, u"enabled", False):
473
now = datetime.datetime.utcnow()
474
if self.last_checked_ok is None:
475
return now < (self.created + self.timeout)
477
return now < (self.last_checked_ok + self.timeout)
480
class ClientDBus(Client, dbus.service.Object):
695
481
"""A Client class using D-Bus
614
# GetAllProperties - method
615
@dbus.service.method(_interface, out_signature=u"a{sv}")
616
def GetAllProperties(self):
618
return dbus.Dictionary({
619
dbus.String(u"name"):
620
dbus.String(self.name, variant_level=1),
621
dbus.String(u"fingerprint"):
622
dbus.String(self.fingerprint, variant_level=1),
623
dbus.String(u"host"):
624
dbus.String(self.host, variant_level=1),
625
dbus.String(u"created"):
626
self._datetime_to_dbus(self.created,
628
dbus.String(u"last_enabled"):
629
(self._datetime_to_dbus(self.last_enabled,
631
if self.last_enabled is not None
632
else dbus.Boolean(False, variant_level=1)),
633
dbus.String(u"enabled"):
634
dbus.Boolean(self.enabled, variant_level=1),
635
dbus.String(u"last_checked_ok"):
636
(self._datetime_to_dbus(self.last_checked_ok,
638
if self.last_checked_ok is not None
639
else dbus.Boolean (False, variant_level=1)),
640
dbus.String(u"timeout"):
641
dbus.UInt64(self.timeout_milliseconds(),
643
dbus.String(u"interval"):
644
dbus.UInt64(self.interval_milliseconds(),
646
dbus.String(u"checker"):
647
dbus.String(self.checker_command,
649
dbus.String(u"checker_running"):
650
dbus.Boolean(self.checker is not None,
652
dbus.String(u"object_path"):
653
dbus.ObjectPath(self.dbus_object_path,
657
# IsStillValid - method
658
@dbus.service.method(_interface, out_signature=u"b")
659
def IsStillValid(self):
660
return self.still_valid()
855
662
# PropertyChanged - signal
856
663
@dbus.service.signal(_interface, signature=u"sv")
857
664
def PropertyChanged(self, property, value):
862
# XXXTEDDY Is sent after succesfull transfer of secret from mandos-server to mandos-client
668
# ReceivedSecret - signal
863
669
@dbus.service.signal(_interface)
670
def ReceivedSecret(self):
868
674
# Rejected - signal
869
@dbus.service.signal(_interface, signature=u"s")
870
def Rejected(self, reason):
874
# NeedApproval - signal
875
@dbus.service.signal(_interface, signature=u"db")
876
def NeedApproval(self, timeout, default):
883
@dbus.service.method(_interface, in_signature=u"b")
884
def Approve(self, value):
888
@dbus.service.method(_interface)
890
return self.checked_ok()
675
@dbus.service.signal(_interface)
680
# SetChecker - method
681
@dbus.service.method(_interface, in_signature=u"s")
682
def SetChecker(self, checker):
683
"D-Bus setter method"
684
self.checker_command = checker
686
self.PropertyChanged(dbus.String(u"checker"),
687
dbus.String(self.checker_command,
691
@dbus.service.method(_interface, in_signature=u"s")
692
def SetHost(self, host):
693
"D-Bus setter method"
696
self.PropertyChanged(dbus.String(u"host"),
697
dbus.String(self.host, variant_level=1))
699
# SetInterval - method
700
@dbus.service.method(_interface, in_signature=u"t")
701
def SetInterval(self, milliseconds):
702
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
704
self.PropertyChanged(dbus.String(u"interval"),
705
(dbus.UInt64(self.interval_milliseconds(),
709
@dbus.service.method(_interface, in_signature=u"ay",
711
def SetSecret(self, secret):
712
"D-Bus setter method"
713
self.secret = str(secret)
715
# SetTimeout - method
716
@dbus.service.method(_interface, in_signature=u"t")
717
def SetTimeout(self, milliseconds):
718
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
720
self.PropertyChanged(dbus.String(u"timeout"),
721
(dbus.UInt64(self.timeout_milliseconds(),
892
724
# Enable - method
893
725
@dbus.service.method(_interface)
912
744
def StopChecker(self):
913
745
self.stop_checker()
917
# approved_pending - property
918
@dbus_service_property(_interface, signature=u"b", access=u"read")
919
def approved_pending_dbus_property(self):
920
return dbus.Boolean(bool(self.approvals_pending))
922
# approved_by_default - property
923
@dbus_service_property(_interface, signature=u"b",
925
def approved_by_default_dbus_property(self):
926
return dbus.Boolean(self.approved_by_default)
928
# approved_delay - property
929
@dbus_service_property(_interface, signature=u"t",
931
def approved_delay_dbus_property(self):
932
return dbus.UInt64(self.approved_delay_milliseconds())
934
# approved_duration - property
935
@dbus_service_property(_interface, signature=u"t",
937
def approved_duration_dbus_property(self):
938
return dbus.UInt64(self._timedelta_to_milliseconds(
939
self.approved_duration))
942
@dbus_service_property(_interface, signature=u"s", access=u"read")
943
def name_dbus_property(self):
944
return dbus.String(self.name)
946
# fingerprint - property
947
@dbus_service_property(_interface, signature=u"s", access=u"read")
948
def fingerprint_dbus_property(self):
949
return dbus.String(self.fingerprint)
952
@dbus_service_property(_interface, signature=u"s",
954
def host_dbus_property(self, value=None):
955
if value is None: # get
956
return dbus.String(self.host)
959
self.PropertyChanged(dbus.String(u"host"),
960
dbus.String(value, variant_level=1))
963
@dbus_service_property(_interface, signature=u"s", access=u"read")
964
def created_dbus_property(self):
965
return dbus.String(self._datetime_to_dbus(self.created))
967
# last_enabled - property
968
@dbus_service_property(_interface, signature=u"s", access=u"read")
969
def last_enabled_dbus_property(self):
970
if self.last_enabled is None:
971
return dbus.String(u"")
972
return dbus.String(self._datetime_to_dbus(self.last_enabled))
975
@dbus_service_property(_interface, signature=u"b",
977
def enabled_dbus_property(self, value=None):
978
if value is None: # get
979
return dbus.Boolean(self.enabled)
985
# last_checked_ok - property
986
@dbus_service_property(_interface, signature=u"s",
988
def last_checked_ok_dbus_property(self, value=None):
989
if value is not None:
992
if self.last_checked_ok is None:
993
return dbus.String(u"")
994
return dbus.String(self._datetime_to_dbus(self
998
@dbus_service_property(_interface, signature=u"t",
1000
def timeout_dbus_property(self, value=None):
1001
if value is None: # get
1002
return dbus.UInt64(self.timeout_milliseconds())
1003
self.timeout = datetime.timedelta(0, 0, 0, value)
1005
self.PropertyChanged(dbus.String(u"timeout"),
1006
dbus.UInt64(value, variant_level=1))
1007
if getattr(self, u"disable_initiator_tag", None) is None:
1009
# Reschedule timeout
1010
gobject.source_remove(self.disable_initiator_tag)
1011
self.disable_initiator_tag = None
1012
time_to_die = (self.
1013
_timedelta_to_milliseconds((self
1018
if time_to_die <= 0:
1019
# The timeout has passed
1022
self.disable_initiator_tag = (gobject.timeout_add
1023
(time_to_die, self.disable))
1025
# interval - property
1026
@dbus_service_property(_interface, signature=u"t",
1027
access=u"readwrite")
1028
def interval_dbus_property(self, value=None):
1029
if value is None: # get
1030
return dbus.UInt64(self.interval_milliseconds())
1031
self.interval = datetime.timedelta(0, 0, 0, value)
1033
self.PropertyChanged(dbus.String(u"interval"),
1034
dbus.UInt64(value, variant_level=1))
1035
if getattr(self, u"checker_initiator_tag", None) is None:
1037
# Reschedule checker run
1038
gobject.source_remove(self.checker_initiator_tag)
1039
self.checker_initiator_tag = (gobject.timeout_add
1040
(value, self.start_checker))
1041
self.start_checker() # Start one now, too
1043
# checker - property
1044
@dbus_service_property(_interface, signature=u"s",
1045
access=u"readwrite")
1046
def checker_dbus_property(self, value=None):
1047
if value is None: # get
1048
return dbus.String(self.checker_command)
1049
self.checker_command = value
1051
self.PropertyChanged(dbus.String(u"checker"),
1052
dbus.String(self.checker_command,
1055
# checker_running - property
1056
@dbus_service_property(_interface, signature=u"b",
1057
access=u"readwrite")
1058
def checker_running_dbus_property(self, value=None):
1059
if value is None: # get
1060
return dbus.Boolean(self.checker is not None)
1062
self.start_checker()
1066
# object_path - property
1067
@dbus_service_property(_interface, signature=u"o", access=u"read")
1068
def object_path_dbus_property(self):
1069
return self.dbus_object_path # is already a dbus.ObjectPath
1072
@dbus_service_property(_interface, signature=u"ay",
1073
access=u"write", byte_arrays=True)
1074
def secret_dbus_property(self, value):
1075
self.secret = str(value)
1080
class ProxyClient(object):
1081
def __init__(self, child_pipe, fpr, address):
1082
self._pipe = child_pipe
1083
self._pipe.send(('init', fpr, address))
1084
if not self._pipe.recv():
1087
def __getattribute__(self, name):
1088
if(name == '_pipe'):
1089
return super(ProxyClient, self).__getattribute__(name)
1090
self._pipe.send(('getattr', name))
1091
data = self._pipe.recv()
1092
if data[0] == 'data':
1094
if data[0] == 'function':
1095
def func(*args, **kwargs):
1096
self._pipe.send(('funcall', name, args, kwargs))
1097
return self._pipe.recv()[1]
1100
def __setattr__(self, name, value):
1101
if(name == '_pipe'):
1102
return super(ProxyClient, self).__setattr__(name, value)
1103
self._pipe.send(('setattr', name, value))
1106
750
class ClientHandler(socketserver.BaseRequestHandler, object):
1107
751
"""A class to handle client connections.
1110
754
Note: This will run in its own forked process."""
1112
756
def handle(self):
1113
with contextlib.closing(self.server.child_pipe) as child_pipe:
1114
logger.info(u"TCP connection from: %s",
1115
unicode(self.client_address))
1116
logger.debug(u"Pipe FD: %d",
1117
self.server.child_pipe.fileno())
757
logger.info(u"TCP connection from: %s",
758
unicode(self.client_address))
759
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
760
# Open IPC pipe to parent process
761
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
1119
762
session = (gnutls.connection
1120
763
.ClientSession(self.request,
1121
764
gnutls.connection
1122
765
.X509Credentials()))
767
line = self.request.makefile().readline()
768
logger.debug(u"Protocol version: %r", line)
770
if int(line.strip().split()[0]) > 1:
772
except (ValueError, IndexError, RuntimeError), error:
773
logger.error(u"Unknown protocol version: %s", error)
1124
776
# Note: gnutls.connection.X509Credentials is really a
1125
777
# generic GnuTLS certificate credentials object so long as
1126
778
# no X.509 keys are added to it. Therefore, we can use it
1127
779
# here despite using OpenPGP certificates.
1129
781
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1130
782
# u"+AES-256-CBC", u"+SHA1",
1131
783
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1158
798
# established. Just abandon the request.
1160
800
logger.debug(u"Handshake succeeded")
1162
approval_required = False
1165
fpr = self.fingerprint(self.peer_certificate
1167
except (TypeError, gnutls.errors.GNUTLSError), error:
1168
logger.warning(u"Bad certificate: %s", error)
1170
logger.debug(u"Fingerprint: %s", fpr)
1173
client = ProxyClient(child_pipe, fpr,
1174
self.client_address)
1178
if client.approved_delay:
1179
delay = client.approved_delay
1180
client.approvals_pending += 1
1181
approval_required = True
1184
if not client.enabled:
1185
logger.warning(u"Client %s is disabled",
1187
if self.server.use_dbus:
1189
client.Rejected("Disabled")
1192
if client._approved or not client.approved_delay:
1193
#We are approved or approval is disabled
1195
elif client._approved is None:
1196
logger.info(u"Client %s need approval",
1198
if self.server.use_dbus:
1200
client.NeedApproval(
1201
client.approved_delay_milliseconds(),
1202
client.approved_by_default)
1204
logger.warning(u"Client %s was not approved",
1206
if self.server.use_dbus:
1208
client.Rejected("Disapproved")
1211
#wait until timeout or approved
1212
#x = float(client._timedelta_to_milliseconds(delay))
1213
time = datetime.datetime.now()
1214
client.changedstate.acquire()
1215
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1216
client.changedstate.release()
1217
time2 = datetime.datetime.now()
1218
if (time2 - time) >= delay:
1219
if not client.approved_by_default:
1220
logger.warning("Client %s timed out while"
1221
" waiting for approval",
1223
if self.server.use_dbus:
1225
client.Rejected("Time out")
1230
delay -= time2 - time
1233
while sent_size < len(client.secret):
1234
# XXX handle session exception
1235
sent = session.send(client.secret[sent_size:])
1236
logger.debug(u"Sent: %d, remaining: %d",
1237
sent, len(client.secret)
1238
- (sent_size + sent))
1241
logger.info(u"Sending secret to %s", client.name)
1242
# bump the timeout as if seen
1244
if self.server.use_dbus:
802
fpr = self.fingerprint(self.peer_certificate(session))
803
except (TypeError, gnutls.errors.GNUTLSError), error:
804
logger.warning(u"Bad certificate: %s", error)
807
logger.debug(u"Fingerprint: %s", fpr)
1249
if approval_required:
1250
client.approvals_pending -= 1
809
for c in self.server.clients:
810
if c.fingerprint == fpr:
814
ipc.write(u"NOTFOUND %s\n" % fpr)
817
# Have to check if client.still_valid(), since it is
818
# possible that the client timed out while establishing
819
# the GnuTLS session.
820
if not client.still_valid():
821
ipc.write(u"INVALID %s\n" % client.name)
824
ipc.write(u"SENDING %s\n" % client.name)
826
while sent_size < len(client.secret):
827
sent = session.send(client.secret[sent_size:])
828
logger.debug(u"Sent: %d, remaining: %d",
829
sent, len(client.secret)
830
- (sent_size + sent))
1254
835
def peer_certificate(session):
1317
class MultiprocessingMixIn(object):
1318
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1319
def sub_process_main(self, request, address):
1321
self.finish_request(request, address)
1323
self.handle_error(request, address)
1324
self.close_request(request)
1326
def process_request(self, request, address):
1327
"""Start a new process to process the request."""
1328
multiprocessing.Process(target = self.sub_process_main,
1329
args = (request, address)).start()
1331
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1332
""" adds a pipe to the MixIn """
898
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
899
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1333
900
def process_request(self, request, client_address):
1334
901
"""Overrides and wraps the original process_request().
1336
This function creates a new pipe in self.pipe
903
This function creates a new pipe in self.pipe
1338
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1340
super(MultiprocessingMixInWithPipe,
905
self.pipe = os.pipe()
906
super(ForkingMixInWithPipe,
1341
907
self).process_request(request, client_address)
1342
self.child_pipe.close()
1343
self.add_pipe(parent_pipe)
1345
def add_pipe(self, parent_pipe):
908
os.close(self.pipe[1]) # close write end
909
self.add_pipe(self.pipe[0])
910
def add_pipe(self, pipe):
1346
911
"""Dummy function; override as necessary"""
1349
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
915
class IPv6_TCPServer(ForkingMixInWithPipe,
1350
916
socketserver.TCPServer, object):
1351
917
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1462
1027
if cond & condition)
1463
1028
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1464
1029
conditions_string)
1466
# XXXTEDDY error or the other end of multiprocessing.Pipe has closed
1467
if condition & gobject.IO_HUP or condition & gobject.IO_ERR:
1470
# Read a request from the child
1471
request = parent_pipe.recv()
1472
logger.debug(u"IPC request: %s", repr(request))
1473
command = request[0]
1475
if command == 'init':
1477
address = request[2]
1479
for c in self.clients:
1480
if c.fingerprint == fpr:
1484
logger.warning(u"Client not found for fingerprint: %s, ad"
1485
u"dress: %s", fpr, address)
1488
mandos_dbus_service.ClientNotFound(fpr, address)
1489
parent_pipe.send(False)
1492
gobject.io_add_watch(parent_pipe.fileno(),
1493
gobject.IO_IN | gobject.IO_HUP,
1494
functools.partial(self.handle_ipc,
1495
parent_pipe = parent_pipe,
1496
client_object = client))
1497
parent_pipe.send(True)
1498
# remove the old hook in favor of the new above hook on same fileno
1500
if command == 'funcall':
1501
funcname = request[1]
1505
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1507
if command == 'getattr':
1508
attrname = request[1]
1509
if callable(client_object.__getattribute__(attrname)):
1510
parent_pipe.send(('function',))
1512
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1514
if command == 'setattr':
1515
attrname = request[1]
1517
setattr(client_object, attrname, value)
1031
# Turn the pipe file descriptor into a Python file object
1032
if source not in file_objects:
1033
file_objects[source] = os.fdopen(source, u"r", 1)
1035
# Read a line from the file object
1036
cmdline = file_objects[source].readline()
1037
if not cmdline: # Empty line means end of file
1038
# close the IPC pipe
1039
file_objects[source].close()
1040
del file_objects[source]
1042
# Stop calling this function
1045
logger.debug(u"IPC command: %r", cmdline)
1047
# Parse and act on command
1048
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1050
if cmd == u"NOTFOUND":
1051
logger.warning(u"Client not found for fingerprint: %s",
1055
mandos_dbus_service.ClientNotFound(args)
1056
elif cmd == u"INVALID":
1057
for client in self.clients:
1058
if client.name == args:
1059
logger.warning(u"Client %s is invalid", args)
1065
logger.error(u"Unknown client %s is invalid", args)
1066
elif cmd == u"SENDING":
1067
for client in self.clients:
1068
if client.name == args:
1069
logger.info(u"Sending secret to %s", client.name)
1073
client.ReceivedSecret()
1076
logger.error(u"Sending secret to unknown client %s",
1079
logger.error(u"Unknown IPC command: %r", cmdline)
1081
# Keep calling this function