252
240
last_checked_ok: datetime.datetime(); (UTC) or None
253
241
timeout: datetime.timedelta(); How long from last_checked_ok
254
until this client is disabled
242
until this client is invalid
255
243
interval: datetime.timedelta(); How often to start a new checker
256
244
disable_hook: If set, called by disable() as disable_hook(self)
257
245
checker: subprocess.Popen(); a running checker process used
258
246
to see if the client lives.
259
247
'None' if no process is running.
260
248
checker_initiator_tag: a gobject event source tag, or None
261
disable_initiator_tag: - '' -
249
disable_initiator_tag: - '' -
262
250
checker_callback_tag: - '' -
263
251
checker_command: string; External command which is run to check if
264
252
client lives. %() expansions are done at
265
253
runtime with vars(self) as dict, so that for
266
254
instance %(name)s can be used in the command.
267
255
current_checker_command: string; current running checker_command
268
approved_delay: datetime.timedelta(); Time to wait for approval
269
_approved: bool(); 'None' if not yet approved/disapproved
270
approved_duration: datetime.timedelta(); Duration of one approval
274
def _timedelta_to_milliseconds(td):
275
"Convert a datetime.timedelta() to milliseconds"
276
return ((td.days * 24 * 60 * 60 * 1000)
277
+ (td.seconds * 1000)
278
+ (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))
280
265
def timeout_milliseconds(self):
281
266
"Return the 'timeout' attribute in milliseconds"
282
return self._timedelta_to_milliseconds(self.timeout)
267
return self._datetime_to_milliseconds(self.timeout)
284
269
def interval_milliseconds(self):
285
270
"Return the 'interval' attribute in milliseconds"
286
return self._timedelta_to_milliseconds(self.interval)
288
def approved_delay_milliseconds(self):
289
return self._timedelta_to_milliseconds(self.approved_delay)
271
return self._datetime_to_milliseconds(self.interval)
291
273
def __init__(self, name = None, disable_hook=None, config=None):
292
274
"""Note: the 'checker' key in 'config' sets the
327
309
self.checker_command = config[u"checker"]
328
310
self.current_checker_command = None
329
311
self.last_connect = None
330
self._approved = None
331
self.approved_by_default = config.get(u"approved_by_default",
333
self.approvals_pending = 0
334
self.approved_delay = string_to_delta(
335
config[u"approved_delay"])
336
self.approved_duration = string_to_delta(
337
config[u"approved_duration"])
338
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
340
def send_changedstate(self):
341
self.changedstate.acquire()
342
self.changedstate.notify_all()
343
self.changedstate.release()
345
313
def enable(self):
346
314
"""Start this client's checker and timeout hooks"""
347
315
if getattr(self, u"enabled", False):
348
316
# Already enabled
350
self.send_changedstate()
351
318
self.last_enabled = datetime.datetime.utcnow()
352
319
# Schedule a new checker to be started an 'interval' from now,
353
320
# and every interval from then on.
354
321
self.checker_initiator_tag = (gobject.timeout_add
355
322
(self.interval_milliseconds(),
356
323
self.start_checker))
324
# Also start a new checker *right now*.
357
326
# Schedule a disable() when 'timeout' has passed
358
327
self.disable_initiator_tag = (gobject.timeout_add
359
328
(self.timeout_milliseconds(),
361
330
self.enabled = True
362
# Also start a new checker *right now*.
365
def disable(self, quiet=True):
366
333
"""Disable this client."""
367
334
if not getattr(self, "enabled", False):
370
self.send_changedstate()
372
logger.info(u"Disabling client %s", self.name)
336
logger.info(u"Disabling client %s", self.name)
373
337
if getattr(self, u"disable_initiator_tag", False):
374
338
gobject.source_remove(self.disable_initiator_tag)
375
339
self.disable_initiator_tag = None
499
458
logger.debug(u"Stopping checker for %(name)s", vars(self))
501
460
os.kill(self.checker.pid, signal.SIGTERM)
503
462
#if self.checker.poll() is None:
504
463
# os.kill(self.checker.pid, signal.SIGKILL)
505
464
except OSError, error:
506
465
if error.errno != errno.ESRCH: # No such process
508
467
self.checker = None
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):
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):
700
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()
858
662
# PropertyChanged - signal
859
663
@dbus.service.signal(_interface, signature=u"sv")
860
664
def PropertyChanged(self, property, value):
668
# ReceivedSecret - signal
865
669
@dbus.service.signal(_interface)
868
Is sent after a successful transfer of secret from the Mandos
869
server to mandos-client
670
def ReceivedSecret(self):
873
674
# Rejected - signal
874
@dbus.service.signal(_interface, signature=u"s")
875
def Rejected(self, reason):
879
# NeedApproval - signal
880
@dbus.service.signal(_interface, signature=u"db")
881
def NeedApproval(self, timeout, default):
888
@dbus.service.method(_interface, in_signature=u"b")
889
def Approve(self, value):
893
@dbus.service.method(_interface)
895
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(),
897
724
# Enable - method
898
725
@dbus.service.method(_interface)
917
744
def StopChecker(self):
918
745
self.stop_checker()
922
# approved_pending - property
923
@dbus_service_property(_interface, signature=u"b", access=u"read")
924
def approved_pending_dbus_property(self):
925
return dbus.Boolean(bool(self.approvals_pending))
927
# approved_by_default - property
928
@dbus_service_property(_interface, signature=u"b",
930
def approved_by_default_dbus_property(self):
931
return dbus.Boolean(self.approved_by_default)
933
# approved_delay - property
934
@dbus_service_property(_interface, signature=u"t",
936
def approved_delay_dbus_property(self):
937
return dbus.UInt64(self.approved_delay_milliseconds())
939
# approved_duration - property
940
@dbus_service_property(_interface, signature=u"t",
942
def approved_duration_dbus_property(self):
943
return dbus.UInt64(self._timedelta_to_milliseconds(
944
self.approved_duration))
947
@dbus_service_property(_interface, signature=u"s", access=u"read")
948
def name_dbus_property(self):
949
return dbus.String(self.name)
951
# fingerprint - property
952
@dbus_service_property(_interface, signature=u"s", access=u"read")
953
def fingerprint_dbus_property(self):
954
return dbus.String(self.fingerprint)
957
@dbus_service_property(_interface, signature=u"s",
959
def host_dbus_property(self, value=None):
960
if value is None: # get
961
return dbus.String(self.host)
964
self.PropertyChanged(dbus.String(u"host"),
965
dbus.String(value, variant_level=1))
968
@dbus_service_property(_interface, signature=u"s", access=u"read")
969
def created_dbus_property(self):
970
return dbus.String(self._datetime_to_dbus(self.created))
972
# last_enabled - property
973
@dbus_service_property(_interface, signature=u"s", access=u"read")
974
def last_enabled_dbus_property(self):
975
if self.last_enabled is None:
976
return dbus.String(u"")
977
return dbus.String(self._datetime_to_dbus(self.last_enabled))
980
@dbus_service_property(_interface, signature=u"b",
982
def enabled_dbus_property(self, value=None):
983
if value is None: # get
984
return dbus.Boolean(self.enabled)
990
# last_checked_ok - property
991
@dbus_service_property(_interface, signature=u"s",
993
def last_checked_ok_dbus_property(self, value=None):
994
if value is not None:
997
if self.last_checked_ok is None:
998
return dbus.String(u"")
999
return dbus.String(self._datetime_to_dbus(self
1002
# timeout - property
1003
@dbus_service_property(_interface, signature=u"t",
1004
access=u"readwrite")
1005
def timeout_dbus_property(self, value=None):
1006
if value is None: # get
1007
return dbus.UInt64(self.timeout_milliseconds())
1008
self.timeout = datetime.timedelta(0, 0, 0, value)
1010
self.PropertyChanged(dbus.String(u"timeout"),
1011
dbus.UInt64(value, variant_level=1))
1012
if getattr(self, u"disable_initiator_tag", None) is None:
1014
# Reschedule timeout
1015
gobject.source_remove(self.disable_initiator_tag)
1016
self.disable_initiator_tag = None
1017
time_to_die = (self.
1018
_timedelta_to_milliseconds((self
1023
if time_to_die <= 0:
1024
# The timeout has passed
1027
self.disable_initiator_tag = (gobject.timeout_add
1028
(time_to_die, self.disable))
1030
# interval - property
1031
@dbus_service_property(_interface, signature=u"t",
1032
access=u"readwrite")
1033
def interval_dbus_property(self, value=None):
1034
if value is None: # get
1035
return dbus.UInt64(self.interval_milliseconds())
1036
self.interval = datetime.timedelta(0, 0, 0, value)
1038
self.PropertyChanged(dbus.String(u"interval"),
1039
dbus.UInt64(value, variant_level=1))
1040
if getattr(self, u"checker_initiator_tag", None) is None:
1042
# Reschedule checker run
1043
gobject.source_remove(self.checker_initiator_tag)
1044
self.checker_initiator_tag = (gobject.timeout_add
1045
(value, self.start_checker))
1046
self.start_checker() # Start one now, too
1048
# checker - property
1049
@dbus_service_property(_interface, signature=u"s",
1050
access=u"readwrite")
1051
def checker_dbus_property(self, value=None):
1052
if value is None: # get
1053
return dbus.String(self.checker_command)
1054
self.checker_command = value
1056
self.PropertyChanged(dbus.String(u"checker"),
1057
dbus.String(self.checker_command,
1060
# checker_running - property
1061
@dbus_service_property(_interface, signature=u"b",
1062
access=u"readwrite")
1063
def checker_running_dbus_property(self, value=None):
1064
if value is None: # get
1065
return dbus.Boolean(self.checker is not None)
1067
self.start_checker()
1071
# object_path - property
1072
@dbus_service_property(_interface, signature=u"o", access=u"read")
1073
def object_path_dbus_property(self):
1074
return self.dbus_object_path # is already a dbus.ObjectPath
1077
@dbus_service_property(_interface, signature=u"ay",
1078
access=u"write", byte_arrays=True)
1079
def secret_dbus_property(self, value):
1080
self.secret = str(value)
1085
class ProxyClient(object):
1086
def __init__(self, child_pipe, fpr, address):
1087
self._pipe = child_pipe
1088
self._pipe.send(('init', fpr, address))
1089
if not self._pipe.recv():
1092
def __getattribute__(self, name):
1093
if(name == '_pipe'):
1094
return super(ProxyClient, self).__getattribute__(name)
1095
self._pipe.send(('getattr', name))
1096
data = self._pipe.recv()
1097
if data[0] == 'data':
1099
if data[0] == 'function':
1100
def func(*args, **kwargs):
1101
self._pipe.send(('funcall', name, args, kwargs))
1102
return self._pipe.recv()[1]
1105
def __setattr__(self, name, value):
1106
if(name == '_pipe'):
1107
return super(ProxyClient, self).__setattr__(name, value)
1108
self._pipe.send(('setattr', name, value))
1111
750
class ClientHandler(socketserver.BaseRequestHandler, object):
1112
751
"""A class to handle client connections.
1115
754
Note: This will run in its own forked process."""
1117
756
def handle(self):
1118
with contextlib.closing(self.server.child_pipe) as child_pipe:
1119
logger.info(u"TCP connection from: %s",
1120
unicode(self.client_address))
1121
logger.debug(u"Pipe FD: %d",
1122
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:
1124
762
session = (gnutls.connection
1125
763
.ClientSession(self.request,
1126
764
gnutls.connection
1127
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)
1129
776
# Note: gnutls.connection.X509Credentials is really a
1130
777
# generic GnuTLS certificate credentials object so long as
1131
778
# no X.509 keys are added to it. Therefore, we can use it
1132
779
# here despite using OpenPGP certificates.
1134
781
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1135
782
# u"+AES-256-CBC", u"+SHA1",
1136
783
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1163
798
# established. Just abandon the request.
1165
800
logger.debug(u"Handshake succeeded")
1167
approval_required = False
1170
fpr = self.fingerprint(self.peer_certificate
1172
except (TypeError, gnutls.errors.GNUTLSError), error:
1173
logger.warning(u"Bad certificate: %s", error)
1175
logger.debug(u"Fingerprint: %s", fpr)
1178
client = ProxyClient(child_pipe, fpr,
1179
self.client_address)
1183
if client.approved_delay:
1184
delay = client.approved_delay
1185
client.approvals_pending += 1
1186
approval_required = True
1189
if not client.enabled:
1190
logger.warning(u"Client %s is disabled",
1192
if self.server.use_dbus:
1194
client.Rejected("Disabled")
1197
if client._approved or not client.approved_delay:
1198
#We are approved or approval is disabled
1200
elif client._approved is None:
1201
logger.info(u"Client %s need approval",
1203
if self.server.use_dbus:
1205
client.NeedApproval(
1206
client.approved_delay_milliseconds(),
1207
client.approved_by_default)
1209
logger.warning(u"Client %s was not approved",
1211
if self.server.use_dbus:
1213
client.Rejected("Disapproved")
1216
#wait until timeout or approved
1217
#x = float(client._timedelta_to_milliseconds(delay))
1218
time = datetime.datetime.now()
1219
client.changedstate.acquire()
1220
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1221
client.changedstate.release()
1222
time2 = datetime.datetime.now()
1223
if (time2 - time) >= delay:
1224
if not client.approved_by_default:
1225
logger.warning("Client %s timed out while"
1226
" waiting for approval",
1228
if self.server.use_dbus:
1230
client.Rejected("Time out")
1235
delay -= time2 - time
1238
while sent_size < len(client.secret):
1240
sent = session.send(client.secret[sent_size:])
1241
except (gnutls.errors.GNUTLSError), error:
1242
logger.warning("gnutls send failed")
1244
logger.debug(u"Sent: %d, remaining: %d",
1245
sent, len(client.secret)
1246
- (sent_size + sent))
1249
logger.info(u"Sending secret to %s", client.name)
1250
# bump the timeout as if seen
1252
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)
1257
if approval_required:
1258
client.approvals_pending -= 1
1261
except (gnutls.errors.GNUTLSError), error:
1262
logger.warning("gnutls bye failed")
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))
1265
835
def peer_certificate(session):
1328
class MultiprocessingMixIn(object):
1329
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1330
def sub_process_main(self, request, address):
1332
self.finish_request(request, address)
1334
self.handle_error(request, address)
1335
self.close_request(request)
1337
def process_request(self, request, address):
1338
"""Start a new process to process the request."""
1339
multiprocessing.Process(target = self.sub_process_main,
1340
args = (request, address)).start()
1342
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1343
""" adds a pipe to the MixIn """
898
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
899
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1344
900
def process_request(self, request, client_address):
1345
901
"""Overrides and wraps the original process_request().
1347
This function creates a new pipe in self.pipe
903
This function creates a new pipe in self.pipe
1349
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1351
super(MultiprocessingMixInWithPipe,
905
self.pipe = os.pipe()
906
super(ForkingMixInWithPipe,
1352
907
self).process_request(request, client_address)
1353
self.child_pipe.close()
1354
self.add_pipe(parent_pipe)
1356
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):
1357
911
"""Dummy function; override as necessary"""
1360
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
915
class IPv6_TCPServer(ForkingMixInWithPipe,
1361
916
socketserver.TCPServer, object):
1362
917
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1473
1027
if cond & condition)
1474
1028
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1475
1029
conditions_string)
1477
# error or the other end of multiprocessing.Pipe has closed
1478
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1481
# Read a request from the child
1482
request = parent_pipe.recv()
1483
logger.debug(u"IPC request: %s", repr(request))
1484
command = request[0]
1486
if command == 'init':
1488
address = request[2]
1490
for c in self.clients:
1491
if c.fingerprint == fpr:
1495
logger.warning(u"Client not found for fingerprint: %s, ad"
1496
u"dress: %s", fpr, address)
1499
mandos_dbus_service.ClientNotFound(fpr, address)
1500
parent_pipe.send(False)
1503
gobject.io_add_watch(parent_pipe.fileno(),
1504
gobject.IO_IN | gobject.IO_HUP,
1505
functools.partial(self.handle_ipc,
1506
parent_pipe = parent_pipe,
1507
client_object = client))
1508
parent_pipe.send(True)
1509
# remove the old hook in favor of the new above hook on same fileno
1511
if command == 'funcall':
1512
funcname = request[1]
1516
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1518
if command == 'getattr':
1519
attrname = request[1]
1520
if callable(client_object.__getattribute__(attrname)):
1521
parent_pipe.send(('function',))
1523
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1525
if command == 'setattr':
1526
attrname = request[1]
1528
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
1820
1342
if server_settings["interface"]:
1821
1343
service.interface = (if_nametoindex
1822
1344
(str(server_settings[u"interface"])))
1825
# Close all input and output, do double fork, etc.
1828
global multiprocessing_manager
1829
multiprocessing_manager = multiprocessing.Manager()
1831
1346
client_class = Client
1833
1348
client_class = functools.partial(ClientDBus, bus = bus)
1834
def client_config_items(config, section):
1835
special_settings = {
1836
"approved_by_default":
1837
lambda: config.getboolean(section,
1838
"approved_by_default"),
1840
for name, value in config.items(section):
1842
yield (name, special_settings[name]())
1846
1349
tcp_server.clients.update(set(
1847
1350
client_class(name = section,
1848
config= dict(client_config_items(
1849
client_config, section)))
1351
config= dict(client_config.items(section)))
1850
1352
for section in client_config.sections()))
1851
1353
if not tcp_server.clients:
1852
1354
logger.warning(u"No clients defined")
1357
# Redirect stdin so all checkers get /dev/null
1358
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1359
os.dup2(null, sys.stdin.fileno())
1363
# No console logging
1364
logger.removeHandler(console)
1365
# Close all input and output, do double fork, etc.
1369
with closing(pidfile):
1856
1370
pid = os.getpid()
1857
1371
pidfile.write(str(pid) + "\n")