205
188
self.group.Commit()
206
189
def entry_group_state_changed(self, state, error):
207
190
"""Derived from the Avahi example code"""
208
logger.debug("Avahi entry group state change: %i", state)
191
logger.debug(u"Avahi state change: %i", state)
210
193
if state == avahi.ENTRY_GROUP_ESTABLISHED:
211
logger.debug("Zeroconf service established.")
194
logger.debug(u"Zeroconf service established.")
212
195
elif state == avahi.ENTRY_GROUP_COLLISION:
213
logger.info("Zeroconf service name collision.")
196
logger.warning(u"Zeroconf service name collision.")
215
198
elif state == avahi.ENTRY_GROUP_FAILURE:
216
logger.critical("Avahi: Error in group state changed %s",
199
logger.critical(u"Avahi: Error in group state changed %s",
218
raise AvahiGroupError("State changed: %s"
201
raise AvahiGroupError(u"State changed: %s"
219
202
% unicode(error))
220
203
def cleanup(self):
221
204
"""Derived from the Avahi example code"""
222
205
if self.group is not None:
225
except (dbus.exceptions.UnknownMethodException,
226
dbus.exceptions.DBusException) as e:
228
207
self.group = None
230
def server_state_changed(self, state, error=None):
208
def server_state_changed(self, state):
231
209
"""Derived from the Avahi example code"""
232
logger.debug("Avahi server state change: %i", state)
233
bad_states = { avahi.SERVER_INVALID:
234
"Zeroconf server invalid",
235
avahi.SERVER_REGISTERING: None,
236
avahi.SERVER_COLLISION:
237
"Zeroconf server name collision",
238
avahi.SERVER_FAILURE:
239
"Zeroconf server failure" }
240
if state in bad_states:
241
if bad_states[state] is not None:
243
logger.error(bad_states[state])
245
logger.error(bad_states[state] + ": %r", error)
210
if state == avahi.SERVER_COLLISION:
211
logger.error(u"Zeroconf server name collision")
247
213
elif state == avahi.SERVER_RUNNING:
251
logger.debug("Unknown state: %r", state)
253
logger.debug("Unknown state: %r: %r", state, error)
254
215
def activate(self):
255
216
"""Derived from the Avahi example code"""
256
217
if self.server is None:
257
218
self.server = dbus.Interface(
258
219
self.bus.get_object(avahi.DBUS_NAME,
259
avahi.DBUS_PATH_SERVER,
260
follow_name_owner_changes=True),
220
avahi.DBUS_PATH_SERVER),
261
221
avahi.DBUS_INTERFACE_SERVER)
262
self.server.connect_to_signal("StateChanged",
222
self.server.connect_to_signal(u"StateChanged",
263
223
self.server_state_changed)
264
224
self.server_state_changed(self.server.GetState())
268
228
"""A representation of a client host served by this server.
271
_approved: bool(); 'None' if not yet approved/disapproved
272
approval_delay: datetime.timedelta(); Time to wait for approval
273
approval_duration: datetime.timedelta(); Duration of one approval
231
name: string; from the config file, used in log messages and
233
fingerprint: string (40 or 32 hexadecimal digits); used to
234
uniquely identify the client
235
secret: bytestring; sent verbatim (over TLS) to client
236
host: string; available for use by the checker command
237
created: datetime.datetime(); (UTC) object creation
238
last_enabled: datetime.datetime(); (UTC)
240
last_checked_ok: datetime.datetime(); (UTC) or None
241
timeout: datetime.timedelta(); How long from last_checked_ok
242
until this client is invalid
243
interval: datetime.timedelta(); How often to start a new checker
244
disable_hook: If set, called by disable() as disable_hook(self)
274
245
checker: subprocess.Popen(); a running checker process used
275
246
to see if the client lives.
276
247
'None' if no process is running.
277
checker_callback_tag: a gobject event source tag, or None
278
checker_command: string; External command which is run to check
279
if client lives. %() expansions are done at
248
checker_initiator_tag: a gobject event source tag, or None
249
disable_initiator_tag: - '' -
250
checker_callback_tag: - '' -
251
checker_command: string; External command which is run to check if
252
client lives. %() expansions are done at
280
253
runtime with vars(self) as dict, so that for
281
254
instance %(name)s can be used in the command.
282
checker_initiator_tag: a gobject event source tag, or None
283
created: datetime.datetime(); (UTC) object creation
284
255
current_checker_command: string; current running checker_command
285
disable_hook: If set, called by disable() as disable_hook(self)
286
disable_initiator_tag: a gobject event source tag, or None
288
fingerprint: string (40 or 32 hexadecimal digits); used to
289
uniquely identify the client
290
host: string; available for use by the checker command
291
interval: datetime.timedelta(); How often to start a new checker
292
last_approval_request: datetime.datetime(); (UTC) or None
293
last_checked_ok: datetime.datetime(); (UTC) or None
294
last_enabled: datetime.datetime(); (UTC)
295
name: string; from the config file, used in log messages and
297
secret: bytestring; sent verbatim (over TLS) to client
298
timeout: datetime.timedelta(); How long from last_checked_ok
299
until this client is disabled
300
extended_timeout: extra long timeout when password has been sent
301
runtime_expansions: Allowed attributes for runtime expansion.
302
expires: datetime.datetime(); time (UTC) when a client will be
306
runtime_expansions = ("approval_delay", "approval_duration",
307
"created", "enabled", "fingerprint",
308
"host", "interval", "last_checked_ok",
309
"last_enabled", "name", "timeout")
312
def _timedelta_to_milliseconds(td):
313
"Convert a datetime.timedelta() to milliseconds"
314
return ((td.days * 24 * 60 * 60 * 1000)
315
+ (td.seconds * 1000)
316
+ (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))
318
265
def timeout_milliseconds(self):
319
266
"Return the 'timeout' attribute in milliseconds"
320
return self._timedelta_to_milliseconds(self.timeout)
322
def extended_timeout_milliseconds(self):
323
"Return the 'extended_timeout' attribute in milliseconds"
324
return self._timedelta_to_milliseconds(self.extended_timeout)
267
return self._datetime_to_milliseconds(self.timeout)
326
269
def interval_milliseconds(self):
327
270
"Return the 'interval' attribute in milliseconds"
328
return self._timedelta_to_milliseconds(self.interval)
330
def approval_delay_milliseconds(self):
331
return self._timedelta_to_milliseconds(self.approval_delay)
271
return self._datetime_to_milliseconds(self.interval)
333
273
def __init__(self, name = None, disable_hook=None, config=None):
334
274
"""Note: the 'checker' key in 'config' sets the
338
278
if config is None:
340
logger.debug("Creating client %r", self.name)
280
logger.debug(u"Creating client %r", self.name)
341
281
# Uppercase and remove spaces from fingerprint for later
342
282
# comparison purposes with return value from the fingerprint()
344
self.fingerprint = (config["fingerprint"].upper()
346
logger.debug(" Fingerprint: %s", self.fingerprint)
347
if "secret" in config:
348
self.secret = config["secret"].decode("base64")
349
elif "secfile" in config:
350
with open(os.path.expanduser(os.path.expandvars
351
(config["secfile"])),
284
self.fingerprint = (config[u"fingerprint"].upper()
286
logger.debug(u" Fingerprint: %s", self.fingerprint)
287
if u"secret" in config:
288
self.secret = config[u"secret"].decode(u"base64")
289
elif u"secfile" in config:
290
with closing(open(os.path.expanduser
292
(config[u"secfile"])))) as secfile:
353
293
self.secret = secfile.read()
355
raise TypeError("No secret or secfile for client %s"
295
raise TypeError(u"No secret or secfile for client %s"
357
self.host = config.get("host", "")
297
self.host = config.get(u"host", u"")
358
298
self.created = datetime.datetime.utcnow()
359
299
self.enabled = False
360
self.last_approval_request = None
361
300
self.last_enabled = None
362
301
self.last_checked_ok = None
363
self.timeout = string_to_delta(config["timeout"])
364
self.extended_timeout = string_to_delta(config["extended_timeout"])
365
self.interval = string_to_delta(config["interval"])
302
self.timeout = string_to_delta(config[u"timeout"])
303
self.interval = string_to_delta(config[u"interval"])
366
304
self.disable_hook = disable_hook
367
305
self.checker = None
368
306
self.checker_initiator_tag = None
369
307
self.disable_initiator_tag = None
371
308
self.checker_callback_tag = None
372
self.checker_command = config["checker"]
309
self.checker_command = config[u"checker"]
373
310
self.current_checker_command = None
374
311
self.last_connect = None
375
self._approved = None
376
self.approved_by_default = config.get("approved_by_default",
378
self.approvals_pending = 0
379
self.approval_delay = string_to_delta(
380
config["approval_delay"])
381
self.approval_duration = string_to_delta(
382
config["approval_duration"])
383
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
385
def send_changedstate(self):
386
self.changedstate.acquire()
387
self.changedstate.notify_all()
388
self.changedstate.release()
390
313
def enable(self):
391
314
"""Start this client's checker and timeout hooks"""
392
if getattr(self, "enabled", False):
315
if getattr(self, u"enabled", False):
393
316
# Already enabled
395
self.send_changedstate()
396
318
self.last_enabled = datetime.datetime.utcnow()
397
319
# Schedule a new checker to be started an 'interval' from now,
398
320
# and every interval from then on.
399
321
self.checker_initiator_tag = (gobject.timeout_add
400
322
(self.interval_milliseconds(),
401
323
self.start_checker))
324
# Also start a new checker *right now*.
402
326
# Schedule a disable() when 'timeout' has passed
403
self.expires = datetime.datetime.utcnow() + self.timeout
404
327
self.disable_initiator_tag = (gobject.timeout_add
405
328
(self.timeout_milliseconds(),
407
330
self.enabled = True
408
# Also start a new checker *right now*.
411
def disable(self, quiet=True):
412
333
"""Disable this client."""
413
334
if not getattr(self, "enabled", False):
416
self.send_changedstate()
418
logger.info("Disabling client %s", self.name)
419
if getattr(self, "disable_initiator_tag", False):
336
logger.info(u"Disabling client %s", self.name)
337
if getattr(self, u"disable_initiator_tag", False):
420
338
gobject.source_remove(self.disable_initiator_tag)
421
339
self.disable_initiator_tag = None
423
if getattr(self, "checker_initiator_tag", False):
340
if getattr(self, u"checker_initiator_tag", False):
424
341
gobject.source_remove(self.checker_initiator_tag)
425
342
self.checker_initiator_tag = None
426
343
self.stop_checker()
549
453
if self.checker_callback_tag:
550
454
gobject.source_remove(self.checker_callback_tag)
551
455
self.checker_callback_tag = None
552
if getattr(self, "checker", None) is None:
456
if getattr(self, u"checker", None) is None:
554
logger.debug("Stopping checker for %(name)s", vars(self))
458
logger.debug(u"Stopping checker for %(name)s", vars(self))
556
460
os.kill(self.checker.pid, signal.SIGTERM)
558
462
#if self.checker.poll() is None:
559
463
# os.kill(self.checker.pid, signal.SIGKILL)
560
except OSError as error:
464
except OSError, error:
561
465
if error.errno != errno.ESRCH: # No such process
563
467
self.checker = None
565
def dbus_service_property(dbus_interface, signature="v",
566
access="readwrite", byte_arrays=False):
567
"""Decorators for marking methods of a DBusObjectWithProperties to
568
become properties on the D-Bus.
570
The decorated method will be called with no arguments by "Get"
571
and with one argument by "Set".
573
The parameters, where they are supported, are the same as
574
dbus.service.method, except there is only "signature", since the
575
type from Get() and the type sent to Set() is the same.
577
# Encoding deeply encoded byte arrays is not supported yet by the
578
# "Set" method, so we fail early here:
579
if byte_arrays and signature != "ay":
580
raise ValueError("Byte arrays not supported for non-'ay'"
581
" signature %r" % signature)
583
func._dbus_is_property = True
584
func._dbus_interface = dbus_interface
585
func._dbus_signature = signature
586
func._dbus_access = access
587
func._dbus_name = func.__name__
588
if func._dbus_name.endswith("_dbus_property"):
589
func._dbus_name = func._dbus_name[:-14]
590
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
595
class DBusPropertyException(dbus.exceptions.DBusException):
596
"""A base class for D-Bus property-related exceptions
598
def __unicode__(self):
599
return unicode(str(self))
602
class DBusPropertyAccessException(DBusPropertyException):
603
"""A property's access permissions disallows an operation.
608
class DBusPropertyNotFound(DBusPropertyException):
609
"""An attempt was made to access a non-existing property.
614
class DBusObjectWithProperties(dbus.service.Object):
615
"""A D-Bus object with properties.
617
Classes inheriting from this can use the dbus_service_property
618
decorator to expose methods as D-Bus properties. It exposes the
619
standard Get(), Set(), and GetAll() methods on the D-Bus.
623
def _is_dbus_property(obj):
624
return getattr(obj, "_dbus_is_property", False)
626
def _get_all_dbus_properties(self):
627
"""Returns a generator of (name, attribute) pairs
629
return ((prop._dbus_name, prop)
631
inspect.getmembers(self, self._is_dbus_property))
633
def _get_dbus_property(self, interface_name, property_name):
634
"""Returns a bound method if one exists which is a D-Bus
635
property with the specified name and interface.
637
for name in (property_name,
638
property_name + "_dbus_property"):
639
prop = getattr(self, name, None)
641
or not self._is_dbus_property(prop)
642
or prop._dbus_name != property_name
643
or (interface_name and prop._dbus_interface
644
and interface_name != prop._dbus_interface)):
648
raise DBusPropertyNotFound(self.dbus_object_path + ":"
649
+ interface_name + "."
652
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
654
def Get(self, interface_name, property_name):
655
"""Standard D-Bus property Get() method, see D-Bus standard.
657
prop = self._get_dbus_property(interface_name, property_name)
658
if prop._dbus_access == "write":
659
raise DBusPropertyAccessException(property_name)
661
if not hasattr(value, "variant_level"):
663
return type(value)(value, variant_level=value.variant_level+1)
665
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
666
def Set(self, interface_name, property_name, value):
667
"""Standard D-Bus property Set() method, see D-Bus standard.
669
prop = self._get_dbus_property(interface_name, property_name)
670
if prop._dbus_access == "read":
671
raise DBusPropertyAccessException(property_name)
672
if prop._dbus_get_args_options["byte_arrays"]:
673
# The byte_arrays option is not supported yet on
674
# signatures other than "ay".
675
if prop._dbus_signature != "ay":
677
value = dbus.ByteArray(''.join(unichr(byte)
681
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
682
out_signature="a{sv}")
683
def GetAll(self, interface_name):
684
"""Standard D-Bus property GetAll() method, see D-Bus
687
Note: Will not include properties with access="write".
690
for name, prop in self._get_all_dbus_properties():
692
and interface_name != prop._dbus_interface):
693
# Interface non-empty but did not match
695
# Ignore write-only properties
696
if prop._dbus_access == "write":
699
if not hasattr(value, "variant_level"):
702
all[name] = type(value)(value, variant_level=
703
value.variant_level+1)
704
return dbus.Dictionary(all, signature="sv")
706
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
708
path_keyword='object_path',
709
connection_keyword='connection')
710
def Introspect(self, object_path, connection):
711
"""Standard D-Bus method, overloaded to insert property tags.
713
xmlstring = dbus.service.Object.Introspect(self, object_path,
716
document = xml.dom.minidom.parseString(xmlstring)
717
def make_tag(document, name, prop):
718
e = document.createElement("property")
719
e.setAttribute("name", name)
720
e.setAttribute("type", prop._dbus_signature)
721
e.setAttribute("access", prop._dbus_access)
723
for if_tag in document.getElementsByTagName("interface"):
724
for tag in (make_tag(document, name, prop)
726
in self._get_all_dbus_properties()
727
if prop._dbus_interface
728
== if_tag.getAttribute("name")):
729
if_tag.appendChild(tag)
730
# Add the names to the return values for the
731
# "org.freedesktop.DBus.Properties" methods
732
if (if_tag.getAttribute("name")
733
== "org.freedesktop.DBus.Properties"):
734
for cn in if_tag.getElementsByTagName("method"):
735
if cn.getAttribute("name") == "Get":
736
for arg in cn.getElementsByTagName("arg"):
737
if (arg.getAttribute("direction")
739
arg.setAttribute("name", "value")
740
elif cn.getAttribute("name") == "GetAll":
741
for arg in cn.getElementsByTagName("arg"):
742
if (arg.getAttribute("direction")
744
arg.setAttribute("name", "props")
745
xmlstring = document.toxml("utf-8")
747
except (AttributeError, xml.dom.DOMException,
748
xml.parsers.expat.ExpatError) as error:
749
logger.error("Failed to override Introspection method",
754
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):
755
481
"""A Client class using D-Bus
758
484
dbus_object_path: dbus.ObjectPath
759
485
bus: dbus.SystemBus()
762
runtime_expansions = (Client.runtime_expansions
763
+ ("dbus_object_path",))
765
487
# dbus.service.Object doesn't use super(), so we can't either.
767
489
def __init__(self, bus = None, *args, **kwargs):
768
self._approvals_pending = 0
770
491
Client.__init__(self, *args, **kwargs)
771
492
# Only now, when this client is initialized, can it show up on
773
client_object_name = unicode(self.name).translate(
776
494
self.dbus_object_path = (dbus.ObjectPath
777
("/clients/" + client_object_name))
778
DBusObjectWithProperties.__init__(self, self.bus,
779
self.dbus_object_path)
780
def _set_expires(self, value):
781
old_value = getattr(self, "_expires", None)
782
self._expires = value
783
if hasattr(self, "dbus_object_path") and old_value != value:
784
dbus_time = (self._datetime_to_dbus(self._expires,
786
self.PropertyChanged(dbus.String("Expires"),
788
expires = property(lambda self: self._expires, _set_expires)
791
def _get_approvals_pending(self):
792
return self._approvals_pending
793
def _set_approvals_pending(self, value):
794
old_value = self._approvals_pending
795
self._approvals_pending = value
797
if (hasattr(self, "dbus_object_path")
798
and bval is not bool(old_value)):
799
dbus_bool = dbus.Boolean(bval, variant_level=1)
800
self.PropertyChanged(dbus.String("ApprovalPending"),
803
approvals_pending = property(_get_approvals_pending,
804
_set_approvals_pending)
805
del _get_approvals_pending, _set_approvals_pending
496
+ self.name.replace(u".", u"_")))
497
dbus.service.Object.__init__(self, self.bus,
498
self.dbus_object_path)
808
501
def _datetime_to_dbus(dt, variant_level=0):
809
502
"""Convert a UTC datetime.datetime() to a D-Bus type."""
811
return dbus.String("", variant_level = variant_level)
812
503
return dbus.String(dt.isoformat(),
813
504
variant_level=variant_level)
815
506
def enable(self):
816
oldstate = getattr(self, "enabled", False)
507
oldstate = getattr(self, u"enabled", False)
817
508
r = Client.enable(self)
818
509
if oldstate != self.enabled:
819
510
# Emit D-Bus signals
820
self.PropertyChanged(dbus.String("Enabled"),
511
self.PropertyChanged(dbus.String(u"enabled"),
821
512
dbus.Boolean(True, variant_level=1))
822
513
self.PropertyChanged(
823
dbus.String("LastEnabled"),
514
dbus.String(u"last_enabled"),
824
515
self._datetime_to_dbus(self.last_enabled,
825
516
variant_level=1))
828
def disable(self, quiet = False):
829
oldstate = getattr(self, "enabled", False)
830
r = Client.disable(self, quiet=quiet)
831
if not quiet and oldstate != self.enabled:
519
def disable(self, signal = True):
520
oldstate = getattr(self, u"enabled", False)
521
r = Client.disable(self)
522
if signal and oldstate != self.enabled:
832
523
# Emit D-Bus signal
833
self.PropertyChanged(dbus.String("Enabled"),
524
self.PropertyChanged(dbus.String(u"enabled"),
834
525
dbus.Boolean(False, variant_level=1))
895
578
# Emit D-Bus signal
896
579
self.CheckerStarted(self.current_checker_command)
897
580
self.PropertyChanged(
898
dbus.String("CheckerRunning"),
581
dbus.String(u"checker_running"),
899
582
dbus.Boolean(True, variant_level=1))
902
585
def stop_checker(self, *args, **kwargs):
903
old_checker = getattr(self, "checker", None)
586
old_checker = getattr(self, u"checker", None)
904
587
r = Client.stop_checker(self, *args, **kwargs)
905
588
if (old_checker is not None
906
and getattr(self, "checker", None) is None):
907
self.PropertyChanged(dbus.String("CheckerRunning"),
589
and getattr(self, u"checker", None) is None):
590
self.PropertyChanged(dbus.String(u"checker_running"),
908
591
dbus.Boolean(False, variant_level=1))
911
def _reset_approved(self):
912
self._approved = None
915
def approve(self, value=True):
916
self.send_changedstate()
917
self._approved = value
918
gobject.timeout_add(self._timedelta_to_milliseconds
919
(self.approval_duration),
920
self._reset_approved)
923
## D-Bus methods, signals & properties
924
_interface = "se.bsnet.fukt.Mandos.Client"
594
## D-Bus methods & signals
595
_interface = u"se.bsnet.fukt.Mandos.Client"
598
@dbus.service.method(_interface)
600
return self.checked_ok()
928
602
# CheckerCompleted - signal
929
@dbus.service.signal(_interface, signature="nxs")
603
@dbus.service.signal(_interface, signature=u"nxs")
930
604
def CheckerCompleted(self, exitcode, waitstatus, command):
934
608
# CheckerStarted - signal
935
@dbus.service.signal(_interface, signature="s")
609
@dbus.service.signal(_interface, signature=u"s")
936
610
def CheckerStarted(self, command):
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()
940
662
# PropertyChanged - signal
941
@dbus.service.signal(_interface, signature="sv")
663
@dbus.service.signal(_interface, signature=u"sv")
942
664
def PropertyChanged(self, property, value):
668
# ReceivedSecret - signal
947
669
@dbus.service.signal(_interface)
950
Is sent after a successful transfer of secret from the Mandos
951
server to mandos-client
670
def ReceivedSecret(self):
955
674
# Rejected - signal
956
@dbus.service.signal(_interface, signature="s")
957
def Rejected(self, reason):
675
@dbus.service.signal(_interface)
961
# NeedApproval - signal
962
@dbus.service.signal(_interface, signature="tb")
963
def NeedApproval(self, timeout, default):
965
return self.need_approval()
970
@dbus.service.method(_interface, in_signature="b")
971
def Approve(self, value):
975
@dbus.service.method(_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(),
979
724
# Enable - method
980
725
@dbus.service.method(_interface)
999
744
def StopChecker(self):
1000
745
self.stop_checker()
1004
# ApprovalPending - property
1005
@dbus_service_property(_interface, signature="b", access="read")
1006
def ApprovalPending_dbus_property(self):
1007
return dbus.Boolean(bool(self.approvals_pending))
1009
# ApprovedByDefault - property
1010
@dbus_service_property(_interface, signature="b",
1012
def ApprovedByDefault_dbus_property(self, value=None):
1013
if value is None: # get
1014
return dbus.Boolean(self.approved_by_default)
1015
old_value = self.approved_by_default
1016
self.approved_by_default = bool(value)
1018
if old_value != self.approved_by_default:
1019
self.PropertyChanged(dbus.String("ApprovedByDefault"),
1020
dbus.Boolean(value, variant_level=1))
1022
# ApprovalDelay - property
1023
@dbus_service_property(_interface, signature="t",
1025
def ApprovalDelay_dbus_property(self, value=None):
1026
if value is None: # get
1027
return dbus.UInt64(self.approval_delay_milliseconds())
1028
old_value = self.approval_delay
1029
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1031
if old_value != self.approval_delay:
1032
self.PropertyChanged(dbus.String("ApprovalDelay"),
1033
dbus.UInt64(value, variant_level=1))
1035
# ApprovalDuration - property
1036
@dbus_service_property(_interface, signature="t",
1038
def ApprovalDuration_dbus_property(self, value=None):
1039
if value is None: # get
1040
return dbus.UInt64(self._timedelta_to_milliseconds(
1041
self.approval_duration))
1042
old_value = self.approval_duration
1043
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1045
if old_value != self.approval_duration:
1046
self.PropertyChanged(dbus.String("ApprovalDuration"),
1047
dbus.UInt64(value, variant_level=1))
1050
@dbus_service_property(_interface, signature="s", access="read")
1051
def Name_dbus_property(self):
1052
return dbus.String(self.name)
1054
# Fingerprint - property
1055
@dbus_service_property(_interface, signature="s", access="read")
1056
def Fingerprint_dbus_property(self):
1057
return dbus.String(self.fingerprint)
1060
@dbus_service_property(_interface, signature="s",
1062
def Host_dbus_property(self, value=None):
1063
if value is None: # get
1064
return dbus.String(self.host)
1065
old_value = self.host
1068
if old_value != self.host:
1069
self.PropertyChanged(dbus.String("Host"),
1070
dbus.String(value, variant_level=1))
1072
# Created - property
1073
@dbus_service_property(_interface, signature="s", access="read")
1074
def Created_dbus_property(self):
1075
return dbus.String(self._datetime_to_dbus(self.created))
1077
# LastEnabled - property
1078
@dbus_service_property(_interface, signature="s", access="read")
1079
def LastEnabled_dbus_property(self):
1080
return self._datetime_to_dbus(self.last_enabled)
1082
# Enabled - property
1083
@dbus_service_property(_interface, signature="b",
1085
def Enabled_dbus_property(self, value=None):
1086
if value is None: # get
1087
return dbus.Boolean(self.enabled)
1093
# LastCheckedOK - property
1094
@dbus_service_property(_interface, signature="s",
1096
def LastCheckedOK_dbus_property(self, value=None):
1097
if value is not None:
1100
return self._datetime_to_dbus(self.last_checked_ok)
1102
# Expires - property
1103
@dbus_service_property(_interface, signature="s", access="read")
1104
def Expires_dbus_property(self):
1105
return self._datetime_to_dbus(self.expires)
1107
# LastApprovalRequest - property
1108
@dbus_service_property(_interface, signature="s", access="read")
1109
def LastApprovalRequest_dbus_property(self):
1110
return self._datetime_to_dbus(self.last_approval_request)
1112
# Timeout - property
1113
@dbus_service_property(_interface, signature="t",
1115
def Timeout_dbus_property(self, value=None):
1116
if value is None: # get
1117
return dbus.UInt64(self.timeout_milliseconds())
1118
old_value = self.timeout
1119
self.timeout = datetime.timedelta(0, 0, 0, value)
1121
if old_value != self.timeout:
1122
self.PropertyChanged(dbus.String("Timeout"),
1123
dbus.UInt64(value, variant_level=1))
1124
if getattr(self, "disable_initiator_tag", None) is None:
1126
# Reschedule timeout
1127
gobject.source_remove(self.disable_initiator_tag)
1128
self.disable_initiator_tag = None
1130
time_to_die = (self.
1131
_timedelta_to_milliseconds((self
1136
if time_to_die <= 0:
1137
# The timeout has passed
1140
self.expires = (datetime.datetime.utcnow()
1141
+ datetime.timedelta(milliseconds = time_to_die))
1142
self.disable_initiator_tag = (gobject.timeout_add
1143
(time_to_die, self.disable))
1145
# ExtendedTimeout - property
1146
@dbus_service_property(_interface, signature="t",
1148
def ExtendedTimeout_dbus_property(self, value=None):
1149
if value is None: # get
1150
return dbus.UInt64(self.extended_timeout_milliseconds())
1151
old_value = self.extended_timeout
1152
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1154
if old_value != self.extended_timeout:
1155
self.PropertyChanged(dbus.String("ExtendedTimeout"),
1156
dbus.UInt64(value, variant_level=1))
1158
# Interval - property
1159
@dbus_service_property(_interface, signature="t",
1161
def Interval_dbus_property(self, value=None):
1162
if value is None: # get
1163
return dbus.UInt64(self.interval_milliseconds())
1164
old_value = self.interval
1165
self.interval = datetime.timedelta(0, 0, 0, value)
1167
if old_value != self.interval:
1168
self.PropertyChanged(dbus.String("Interval"),
1169
dbus.UInt64(value, variant_level=1))
1170
if getattr(self, "checker_initiator_tag", None) is None:
1172
# Reschedule checker run
1173
gobject.source_remove(self.checker_initiator_tag)
1174
self.checker_initiator_tag = (gobject.timeout_add
1175
(value, self.start_checker))
1176
self.start_checker() # Start one now, too
1178
# Checker - property
1179
@dbus_service_property(_interface, signature="s",
1181
def Checker_dbus_property(self, value=None):
1182
if value is None: # get
1183
return dbus.String(self.checker_command)
1184
old_value = self.checker_command
1185
self.checker_command = value
1187
if old_value != self.checker_command:
1188
self.PropertyChanged(dbus.String("Checker"),
1189
dbus.String(self.checker_command,
1192
# CheckerRunning - property
1193
@dbus_service_property(_interface, signature="b",
1195
def CheckerRunning_dbus_property(self, value=None):
1196
if value is None: # get
1197
return dbus.Boolean(self.checker is not None)
1199
self.start_checker()
1203
# ObjectPath - property
1204
@dbus_service_property(_interface, signature="o", access="read")
1205
def ObjectPath_dbus_property(self):
1206
return self.dbus_object_path # is already a dbus.ObjectPath
1209
@dbus_service_property(_interface, signature="ay",
1210
access="write", byte_arrays=True)
1211
def Secret_dbus_property(self, value):
1212
self.secret = str(value)
1217
class ProxyClient(object):
1218
def __init__(self, child_pipe, fpr, address):
1219
self._pipe = child_pipe
1220
self._pipe.send(('init', fpr, address))
1221
if not self._pipe.recv():
1224
def __getattribute__(self, name):
1225
if(name == '_pipe'):
1226
return super(ProxyClient, self).__getattribute__(name)
1227
self._pipe.send(('getattr', name))
1228
data = self._pipe.recv()
1229
if data[0] == 'data':
1231
if data[0] == 'function':
1232
def func(*args, **kwargs):
1233
self._pipe.send(('funcall', name, args, kwargs))
1234
return self._pipe.recv()[1]
1237
def __setattr__(self, name, value):
1238
if(name == '_pipe'):
1239
return super(ProxyClient, self).__setattr__(name, value)
1240
self._pipe.send(('setattr', name, value))
1243
750
class ClientHandler(socketserver.BaseRequestHandler, object):
1244
751
"""A class to handle client connections.
1247
754
Note: This will run in its own forked process."""
1249
756
def handle(self):
1250
with contextlib.closing(self.server.child_pipe) as child_pipe:
1251
logger.info("TCP connection from: %s",
1252
unicode(self.client_address))
1253
logger.debug("Pipe FD: %d",
1254
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:
1256
762
session = (gnutls.connection
1257
763
.ClientSession(self.request,
1258
764
gnutls.connection
1259
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)
1261
776
# Note: gnutls.connection.X509Credentials is really a
1262
777
# generic GnuTLS certificate credentials object so long as
1263
778
# no X.509 keys are added to it. Therefore, we can use it
1264
779
# here despite using OpenPGP certificates.
1266
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1267
# "+AES-256-CBC", "+SHA1",
1268
# "+COMP-NULL", "+CTYPE-OPENPGP",
781
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
782
# u"+AES-256-CBC", u"+SHA1",
783
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1270
785
# Use a fallback default, since this MUST be set.
1271
786
priority = self.server.gnutls_priority
1272
787
if priority is None:
1274
789
(gnutls.library.functions
1275
790
.gnutls_priority_set_direct(session._c_object,
1276
791
priority, None))
1278
# Start communication using the Mandos protocol
1279
# Get protocol number
1280
line = self.request.makefile().readline()
1281
logger.debug("Protocol version: %r", line)
1283
if int(line.strip().split()[0]) > 1:
1285
except (ValueError, IndexError, RuntimeError) as error:
1286
logger.error("Unknown protocol version: %s", error)
1289
# Start GnuTLS connection
1291
794
session.handshake()
1292
except gnutls.errors.GNUTLSError as error:
1293
logger.warning("Handshake failed: %s", error)
795
except gnutls.errors.GNUTLSError, error:
796
logger.warning(u"Handshake failed: %s", error)
1294
797
# Do not run session.bye() here: the session is not
1295
798
# established. Just abandon the request.
1297
logger.debug("Handshake succeeded")
1299
approval_required = False
800
logger.debug(u"Handshake succeeded")
1302
fpr = self.fingerprint(self.peer_certificate
1305
gnutls.errors.GNUTLSError) as error:
1306
logger.warning("Bad certificate: %s", error)
1308
logger.debug("Fingerprint: %s", fpr)
1311
client = ProxyClient(child_pipe, fpr,
1312
self.client_address)
1316
if client.approval_delay:
1317
delay = client.approval_delay
1318
client.approvals_pending += 1
1319
approval_required = True
1322
if not client.enabled:
1323
logger.info("Client %s is disabled",
1325
if self.server.use_dbus:
1327
client.Rejected("Disabled")
1330
if client._approved or not client.approval_delay:
1331
#We are approved or approval is disabled
1333
elif client._approved is None:
1334
logger.info("Client %s needs approval",
1336
if self.server.use_dbus:
1338
client.NeedApproval(
1339
client.approval_delay_milliseconds(),
1340
client.approved_by_default)
1342
logger.warning("Client %s was not approved",
1344
if self.server.use_dbus:
1346
client.Rejected("Denied")
1349
#wait until timeout or approved
1350
#x = float(client._timedelta_to_milliseconds(delay))
1351
time = datetime.datetime.now()
1352
client.changedstate.acquire()
1353
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1354
client.changedstate.release()
1355
time2 = datetime.datetime.now()
1356
if (time2 - time) >= delay:
1357
if not client.approved_by_default:
1358
logger.warning("Client %s timed out while"
1359
" waiting for approval",
1361
if self.server.use_dbus:
1363
client.Rejected("Approval timed out")
1368
delay -= time2 - time
1371
while sent_size < len(client.secret):
1373
sent = session.send(client.secret[sent_size:])
1374
except gnutls.errors.GNUTLSError as error:
1375
logger.warning("gnutls send failed")
1377
logger.debug("Sent: %d, remaining: %d",
1378
sent, len(client.secret)
1379
- (sent_size + sent))
1382
logger.info("Sending secret to %s", client.name)
1383
# bump the timeout as if seen
1384
client.checked_ok(client.extended_timeout)
1385
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)
1390
if approval_required:
1391
client.approvals_pending -= 1
1394
except gnutls.errors.GNUTLSError as error:
1395
logger.warning("GnuTLS bye failed")
809
for c in self.server.clients:
810
if c.fingerprint == fpr:
814
ipc.write(u"NOTFOUND %s %s\n"
815
% (fpr, unicode(self.client_address)))
818
# Have to check if client.still_valid(), since it is
819
# possible that the client timed out while establishing
820
# the GnuTLS session.
821
if not client.still_valid():
822
ipc.write(u"INVALID %s\n" % client.name)
825
ipc.write(u"SENDING %s\n" % client.name)
827
while sent_size < len(client.secret):
828
sent = session.send(client.secret[sent_size:])
829
logger.debug(u"Sent: %d, remaining: %d",
830
sent, len(client.secret)
831
- (sent_size + sent))
1398
836
def peer_certificate(session):
1454
892
# Convert the buffer to a Python bytestring
1455
893
fpr = ctypes.string_at(buf, buf_len.value)
1456
894
# Convert the bytestring to hexadecimal notation
1457
hex_fpr = ''.join("%02X" % ord(char) for char in fpr)
895
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1461
class MultiprocessingMixIn(object):
1462
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1463
def sub_process_main(self, request, address):
1465
self.finish_request(request, address)
1467
self.handle_error(request, address)
1468
self.close_request(request)
1470
def process_request(self, request, address):
1471
"""Start a new process to process the request."""
1472
multiprocessing.Process(target = self.sub_process_main,
1473
args = (request, address)).start()
1475
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1476
""" adds a pipe to the MixIn """
899
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
900
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1477
901
def process_request(self, request, client_address):
1478
902
"""Overrides and wraps the original process_request().
1480
904
This function creates a new pipe in self.pipe
1482
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1484
super(MultiprocessingMixInWithPipe,
906
self.pipe = os.pipe()
907
super(ForkingMixInWithPipe,
1485
908
self).process_request(request, client_address)
1486
self.child_pipe.close()
1487
self.add_pipe(parent_pipe)
1489
def add_pipe(self, parent_pipe):
909
os.close(self.pipe[1]) # close write end
910
self.add_pipe(self.pipe[0])
911
def add_pipe(self, pipe):
1490
912
"""Dummy function; override as necessary"""
1491
raise NotImplementedError
1493
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
916
class IPv6_TCPServer(ForkingMixInWithPipe,
1494
917
socketserver.TCPServer, object):
1495
918
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1604
1026
for cond, name in
1605
1027
condition_names.iteritems()
1606
1028
if cond & condition)
1607
# error or the other end of multiprocessing.Pipe has closed
1608
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1611
# Read a request from the child
1612
request = parent_pipe.recv()
1613
command = request[0]
1615
if command == 'init':
1617
address = request[2]
1619
for c in self.clients:
1620
if c.fingerprint == fpr:
1624
logger.info("Client not found for fingerprint: %s, ad"
1625
"dress: %s", fpr, address)
1628
mandos_dbus_service.ClientNotFound(fpr, address[0])
1629
parent_pipe.send(False)
1632
gobject.io_add_watch(parent_pipe.fileno(),
1633
gobject.IO_IN | gobject.IO_HUP,
1634
functools.partial(self.handle_ipc,
1635
parent_pipe = parent_pipe,
1636
client_object = client))
1637
parent_pipe.send(True)
1638
# remove the old hook in favor of the new above hook on same fileno
1640
if command == 'funcall':
1641
funcname = request[1]
1645
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1647
if command == 'getattr':
1648
attrname = request[1]
1649
if callable(client_object.__getattribute__(attrname)):
1650
parent_pipe.send(('function',))
1652
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1654
if command == 'setattr':
1655
attrname = request[1]
1657
setattr(client_object, attrname, value)
1029
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1032
# Turn the pipe file descriptor into a Python file object
1033
if source not in file_objects:
1034
file_objects[source] = os.fdopen(source, u"r", 1)
1036
# Read a line from the file object
1037
cmdline = file_objects[source].readline()
1038
if not cmdline: # Empty line means end of file
1039
# close the IPC pipe
1040
file_objects[source].close()
1041
del file_objects[source]
1043
# Stop calling this function
1046
logger.debug(u"IPC command: %r", cmdline)
1048
# Parse and act on command
1049
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1051
if cmd == u"NOTFOUND":
1052
logger.warning(u"Client not found for fingerprint: %s",
1056
mandos_dbus_service.ClientNotFound(args)
1057
elif cmd == u"INVALID":
1058
for client in self.clients:
1059
if client.name == args:
1060
logger.warning(u"Client %s is invalid", args)
1066
logger.error(u"Unknown client %s is invalid", args)
1067
elif cmd == u"SENDING":
1068
for client in self.clients:
1069
if client.name == args:
1070
logger.info(u"Sending secret to %s", client.name)
1074
client.ReceivedSecret()
1077
logger.error(u"Sending secret to unknown client %s",
1080
logger.error(u"Unknown IPC command: %r", cmdline)
1082
# Keep calling this function
1662
1086
def string_to_delta(interval):
1663
1087
"""Parse a string and return a datetime.timedelta
1665
>>> string_to_delta('7d')
1089
>>> string_to_delta(u'7d')
1666
1090
datetime.timedelta(7)
1667
>>> string_to_delta('60s')
1091
>>> string_to_delta(u'60s')
1668
1092
datetime.timedelta(0, 60)
1669
>>> string_to_delta('60m')
1093
>>> string_to_delta(u'60m')
1670
1094
datetime.timedelta(0, 3600)
1671
>>> string_to_delta('24h')
1095
>>> string_to_delta(u'24h')
1672
1096
datetime.timedelta(1)
1673
>>> string_to_delta('1w')
1097
>>> string_to_delta(u'1w')
1674
1098
datetime.timedelta(7)
1675
>>> string_to_delta('5m 30s')
1099
>>> string_to_delta(u'5m 30s')
1676
1100
datetime.timedelta(0, 330)
1678
1102
timevalue = datetime.timedelta(0)
1752
##################################################################
1175
######################################################################
1753
1176
# Parsing of options, both command line and config file
1755
parser = argparse.ArgumentParser()
1756
parser.add_argument("-v", "--version", action="version",
1757
version = "%%(prog)s %s" % version,
1758
help="show version number and exit")
1759
parser.add_argument("-i", "--interface", metavar="IF",
1760
help="Bind to interface IF")
1761
parser.add_argument("-a", "--address",
1762
help="Address to listen for requests on")
1763
parser.add_argument("-p", "--port", type=int,
1764
help="Port number to receive requests on")
1765
parser.add_argument("--check", action="store_true",
1766
help="Run self-test")
1767
parser.add_argument("--debug", action="store_true",
1768
help="Debug mode; run in foreground and log"
1770
parser.add_argument("--debuglevel", metavar="LEVEL",
1771
help="Debug level for stdout output")
1772
parser.add_argument("--priority", help="GnuTLS"
1773
" priority string (see GnuTLS documentation)")
1774
parser.add_argument("--servicename",
1775
metavar="NAME", help="Zeroconf service name")
1776
parser.add_argument("--configdir",
1777
default="/etc/mandos", metavar="DIR",
1778
help="Directory to search for configuration"
1780
parser.add_argument("--no-dbus", action="store_false",
1781
dest="use_dbus", help="Do not provide D-Bus"
1782
" system bus interface")
1783
parser.add_argument("--no-ipv6", action="store_false",
1784
dest="use_ipv6", help="Do not use IPv6")
1785
options = parser.parse_args()
1178
parser = optparse.OptionParser(version = "%%prog %s" % version)
1179
parser.add_option("-i", u"--interface", type=u"string",
1180
metavar="IF", help=u"Bind to interface IF")
1181
parser.add_option("-a", u"--address", type=u"string",
1182
help=u"Address to listen for requests on")
1183
parser.add_option("-p", u"--port", type=u"int",
1184
help=u"Port number to receive requests on")
1185
parser.add_option("--check", action=u"store_true",
1186
help=u"Run self-test")
1187
parser.add_option("--debug", action=u"store_true",
1188
help=u"Debug mode; run in foreground and log to"
1190
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1191
u" priority string (see GnuTLS documentation)")
1192
parser.add_option("--servicename", type=u"string",
1193
metavar=u"NAME", help=u"Zeroconf service name")
1194
parser.add_option("--configdir", type=u"string",
1195
default=u"/etc/mandos", metavar=u"DIR",
1196
help=u"Directory to search for configuration"
1198
parser.add_option("--no-dbus", action=u"store_false",
1200
help=optparse.SUPPRESS_HELP) # XXX: Not done yet
1201
parser.add_option("--no-ipv6", action=u"store_false",
1202
dest=u"use_ipv6", help=u"Do not use IPv6")
1203
options = parser.parse_args()[0]
1787
1205
if options.check:
1836
1253
##################################################################
1838
1255
# For convenience
1839
debug = server_settings["debug"]
1840
debuglevel = server_settings["debuglevel"]
1841
use_dbus = server_settings["use_dbus"]
1842
use_ipv6 = server_settings["use_ipv6"]
1844
if server_settings["servicename"] != "Mandos":
1256
debug = server_settings[u"debug"]
1257
use_dbus = server_settings[u"use_dbus"]
1258
use_dbus = False # XXX: Not done yet
1259
use_ipv6 = server_settings[u"use_ipv6"]
1262
syslogger.setLevel(logging.WARNING)
1263
console.setLevel(logging.WARNING)
1265
if server_settings[u"servicename"] != u"Mandos":
1845
1266
syslogger.setFormatter(logging.Formatter
1846
('Mandos (%s) [%%(process)d]:'
1847
' %%(levelname)s: %%(message)s'
1848
% server_settings["servicename"]))
1267
(u'Mandos (%s) [%%(process)d]:'
1268
u' %%(levelname)s: %%(message)s'
1269
% server_settings[u"servicename"]))
1850
1271
# Parse config file with clients
1851
client_defaults = { "timeout": "5m",
1852
"extended_timeout": "15m",
1854
"checker": "fping -q -- %%(host)s",
1856
"approval_delay": "0s",
1857
"approval_duration": "1s",
1272
client_defaults = { u"timeout": u"1h",
1274
u"checker": u"fping -q -- %%(host)s",
1859
1277
client_config = configparser.SafeConfigParser(client_defaults)
1860
client_config.read(os.path.join(server_settings["configdir"],
1278
client_config.read(os.path.join(server_settings[u"configdir"],
1863
1281
global mandos_dbus_service
1864
1282
mandos_dbus_service = None
1866
tcp_server = MandosServer((server_settings["address"],
1867
server_settings["port"]),
1284
tcp_server = MandosServer((server_settings[u"address"],
1285
server_settings[u"port"]),
1869
interface=(server_settings["interface"]
1287
interface=server_settings[u"interface"],
1871
1288
use_ipv6=use_ipv6,
1872
1289
gnutls_priority=
1873
server_settings["priority"],
1290
server_settings[u"priority"],
1874
1291
use_dbus=use_dbus)
1876
pidfilename = "/var/run/mandos.pid"
1878
pidfile = open(pidfilename, "w")
1880
logger.error("Could not open file %r", pidfilename)
1292
pidfilename = u"/var/run/mandos.pid"
1294
pidfile = open(pidfilename, u"w")
1296
logger.error(u"Could not open file %r", pidfilename)
1883
uid = pwd.getpwnam("_mandos").pw_uid
1884
gid = pwd.getpwnam("_mandos").pw_gid
1299
uid = pwd.getpwnam(u"_mandos").pw_uid
1300
gid = pwd.getpwnam(u"_mandos").pw_gid
1885
1301
except KeyError:
1887
uid = pwd.getpwnam("mandos").pw_uid
1888
gid = pwd.getpwnam("mandos").pw_gid
1303
uid = pwd.getpwnam(u"mandos").pw_uid
1304
gid = pwd.getpwnam(u"mandos").pw_gid
1889
1305
except KeyError:
1891
uid = pwd.getpwnam("nobody").pw_uid
1892
gid = pwd.getpwnam("nobody").pw_gid
1307
uid = pwd.getpwnam(u"nobody").pw_uid
1308
gid = pwd.getpwnam(u"nobody").pw_gid
1893
1309
except KeyError:
1899
except OSError as error:
1315
except OSError, error:
1900
1316
if error[0] != errno.EPERM:
1903
if not debug and not debuglevel:
1904
syslogger.setLevel(logging.WARNING)
1905
console.setLevel(logging.WARNING)
1907
level = getattr(logging, debuglevel.upper())
1908
syslogger.setLevel(level)
1909
console.setLevel(level)
1319
# Enable all possible GnuTLS debugging
1912
# Enable all possible GnuTLS debugging
1914
1321
# "Use a log level over 10 to enable all debugging options."
1915
1322
# - GnuTLS manual
1916
1323
gnutls.library.functions.gnutls_global_set_log_level(11)
1918
1325
@gnutls.library.types.gnutls_log_func
1919
1326
def debug_gnutls(level, string):
1920
logger.debug("GnuTLS: %s", string[:-1])
1327
logger.debug(u"GnuTLS: %s", string[:-1])
1922
1329
(gnutls.library.functions
1923
1330
.gnutls_global_set_log_function(debug_gnutls))
1925
# Redirect stdin so all checkers get /dev/null
1926
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1927
os.dup2(null, sys.stdin.fileno())
1931
# No console logging
1932
logger.removeHandler(console)
1934
# Need to fork before connecting to D-Bus
1936
# Close all input and output, do double fork, etc.
1939
1332
global main_loop
1940
1333
# From the Avahi example code
1943
1336
bus = dbus.SystemBus()
1944
1337
# End of Avahi example code
1947
bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1948
bus, do_not_queue=True)
1949
except dbus.exceptions.NameExistsException as e:
1950
logger.error(unicode(e) + ", disabling D-Bus")
1952
server_settings["use_dbus"] = False
1953
tcp_server.use_dbus = False
1339
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1954
1340
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1955
service = AvahiService(name = server_settings["servicename"],
1956
servicetype = "_mandos._tcp",
1341
service = AvahiService(name = server_settings[u"servicename"],
1342
servicetype = u"_mandos._tcp",
1957
1343
protocol = protocol, bus = bus)
1958
1344
if server_settings["interface"]:
1959
1345
service.interface = (if_nametoindex
1960
(str(server_settings["interface"])))
1962
global multiprocessing_manager
1963
multiprocessing_manager = multiprocessing.Manager()
1346
(str(server_settings[u"interface"])))
1965
1348
client_class = Client
1967
1350
client_class = functools.partial(ClientDBus, bus = bus)
1968
def client_config_items(config, section):
1969
special_settings = {
1970
"approved_by_default":
1971
lambda: config.getboolean(section,
1972
"approved_by_default"),
1974
for name, value in config.items(section):
1976
yield (name, special_settings[name]())
1980
1351
tcp_server.clients.update(set(
1981
1352
client_class(name = section,
1982
config= dict(client_config_items(
1983
client_config, section)))
1353
config= dict(client_config.items(section)))
1984
1354
for section in client_config.sections()))
1985
1355
if not tcp_server.clients:
1986
logger.warning("No clients defined")
1356
logger.warning(u"No clients defined")
1359
# Redirect stdin so all checkers get /dev/null
1360
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1361
os.dup2(null, sys.stdin.fileno())
1365
# No console logging
1366
logger.removeHandler(console)
1367
# Close all input and output, do double fork, etc.
1371
with closing(pidfile):
1373
pidfile.write(str(pid) + "\n")
1376
logger.error(u"Could not write to file %r with PID %d",
1379
# "pidfile" was never created
1384
"Cleanup function; run on exit"
1387
while tcp_server.clients:
1388
client = tcp_server.clients.pop()
1389
client.disable_hook = None
1392
atexit.register(cleanup)
1992
pidfile.write(str(pid) + "\n".encode("utf-8"))
1995
logger.error("Could not write to file %r with PID %d",
1998
# "pidfile" was never created
2002
1395
signal.signal(signal.SIGINT, signal.SIG_IGN)
2004
1396
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2005
1397
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())