206
188
self.group.Commit()
207
189
def entry_group_state_changed(self, state, error):
208
190
"""Derived from the Avahi example code"""
209
logger.debug("Avahi entry group state change: %i", state)
191
logger.debug(u"Avahi state change: %i", state)
211
193
if state == avahi.ENTRY_GROUP_ESTABLISHED:
212
logger.debug("Zeroconf service established.")
194
logger.debug(u"Zeroconf service established.")
213
195
elif state == avahi.ENTRY_GROUP_COLLISION:
214
logger.info("Zeroconf service name collision.")
196
logger.warning(u"Zeroconf service name collision.")
216
198
elif state == avahi.ENTRY_GROUP_FAILURE:
217
logger.critical("Avahi: Error in group state changed %s",
199
logger.critical(u"Avahi: Error in group state changed %s",
219
raise AvahiGroupError("State changed: %s"
201
raise AvahiGroupError(u"State changed: %s"
220
202
% unicode(error))
221
203
def cleanup(self):
222
204
"""Derived from the Avahi example code"""
223
205
if self.group is not None:
226
except (dbus.exceptions.UnknownMethodException,
227
dbus.exceptions.DBusException) as e:
229
207
self.group = None
231
def server_state_changed(self, state, error=None):
208
def server_state_changed(self, state):
232
209
"""Derived from the Avahi example code"""
233
logger.debug("Avahi server state change: %i", state)
234
bad_states = { avahi.SERVER_INVALID:
235
"Zeroconf server invalid",
236
avahi.SERVER_REGISTERING: None,
237
avahi.SERVER_COLLISION:
238
"Zeroconf server name collision",
239
avahi.SERVER_FAILURE:
240
"Zeroconf server failure" }
241
if state in bad_states:
242
if bad_states[state] is not None:
244
logger.error(bad_states[state])
246
logger.error(bad_states[state] + ": %r", error)
210
if state == avahi.SERVER_COLLISION:
211
logger.error(u"Zeroconf server name collision")
248
213
elif state == avahi.SERVER_RUNNING:
252
logger.debug("Unknown state: %r", state)
254
logger.debug("Unknown state: %r: %r", state, error)
255
215
def activate(self):
256
216
"""Derived from the Avahi example code"""
257
217
if self.server is None:
258
218
self.server = dbus.Interface(
259
219
self.bus.get_object(avahi.DBUS_NAME,
260
avahi.DBUS_PATH_SERVER,
261
follow_name_owner_changes=True),
220
avahi.DBUS_PATH_SERVER),
262
221
avahi.DBUS_INTERFACE_SERVER)
263
self.server.connect_to_signal("StateChanged",
222
self.server.connect_to_signal(u"StateChanged",
264
223
self.server_state_changed)
265
224
self.server_state_changed(self.server.GetState())
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))
274
227
class Client(object):
275
228
"""A representation of a client host served by this server.
278
_approved: bool(); 'None' if not yet approved/disapproved
279
approval_delay: datetime.timedelta(); Time to wait for approval
280
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)
281
245
checker: subprocess.Popen(); a running checker process used
282
246
to see if the client lives.
283
247
'None' if no process is running.
284
checker_callback_tag: a gobject event source tag, or None
285
checker_command: string; External command which is run to check
286
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
287
253
runtime with vars(self) as dict, so that for
288
254
instance %(name)s can be used in the command.
289
checker_initiator_tag: a gobject event source tag, or None
290
created: datetime.datetime(); (UTC) object creation
291
255
current_checker_command: string; current running checker_command
292
disable_hook: If set, called by disable() as disable_hook(self)
293
disable_initiator_tag: a gobject event source tag, or None
295
fingerprint: string (40 or 32 hexadecimal digits); used to
296
uniquely identify the client
297
host: string; available for use by the checker command
298
interval: datetime.timedelta(); How often to start a new checker
299
last_approval_request: datetime.datetime(); (UTC) or None
300
last_checked_ok: datetime.datetime(); (UTC) or None
301
last_enabled: datetime.datetime(); (UTC)
302
name: string; from the config file, used in log messages and
304
secret: bytestring; sent verbatim (over TLS) to client
305
timeout: datetime.timedelta(); How long from last_checked_ok
306
until this client is disabled
307
extended_timeout: extra long timeout when password has been sent
308
runtime_expansions: Allowed attributes for runtime expansion.
309
expires: datetime.datetime(); time (UTC) when a client will be
313
runtime_expansions = ("approval_delay", "approval_duration",
314
"created", "enabled", "fingerprint",
315
"host", "interval", "last_checked_ok",
316
"last_enabled", "name", "timeout")
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 _timedelta_to_milliseconds(self.timeout)
322
def extended_timeout_milliseconds(self):
323
"Return the 'extended_timeout' attribute in milliseconds"
324
return _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 _timedelta_to_milliseconds(self.interval)
330
def approval_delay_milliseconds(self):
331
return _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()
318
self.last_enabled = datetime.datetime.utcnow()
396
319
# Schedule a new checker to be started an 'interval' from now,
397
320
# and every interval from then on.
398
321
self.checker_initiator_tag = (gobject.timeout_add
399
322
(self.interval_milliseconds(),
400
323
self.start_checker))
324
# Also start a new checker *right now*.
401
326
# Schedule a disable() when 'timeout' has passed
402
self.expires = datetime.datetime.utcnow() + self.timeout
403
327
self.disable_initiator_tag = (gobject.timeout_add
404
328
(self.timeout_milliseconds(),
406
330
self.enabled = True
407
self.last_enabled = datetime.datetime.utcnow()
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
566
def dbus_service_property(dbus_interface, signature="v",
567
access="readwrite", byte_arrays=False):
568
"""Decorators for marking methods of a DBusObjectWithProperties to
569
become properties on the D-Bus.
571
The decorated method will be called with no arguments by "Get"
572
and with one argument by "Set".
574
The parameters, where they are supported, are the same as
575
dbus.service.method, except there is only "signature", since the
576
type from Get() and the type sent to Set() is the same.
578
# Encoding deeply encoded byte arrays is not supported yet by the
579
# "Set" method, so we fail early here:
580
if byte_arrays and signature != "ay":
581
raise ValueError("Byte arrays not supported for non-'ay'"
582
" signature %r" % signature)
584
func._dbus_is_property = True
585
func._dbus_interface = dbus_interface
586
func._dbus_signature = signature
587
func._dbus_access = access
588
func._dbus_name = func.__name__
589
if func._dbus_name.endswith("_dbus_property"):
590
func._dbus_name = func._dbus_name[:-14]
591
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
596
class DBusPropertyException(dbus.exceptions.DBusException):
597
"""A base class for D-Bus property-related exceptions
599
def __unicode__(self):
600
return unicode(str(self))
603
class DBusPropertyAccessException(DBusPropertyException):
604
"""A property's access permissions disallows an operation.
609
class DBusPropertyNotFound(DBusPropertyException):
610
"""An attempt was made to access a non-existing property.
615
class DBusObjectWithProperties(dbus.service.Object):
616
"""A D-Bus object with properties.
618
Classes inheriting from this can use the dbus_service_property
619
decorator to expose methods as D-Bus properties. It exposes the
620
standard Get(), Set(), and GetAll() methods on the D-Bus.
624
def _is_dbus_property(obj):
625
return getattr(obj, "_dbus_is_property", False)
627
def _get_all_dbus_properties(self):
628
"""Returns a generator of (name, attribute) pairs
630
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
631
for cls in self.__class__.__mro__
632
for name, prop in inspect.getmembers(cls, self._is_dbus_property))
634
def _get_dbus_property(self, interface_name, property_name):
635
"""Returns a bound method if one exists which is a D-Bus
636
property with the specified name and interface.
638
for cls in self.__class__.__mro__:
639
for name, value in inspect.getmembers(cls, self._is_dbus_property):
640
if value._dbus_name == property_name and value._dbus_interface == interface_name:
641
return value.__get__(self)
644
raise DBusPropertyNotFound(self.dbus_object_path + ":"
645
+ interface_name + "."
649
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
651
def Get(self, interface_name, property_name):
652
"""Standard D-Bus property Get() method, see D-Bus standard.
654
prop = self._get_dbus_property(interface_name, property_name)
655
if prop._dbus_access == "write":
656
raise DBusPropertyAccessException(property_name)
658
if not hasattr(value, "variant_level"):
660
return type(value)(value, variant_level=value.variant_level+1)
662
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
663
def Set(self, interface_name, property_name, value):
664
"""Standard D-Bus property Set() method, see D-Bus standard.
666
prop = self._get_dbus_property(interface_name, property_name)
667
if prop._dbus_access == "read":
668
raise DBusPropertyAccessException(property_name)
669
if prop._dbus_get_args_options["byte_arrays"]:
670
# The byte_arrays option is not supported yet on
671
# signatures other than "ay".
672
if prop._dbus_signature != "ay":
674
value = dbus.ByteArray(''.join(unichr(byte)
678
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
679
out_signature="a{sv}")
680
def GetAll(self, interface_name):
681
"""Standard D-Bus property GetAll() method, see D-Bus
684
Note: Will not include properties with access="write".
687
for name, prop in self._get_all_dbus_properties():
689
and interface_name != prop._dbus_interface):
690
# Interface non-empty but did not match
692
# Ignore write-only properties
693
if prop._dbus_access == "write":
696
if not hasattr(value, "variant_level"):
699
all[name] = type(value)(value, variant_level=
700
value.variant_level+1)
701
return dbus.Dictionary(all, signature="sv")
703
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
705
path_keyword='object_path',
706
connection_keyword='connection')
707
def Introspect(self, object_path, connection):
708
"""Standard D-Bus method, overloaded to insert property tags.
710
xmlstring = dbus.service.Object.Introspect(self, object_path,
713
document = xml.dom.minidom.parseString(xmlstring)
714
def make_tag(document, name, prop):
715
e = document.createElement("property")
716
e.setAttribute("name", name)
717
e.setAttribute("type", prop._dbus_signature)
718
e.setAttribute("access", prop._dbus_access)
720
for if_tag in document.getElementsByTagName("interface"):
721
for tag in (make_tag(document, name, prop)
723
in self._get_all_dbus_properties()
724
if prop._dbus_interface
725
== if_tag.getAttribute("name")):
726
if_tag.appendChild(tag)
727
# Add the names to the return values for the
728
# "org.freedesktop.DBus.Properties" methods
729
if (if_tag.getAttribute("name")
730
== "org.freedesktop.DBus.Properties"):
731
for cn in if_tag.getElementsByTagName("method"):
732
if cn.getAttribute("name") == "Get":
733
for arg in cn.getElementsByTagName("arg"):
734
if (arg.getAttribute("direction")
736
arg.setAttribute("name", "value")
737
elif cn.getAttribute("name") == "GetAll":
738
for arg in cn.getElementsByTagName("arg"):
739
if (arg.getAttribute("direction")
741
arg.setAttribute("name", "props")
742
xmlstring = document.toxml("utf-8")
744
except (AttributeError, xml.dom.DOMException,
745
xml.parsers.expat.ExpatError) as error:
746
logger.error("Failed to override Introspection method",
751
def datetime_to_dbus (dt, variant_level=0):
752
"""Convert a UTC datetime.datetime() to a D-Bus type."""
754
return dbus.String("", variant_level = variant_level)
755
return dbus.String(dt.isoformat(),
756
variant_level=variant_level)
758
class transitional_dbus_metaclass(DBusObjectWithProperties.__metaclass__):
759
def __new__(mcs, name, bases, attr):
760
for attrname, old_dbusobj in inspect.getmembers(bases[0]):
761
new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.")
762
if (getattr(old_dbusobj, "_dbus_is_signal", False)
763
and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
764
unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars,
765
old_dbusobj.__closure__))["func"].cell_contents
766
newfunc = types.FunctionType(unwrappedfunc.func_code,
767
unwrappedfunc.func_globals,
768
unwrappedfunc.func_name,
769
unwrappedfunc.func_defaults,
770
unwrappedfunc.func_closure)
771
new_dbusfunc = dbus.service.signal(
772
new_interface, old_dbusobj._dbus_signature)(newfunc)
773
attr["_transitional_" + attrname] = new_dbusfunc
775
def fixscope(func1, func2):
776
def newcall(*args, **kwargs):
777
func1(*args, **kwargs)
778
func2(*args, **kwargs)
781
attr[attrname] = fixscope(old_dbusobj, new_dbusfunc)
783
elif (getattr(old_dbusobj, "_dbus_is_method", False)
784
and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
785
new_dbusfunc = (dbus.service.method
787
old_dbusobj._dbus_in_signature,
788
old_dbusobj._dbus_out_signature)
790
(old_dbusobj.func_code,
791
old_dbusobj.func_globals,
792
old_dbusobj.func_name,
793
old_dbusobj.func_defaults,
794
old_dbusobj.func_closure)))
796
attr[attrname] = new_dbusfunc
797
elif (getattr(old_dbusobj, "_dbus_is_property", False)
798
and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
799
new_dbusfunc = (dbus_service_property
801
old_dbusobj._dbus_signature,
802
old_dbusobj._dbus_access,
803
old_dbusobj._dbus_get_args_options["byte_arrays"])
805
(old_dbusobj.func_code,
806
old_dbusobj.func_globals,
807
old_dbusobj.func_name,
808
old_dbusobj.func_defaults,
809
old_dbusobj.func_closure)))
811
attr[attrname] = new_dbusfunc
812
return type.__new__(mcs, name, bases, attr)
814
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):
815
481
"""A Client class using D-Bus
818
484
dbus_object_path: dbus.ObjectPath
819
485
bus: dbus.SystemBus()
822
runtime_expansions = (Client.runtime_expansions
823
+ ("dbus_object_path",))
825
487
# dbus.service.Object doesn't use super(), so we can't either.
827
489
def __init__(self, bus = None, *args, **kwargs):
828
self._approvals_pending = 0
830
491
Client.__init__(self, *args, **kwargs)
831
492
# Only now, when this client is initialized, can it show up on
833
client_object_name = unicode(self.name).translate(
836
494
self.dbus_object_path = (dbus.ObjectPath
837
("/clients/" + client_object_name))
838
DBusObjectWithProperties.__init__(self, self.bus,
839
self.dbus_object_path)
841
def notifychangeproperty(transform_func,
842
dbus_name, type_func=lambda x: x,
844
""" Modify a variable so that its a property that announce its
846
transform_fun: Function that takes a value and transform it to
848
dbus_name: DBus name of the variable
849
type_func: Function that transform the value before sending it
851
variant_level: DBus variant level. default: 1
854
def setter(self, value):
855
old_value = real_value[0]
856
real_value[0] = value
857
if hasattr(self, "dbus_object_path"):
858
if type_func(old_value) != type_func(real_value[0]):
859
dbus_value = transform_func(type_func(real_value[0]),
861
self.PropertyChanged(dbus.String(dbus_name),
864
return property(lambda self: real_value[0], setter)
867
expires = notifychangeproperty(datetime_to_dbus, "Expires")
868
approvals_pending = notifychangeproperty(dbus.Boolean,
871
enabled = notifychangeproperty(dbus.Boolean, "Enabled")
872
last_enabled = notifychangeproperty(datetime_to_dbus,
874
checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
875
type_func = lambda checker: checker is not None)
876
last_checked_ok = notifychangeproperty(datetime_to_dbus,
878
last_approval_request = notifychangeproperty(datetime_to_dbus,
879
"LastApprovalRequest")
880
approved_by_default = notifychangeproperty(dbus.Boolean,
882
approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
883
type_func = _timedelta_to_milliseconds)
884
approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
885
type_func = _timedelta_to_milliseconds)
886
host = notifychangeproperty(dbus.String, "Host")
887
timeout = notifychangeproperty(dbus.UInt16, "Timeout",
888
type_func = _timedelta_to_milliseconds)
889
extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
890
type_func = _timedelta_to_milliseconds)
891
interval = notifychangeproperty(dbus.UInt16, "Interval",
892
type_func = _timedelta_to_milliseconds)
893
checker_command = notifychangeproperty(dbus.String, "Checker")
895
del notifychangeproperty
496
+ self.name.replace(u".", u"_")))
497
dbus.service.Object.__init__(self, self.bus,
498
self.dbus_object_path)
501
def _datetime_to_dbus(dt, variant_level=0):
502
"""Convert a UTC datetime.datetime() to a D-Bus type."""
503
return dbus.String(dt.isoformat(),
504
variant_level=variant_level)
507
oldstate = getattr(self, u"enabled", False)
508
r = Client.enable(self)
509
if oldstate != self.enabled:
511
self.PropertyChanged(dbus.String(u"enabled"),
512
dbus.Boolean(True, variant_level=1))
513
self.PropertyChanged(
514
dbus.String(u"last_enabled"),
515
self._datetime_to_dbus(self.last_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:
524
self.PropertyChanged(dbus.String(u"enabled"),
525
dbus.Boolean(False, variant_level=1))
897
528
def __del__(self, *args, **kwargs):
899
530
self.remove_from_connection()
900
531
except LookupError:
902
if hasattr(DBusObjectWithProperties, "__del__"):
903
DBusObjectWithProperties.__del__(self, *args, **kwargs)
533
if hasattr(dbus.service.Object, u"__del__"):
534
dbus.service.Object.__del__(self, *args, **kwargs)
904
535
Client.__del__(self, *args, **kwargs)
906
537
def checker_callback(self, pid, condition, command,
907
538
*args, **kwargs):
908
539
self.checker_callback_tag = None
909
540
self.checker = None
542
self.PropertyChanged(dbus.String(u"checker_running"),
543
dbus.Boolean(False, variant_level=1))
910
544
if os.WIFEXITED(condition):
911
545
exitstatus = os.WEXITSTATUS(condition)
912
546
# Emit D-Bus signal
934
577
and old_checker_pid != self.checker.pid):
935
578
# Emit D-Bus signal
936
579
self.CheckerStarted(self.current_checker_command)
939
def _reset_approved(self):
940
self._approved = None
943
def approve(self, value=True):
944
self.send_changedstate()
945
self._approved = value
946
gobject.timeout_add(_timedelta_to_milliseconds
947
(self.approval_duration),
948
self._reset_approved)
951
## D-Bus methods, signals & properties
952
_interface = "se.bsnet.fukt.Mandos.Client"
580
self.PropertyChanged(
581
dbus.String(u"checker_running"),
582
dbus.Boolean(True, variant_level=1))
585
def stop_checker(self, *args, **kwargs):
586
old_checker = getattr(self, u"checker", None)
587
r = Client.stop_checker(self, *args, **kwargs)
588
if (old_checker is not None
589
and getattr(self, u"checker", None) is None):
590
self.PropertyChanged(dbus.String(u"checker_running"),
591
dbus.Boolean(False, variant_level=1))
594
## D-Bus methods & signals
595
_interface = u"se.bsnet.fukt.Mandos.Client"
598
@dbus.service.method(_interface)
600
return self.checked_ok()
956
602
# CheckerCompleted - signal
957
@dbus.service.signal(_interface, signature="nxs")
603
@dbus.service.signal(_interface, signature=u"nxs")
958
604
def CheckerCompleted(self, exitcode, waitstatus, command):
962
608
# CheckerStarted - signal
963
@dbus.service.signal(_interface, signature="s")
609
@dbus.service.signal(_interface, signature=u"s")
964
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()
968
662
# PropertyChanged - signal
969
@dbus.service.signal(_interface, signature="sv")
663
@dbus.service.signal(_interface, signature=u"sv")
970
664
def PropertyChanged(self, property, value):
668
# ReceivedSecret - signal
975
669
@dbus.service.signal(_interface)
978
Is sent after a successful transfer of secret from the Mandos
979
server to mandos-client
670
def ReceivedSecret(self):
983
674
# Rejected - signal
984
@dbus.service.signal(_interface, signature="s")
985
def Rejected(self, reason):
675
@dbus.service.signal(_interface)
989
# NeedApproval - signal
990
@dbus.service.signal(_interface, signature="tb")
991
def NeedApproval(self, timeout, default):
993
return self.need_approval()
998
@dbus.service.method(_interface, in_signature="b")
999
def Approve(self, value):
1002
# CheckedOK - method
1003
@dbus.service.method(_interface)
1004
def CheckedOK(self):
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(),
1007
724
# Enable - method
1008
725
@dbus.service.method(_interface)
1027
744
def StopChecker(self):
1028
745
self.stop_checker()
1032
# ApprovalPending - property
1033
@dbus_service_property(_interface, signature="b", access="read")
1034
def ApprovalPending_dbus_property(self):
1035
return dbus.Boolean(bool(self.approvals_pending))
1037
# ApprovedByDefault - property
1038
@dbus_service_property(_interface, signature="b",
1040
def ApprovedByDefault_dbus_property(self, value=None):
1041
if value is None: # get
1042
return dbus.Boolean(self.approved_by_default)
1043
self.approved_by_default = bool(value)
1045
# ApprovalDelay - property
1046
@dbus_service_property(_interface, signature="t",
1048
def ApprovalDelay_dbus_property(self, value=None):
1049
if value is None: # get
1050
return dbus.UInt64(self.approval_delay_milliseconds())
1051
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1053
# ApprovalDuration - property
1054
@dbus_service_property(_interface, signature="t",
1056
def ApprovalDuration_dbus_property(self, value=None):
1057
if value is None: # get
1058
return dbus.UInt64(_timedelta_to_milliseconds(
1059
self.approval_duration))
1060
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1063
@dbus_service_property(_interface, signature="s", access="read")
1064
def Name_dbus_property(self):
1065
return dbus.String(self.name)
1067
# Fingerprint - property
1068
@dbus_service_property(_interface, signature="s", access="read")
1069
def Fingerprint_dbus_property(self):
1070
return dbus.String(self.fingerprint)
1073
@dbus_service_property(_interface, signature="s",
1075
def Host_dbus_property(self, value=None):
1076
if value is None: # get
1077
return dbus.String(self.host)
1080
# Created - property
1081
@dbus_service_property(_interface, signature="s", access="read")
1082
def Created_dbus_property(self):
1083
return dbus.String(datetime_to_dbus(self.created))
1085
# LastEnabled - property
1086
@dbus_service_property(_interface, signature="s", access="read")
1087
def LastEnabled_dbus_property(self):
1088
return datetime_to_dbus(self.last_enabled)
1090
# Enabled - property
1091
@dbus_service_property(_interface, signature="b",
1093
def Enabled_dbus_property(self, value=None):
1094
if value is None: # get
1095
return dbus.Boolean(self.enabled)
1101
# LastCheckedOK - property
1102
@dbus_service_property(_interface, signature="s",
1104
def LastCheckedOK_dbus_property(self, value=None):
1105
if value is not None:
1108
return datetime_to_dbus(self.last_checked_ok)
1110
# Expires - property
1111
@dbus_service_property(_interface, signature="s", access="read")
1112
def Expires_dbus_property(self):
1113
return datetime_to_dbus(self.expires)
1115
# LastApprovalRequest - property
1116
@dbus_service_property(_interface, signature="s", access="read")
1117
def LastApprovalRequest_dbus_property(self):
1118
return datetime_to_dbus(self.last_approval_request)
1120
# Timeout - property
1121
@dbus_service_property(_interface, signature="t",
1123
def Timeout_dbus_property(self, value=None):
1124
if value is None: # get
1125
return dbus.UInt64(self.timeout_milliseconds())
1126
self.timeout = datetime.timedelta(0, 0, 0, value)
1127
if getattr(self, "disable_initiator_tag", None) is None:
1129
# Reschedule timeout
1130
gobject.source_remove(self.disable_initiator_tag)
1131
self.disable_initiator_tag = None
1133
time_to_die = (self.
1134
_timedelta_to_milliseconds((self
1139
if time_to_die <= 0:
1140
# The timeout has passed
1143
self.expires = (datetime.datetime.utcnow()
1144
+ datetime.timedelta(milliseconds = time_to_die))
1145
self.disable_initiator_tag = (gobject.timeout_add
1146
(time_to_die, self.disable))
1148
# ExtendedTimeout - property
1149
@dbus_service_property(_interface, signature="t",
1151
def ExtendedTimeout_dbus_property(self, value=None):
1152
if value is None: # get
1153
return dbus.UInt64(self.extended_timeout_milliseconds())
1154
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1156
# Interval - property
1157
@dbus_service_property(_interface, signature="t",
1159
def Interval_dbus_property(self, value=None):
1160
if value is None: # get
1161
return dbus.UInt64(self.interval_milliseconds())
1162
self.interval = datetime.timedelta(0, 0, 0, value)
1163
if getattr(self, "checker_initiator_tag", None) is None:
1165
# Reschedule checker run
1166
gobject.source_remove(self.checker_initiator_tag)
1167
self.checker_initiator_tag = (gobject.timeout_add
1168
(value, self.start_checker))
1169
self.start_checker() # Start one now, too
1171
# Checker - property
1172
@dbus_service_property(_interface, signature="s",
1174
def Checker_dbus_property(self, value=None):
1175
if value is None: # get
1176
return dbus.String(self.checker_command)
1177
self.checker_command = value
1179
# CheckerRunning - property
1180
@dbus_service_property(_interface, signature="b",
1182
def CheckerRunning_dbus_property(self, value=None):
1183
if value is None: # get
1184
return dbus.Boolean(self.checker is not None)
1186
self.start_checker()
1190
# ObjectPath - property
1191
@dbus_service_property(_interface, signature="o", access="read")
1192
def ObjectPath_dbus_property(self):
1193
return self.dbus_object_path # is already a dbus.ObjectPath
1196
@dbus_service_property(_interface, signature="ay",
1197
access="write", byte_arrays=True)
1198
def Secret_dbus_property(self, value):
1199
self.secret = str(value)
1204
class ProxyClient(object):
1205
def __init__(self, child_pipe, fpr, address):
1206
self._pipe = child_pipe
1207
self._pipe.send(('init', fpr, address))
1208
if not self._pipe.recv():
1211
def __getattribute__(self, name):
1212
if(name == '_pipe'):
1213
return super(ProxyClient, self).__getattribute__(name)
1214
self._pipe.send(('getattr', name))
1215
data = self._pipe.recv()
1216
if data[0] == 'data':
1218
if data[0] == 'function':
1219
def func(*args, **kwargs):
1220
self._pipe.send(('funcall', name, args, kwargs))
1221
return self._pipe.recv()[1]
1224
def __setattr__(self, name, value):
1225
if(name == '_pipe'):
1226
return super(ProxyClient, self).__setattr__(name, value)
1227
self._pipe.send(('setattr', name, value))
1229
class ClientDBusTransitional(ClientDBus):
1230
__metaclass__ = transitional_dbus_metaclass
1232
750
class ClientHandler(socketserver.BaseRequestHandler, object):
1233
751
"""A class to handle client connections.
1236
754
Note: This will run in its own forked process."""
1238
756
def handle(self):
1239
with contextlib.closing(self.server.child_pipe) as child_pipe:
1240
logger.info("TCP connection from: %s",
1241
unicode(self.client_address))
1242
logger.debug("Pipe FD: %d",
1243
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:
1245
762
session = (gnutls.connection
1246
763
.ClientSession(self.request,
1247
764
gnutls.connection
1248
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)
1250
776
# Note: gnutls.connection.X509Credentials is really a
1251
777
# generic GnuTLS certificate credentials object so long as
1252
778
# no X.509 keys are added to it. Therefore, we can use it
1253
779
# here despite using OpenPGP certificates.
1255
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1256
# "+AES-256-CBC", "+SHA1",
1257
# "+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",
1259
785
# Use a fallback default, since this MUST be set.
1260
786
priority = self.server.gnutls_priority
1261
787
if priority is None:
1263
789
(gnutls.library.functions
1264
790
.gnutls_priority_set_direct(session._c_object,
1265
791
priority, None))
1267
# Start communication using the Mandos protocol
1268
# Get protocol number
1269
line = self.request.makefile().readline()
1270
logger.debug("Protocol version: %r", line)
1272
if int(line.strip().split()[0]) > 1:
1274
except (ValueError, IndexError, RuntimeError) as error:
1275
logger.error("Unknown protocol version: %s", error)
1278
# Start GnuTLS connection
1280
794
session.handshake()
1281
except gnutls.errors.GNUTLSError as error:
1282
logger.warning("Handshake failed: %s", error)
795
except gnutls.errors.GNUTLSError, error:
796
logger.warning(u"Handshake failed: %s", error)
1283
797
# Do not run session.bye() here: the session is not
1284
798
# established. Just abandon the request.
1286
logger.debug("Handshake succeeded")
1288
approval_required = False
800
logger.debug(u"Handshake succeeded")
1291
fpr = self.fingerprint(self.peer_certificate
1294
gnutls.errors.GNUTLSError) as error:
1295
logger.warning("Bad certificate: %s", error)
1297
logger.debug("Fingerprint: %s", fpr)
1300
client = ProxyClient(child_pipe, fpr,
1301
self.client_address)
1305
if client.approval_delay:
1306
delay = client.approval_delay
1307
client.approvals_pending += 1
1308
approval_required = True
1311
if not client.enabled:
1312
logger.info("Client %s is disabled",
1314
if self.server.use_dbus:
1316
client.Rejected("Disabled")
1319
if client._approved or not client.approval_delay:
1320
#We are approved or approval is disabled
1322
elif client._approved is None:
1323
logger.info("Client %s needs approval",
1325
if self.server.use_dbus:
1327
client.NeedApproval(
1328
client.approval_delay_milliseconds(),
1329
client.approved_by_default)
1331
logger.warning("Client %s was not approved",
1333
if self.server.use_dbus:
1335
client.Rejected("Denied")
1338
#wait until timeout or approved
1339
#x = float(client._timedelta_to_milliseconds(delay))
1340
time = datetime.datetime.now()
1341
client.changedstate.acquire()
1342
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1343
client.changedstate.release()
1344
time2 = datetime.datetime.now()
1345
if (time2 - time) >= delay:
1346
if not client.approved_by_default:
1347
logger.warning("Client %s timed out while"
1348
" waiting for approval",
1350
if self.server.use_dbus:
1352
client.Rejected("Approval timed out")
1357
delay -= time2 - time
1360
while sent_size < len(client.secret):
1362
sent = session.send(client.secret[sent_size:])
1363
except gnutls.errors.GNUTLSError as error:
1364
logger.warning("gnutls send failed")
1366
logger.debug("Sent: %d, remaining: %d",
1367
sent, len(client.secret)
1368
- (sent_size + sent))
1371
logger.info("Sending secret to %s", client.name)
1372
# bump the timeout as if seen
1373
client.checked_ok(client.extended_timeout)
1374
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)
1379
if approval_required:
1380
client.approvals_pending -= 1
1383
except gnutls.errors.GNUTLSError as error:
1384
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))
1387
836
def peer_certificate(session):
1595
1026
for cond, name in
1596
1027
condition_names.iteritems()
1597
1028
if cond & condition)
1598
# error or the other end of multiprocessing.Pipe has closed
1599
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1602
# Read a request from the child
1603
request = parent_pipe.recv()
1604
command = request[0]
1606
if command == 'init':
1608
address = request[2]
1610
for c in self.clients:
1611
if c.fingerprint == fpr:
1615
logger.info("Client not found for fingerprint: %s, ad"
1616
"dress: %s", fpr, address)
1619
mandos_dbus_service.ClientNotFound(fpr, address[0])
1620
parent_pipe.send(False)
1623
gobject.io_add_watch(parent_pipe.fileno(),
1624
gobject.IO_IN | gobject.IO_HUP,
1625
functools.partial(self.handle_ipc,
1626
parent_pipe = parent_pipe,
1627
client_object = client))
1628
parent_pipe.send(True)
1629
# remove the old hook in favor of the new above hook on same fileno
1631
if command == 'funcall':
1632
funcname = request[1]
1636
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1638
if command == 'getattr':
1639
attrname = request[1]
1640
if callable(client_object.__getattribute__(attrname)):
1641
parent_pipe.send(('function',))
1643
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1645
if command == 'setattr':
1646
attrname = request[1]
1648
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
1653
1086
def string_to_delta(interval):
1654
1087
"""Parse a string and return a datetime.timedelta
1656
>>> string_to_delta('7d')
1089
>>> string_to_delta(u'7d')
1657
1090
datetime.timedelta(7)
1658
>>> string_to_delta('60s')
1091
>>> string_to_delta(u'60s')
1659
1092
datetime.timedelta(0, 60)
1660
>>> string_to_delta('60m')
1093
>>> string_to_delta(u'60m')
1661
1094
datetime.timedelta(0, 3600)
1662
>>> string_to_delta('24h')
1095
>>> string_to_delta(u'24h')
1663
1096
datetime.timedelta(1)
1664
>>> string_to_delta('1w')
1097
>>> string_to_delta(u'1w')
1665
1098
datetime.timedelta(7)
1666
>>> string_to_delta('5m 30s')
1099
>>> string_to_delta(u'5m 30s')
1667
1100
datetime.timedelta(0, 330)
1669
1102
timevalue = datetime.timedelta(0)
1743
##################################################################
1175
######################################################################
1744
1176
# Parsing of options, both command line and config file
1746
parser = argparse.ArgumentParser()
1747
parser.add_argument("-v", "--version", action="version",
1748
version = "%%(prog)s %s" % version,
1749
help="show version number and exit")
1750
parser.add_argument("-i", "--interface", metavar="IF",
1751
help="Bind to interface IF")
1752
parser.add_argument("-a", "--address",
1753
help="Address to listen for requests on")
1754
parser.add_argument("-p", "--port", type=int,
1755
help="Port number to receive requests on")
1756
parser.add_argument("--check", action="store_true",
1757
help="Run self-test")
1758
parser.add_argument("--debug", action="store_true",
1759
help="Debug mode; run in foreground and log"
1761
parser.add_argument("--debuglevel", metavar="LEVEL",
1762
help="Debug level for stdout output")
1763
parser.add_argument("--priority", help="GnuTLS"
1764
" priority string (see GnuTLS documentation)")
1765
parser.add_argument("--servicename",
1766
metavar="NAME", help="Zeroconf service name")
1767
parser.add_argument("--configdir",
1768
default="/etc/mandos", metavar="DIR",
1769
help="Directory to search for configuration"
1771
parser.add_argument("--no-dbus", action="store_false",
1772
dest="use_dbus", help="Do not provide D-Bus"
1773
" system bus interface")
1774
parser.add_argument("--no-ipv6", action="store_false",
1775
dest="use_ipv6", help="Do not use IPv6")
1776
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",
1199
dest=u"use_dbus", help=u"Do not provide D-Bus"
1200
u" system bus interface")
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]
1778
1205
if options.check:
1827
1253
##################################################################
1829
1255
# For convenience
1830
debug = server_settings["debug"]
1831
debuglevel = server_settings["debuglevel"]
1832
use_dbus = server_settings["use_dbus"]
1833
use_ipv6 = server_settings["use_ipv6"]
1835
if server_settings["servicename"] != "Mandos":
1256
debug = server_settings[u"debug"]
1257
use_dbus = server_settings[u"use_dbus"]
1258
use_ipv6 = server_settings[u"use_ipv6"]
1261
syslogger.setLevel(logging.WARNING)
1262
console.setLevel(logging.WARNING)
1264
if server_settings[u"servicename"] != u"Mandos":
1836
1265
syslogger.setFormatter(logging.Formatter
1837
('Mandos (%s) [%%(process)d]:'
1838
' %%(levelname)s: %%(message)s'
1839
% server_settings["servicename"]))
1266
(u'Mandos (%s) [%%(process)d]:'
1267
u' %%(levelname)s: %%(message)s'
1268
% server_settings[u"servicename"]))
1841
1270
# Parse config file with clients
1842
client_defaults = { "timeout": "5m",
1843
"extended_timeout": "15m",
1845
"checker": "fping -q -- %%(host)s",
1847
"approval_delay": "0s",
1848
"approval_duration": "1s",
1271
client_defaults = { u"timeout": u"1h",
1273
u"checker": u"fping -q -- %%(host)s",
1850
1276
client_config = configparser.SafeConfigParser(client_defaults)
1851
client_config.read(os.path.join(server_settings["configdir"],
1277
client_config.read(os.path.join(server_settings[u"configdir"],
1854
1280
global mandos_dbus_service
1855
1281
mandos_dbus_service = None
1857
tcp_server = MandosServer((server_settings["address"],
1858
server_settings["port"]),
1283
tcp_server = MandosServer((server_settings[u"address"],
1284
server_settings[u"port"]),
1860
interface=(server_settings["interface"]
1286
interface=server_settings[u"interface"],
1862
1287
use_ipv6=use_ipv6,
1863
1288
gnutls_priority=
1864
server_settings["priority"],
1289
server_settings[u"priority"],
1865
1290
use_dbus=use_dbus)
1867
pidfilename = "/var/run/mandos.pid"
1869
pidfile = open(pidfilename, "w")
1871
logger.error("Could not open file %r", pidfilename)
1291
pidfilename = u"/var/run/mandos.pid"
1293
pidfile = open(pidfilename, u"w")
1295
logger.error(u"Could not open file %r", pidfilename)
1874
uid = pwd.getpwnam("_mandos").pw_uid
1875
gid = pwd.getpwnam("_mandos").pw_gid
1298
uid = pwd.getpwnam(u"_mandos").pw_uid
1299
gid = pwd.getpwnam(u"_mandos").pw_gid
1876
1300
except KeyError:
1878
uid = pwd.getpwnam("mandos").pw_uid
1879
gid = pwd.getpwnam("mandos").pw_gid
1302
uid = pwd.getpwnam(u"mandos").pw_uid
1303
gid = pwd.getpwnam(u"mandos").pw_gid
1880
1304
except KeyError:
1882
uid = pwd.getpwnam("nobody").pw_uid
1883
gid = pwd.getpwnam("nobody").pw_gid
1306
uid = pwd.getpwnam(u"nobody").pw_uid
1307
gid = pwd.getpwnam(u"nobody").pw_gid
1884
1308
except KeyError:
1890
except OSError as error:
1314
except OSError, error:
1891
1315
if error[0] != errno.EPERM:
1894
if not debug and not debuglevel:
1895
syslogger.setLevel(logging.WARNING)
1896
console.setLevel(logging.WARNING)
1898
level = getattr(logging, debuglevel.upper())
1899
syslogger.setLevel(level)
1900
console.setLevel(level)
1318
# Enable all possible GnuTLS debugging
1903
# Enable all possible GnuTLS debugging
1905
1320
# "Use a log level over 10 to enable all debugging options."
1906
1321
# - GnuTLS manual
1907
1322
gnutls.library.functions.gnutls_global_set_log_level(11)
1909
1324
@gnutls.library.types.gnutls_log_func
1910
1325
def debug_gnutls(level, string):
1911
logger.debug("GnuTLS: %s", string[:-1])
1326
logger.debug(u"GnuTLS: %s", string[:-1])
1913
1328
(gnutls.library.functions
1914
1329
.gnutls_global_set_log_function(debug_gnutls))
1916
# Redirect stdin so all checkers get /dev/null
1917
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1918
os.dup2(null, sys.stdin.fileno())
1922
# No console logging
1923
logger.removeHandler(console)
1925
# Need to fork before connecting to D-Bus
1927
# Close all input and output, do double fork, etc.
1930
1331
global main_loop
1931
1332
# From the Avahi example code
1934
1335
bus = dbus.SystemBus()
1935
1336
# End of Avahi example code
1938
bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1939
bus, do_not_queue=True)
1940
bus_name2 = dbus.service.BusName("se.recompile.Mandos",
1941
bus, do_not_queue=True)
1942
except dbus.exceptions.NameExistsException as e:
1943
logger.error(unicode(e) + ", disabling D-Bus")
1945
server_settings["use_dbus"] = False
1946
tcp_server.use_dbus = False
1338
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1947
1339
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1948
service = AvahiService(name = server_settings["servicename"],
1949
servicetype = "_mandos._tcp",
1340
service = AvahiService(name = server_settings[u"servicename"],
1341
servicetype = u"_mandos._tcp",
1950
1342
protocol = protocol, bus = bus)
1951
1343
if server_settings["interface"]:
1952
1344
service.interface = (if_nametoindex
1953
(str(server_settings["interface"])))
1955
global multiprocessing_manager
1956
multiprocessing_manager = multiprocessing.Manager()
1345
(str(server_settings[u"interface"])))
1958
1347
client_class = Client
1960
client_class = functools.partial(ClientDBusTransitional, bus = bus)
1961
def client_config_items(config, section):
1962
special_settings = {
1963
"approved_by_default":
1964
lambda: config.getboolean(section,
1965
"approved_by_default"),
1967
for name, value in config.items(section):
1969
yield (name, special_settings[name]())
1349
client_class = functools.partial(ClientDBus, bus = bus)
1973
1350
tcp_server.clients.update(set(
1974
1351
client_class(name = section,
1975
config= dict(client_config_items(
1976
client_config, section)))
1352
config= dict(client_config.items(section)))
1977
1353
for section in client_config.sections()))
1978
1354
if not tcp_server.clients:
1979
logger.warning("No clients defined")
1355
logger.warning(u"No clients defined")
1358
# Redirect stdin so all checkers get /dev/null
1359
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1360
os.dup2(null, sys.stdin.fileno())
1364
# No console logging
1365
logger.removeHandler(console)
1366
# Close all input and output, do double fork, etc.
1370
with closing(pidfile):
1372
pidfile.write(str(pid) + "\n")
1375
logger.error(u"Could not write to file %r with PID %d",
1378
# "pidfile" was never created
1383
"Cleanup function; run on exit"
1386
while tcp_server.clients:
1387
client = tcp_server.clients.pop()
1388
client.disable_hook = None
1391
atexit.register(cleanup)
1985
pidfile.write(str(pid) + "\n".encode("utf-8"))
1988
logger.error("Could not write to file %r with PID %d",
1991
# "pidfile" was never created
1995
1394
signal.signal(signal.SIGINT, signal.SIG_IGN)
1997
1395
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1998
1396
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())