183
174
def password_encode(self, password):
184
175
# Passphrase can not be empty and can not contain newlines or
185
176
# NUL bytes. So we prefix it and hex encode it.
186
encoded = b"mandos" + binascii.hexlify(password)
187
if len(encoded) > 2048:
188
# GnuPG can't handle long passwords, so encode differently
189
encoded = (b"mandos" + password.replace(b"\\", b"\\\\")
190
.replace(b"\n", b"\\n")
191
.replace(b"\0", b"\\x00"))
177
return b"mandos" + binascii.hexlify(password)
194
179
def encrypt(self, data, password):
195
passphrase = self.password_encode(password)
196
with tempfile.NamedTemporaryFile(
197
dir=self.tempdir) as passfile:
198
passfile.write(passphrase)
200
proc = subprocess.Popen(['gpg', '--symmetric',
204
stdin = subprocess.PIPE,
205
stdout = subprocess.PIPE,
206
stderr = subprocess.PIPE)
207
ciphertext, err = proc.communicate(input = data)
208
if proc.returncode != 0:
180
self.gnupg.passphrase = self.password_encode(password)
181
with open(os.devnull, "w") as devnull:
183
proc = self.gnupg.run(['--symmetric'],
184
create_fhs=['stdin', 'stdout'],
185
attach_fhs={'stderr': devnull})
186
with contextlib.closing(proc.handles['stdin']) as f:
188
with contextlib.closing(proc.handles['stdout']) as f:
189
ciphertext = f.read()
193
self.gnupg.passphrase = None
210
194
return ciphertext
212
196
def decrypt(self, data, password):
213
passphrase = self.password_encode(password)
214
with tempfile.NamedTemporaryFile(
215
dir = self.tempdir) as passfile:
216
passfile.write(passphrase)
218
proc = subprocess.Popen(['gpg', '--decrypt',
222
stdin = subprocess.PIPE,
223
stdout = subprocess.PIPE,
224
stderr = subprocess.PIPE)
225
decrypted_plaintext, err = proc.communicate(input = data)
226
if proc.returncode != 0:
197
self.gnupg.passphrase = self.password_encode(password)
198
with open(os.devnull, "w") as devnull:
200
proc = self.gnupg.run(['--decrypt'],
201
create_fhs=['stdin', 'stdout'],
202
attach_fhs={'stderr': devnull})
203
with contextlib.closing(proc.handles['stdin']) as f:
205
with contextlib.closing(proc.handles['stdout']) as f:
206
decrypted_plaintext = f.read()
210
self.gnupg.passphrase = None
228
211
return decrypted_plaintext
231
214
class AvahiError(Exception):
232
215
def __init__(self, value, *args, **kwargs):
233
216
self.value = value
234
return super(AvahiError, self).__init__(value, *args,
217
super(AvahiError, self).__init__(value, *args, **kwargs)
218
def __unicode__(self):
219
return unicode(repr(self.value))
238
221
class AvahiServiceError(AvahiError):
242
224
class AvahiGroupError(AvahiError):
416
376
follow_name_owner_changes=True),
417
377
avahi.DBUS_INTERFACE_SERVER)
418
378
self.server.connect_to_signal("StateChanged",
419
self.server_state_changed)
379
self.server_state_changed)
420
380
self.server_state_changed(self.server.GetState())
423
382
class AvahiServiceToSyslog(AvahiService):
424
def rename(self, *args, **kwargs):
425
384
"""Add the new name to the syslog messages"""
426
ret = AvahiService.rename(self, *args, **kwargs)
427
syslogger.setFormatter(logging.Formatter(
428
'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
385
ret = AvahiService.rename(self)
386
syslogger.setFormatter(logging.Formatter
387
('Mandos ({0}) [%(process)d]:'
388
' %(levelname)s: %(message)s'
432
# Pretend that we have a GnuTLS module
433
class GnuTLS(object):
434
"""This isn't so much a class as it is a module-like namespace.
435
It is instantiated once, and simulates having a GnuTLS module."""
437
_library = ctypes.cdll.LoadLibrary(
438
ctypes.util.find_library("gnutls"))
439
_need_version = "3.3.0"
441
# Need to use class name "GnuTLS" here, since this method is
442
# called before the assignment to the "gnutls" global variable
444
if GnuTLS.check_version(self._need_version) is None:
445
raise GnuTLS.Error("Needs GnuTLS {} or later"
446
.format(self._need_version))
448
# Unless otherwise indicated, the constants and types below are
449
# all from the gnutls/gnutls.h C header file.
457
E_NO_CERTIFICATE_FOUND = -49
458
OPENPGP_FMT_RAW = 0 # gnutls/openpgp.h
461
class session_int(ctypes.Structure):
463
session_t = ctypes.POINTER(session_int)
464
class certificate_credentials_st(ctypes.Structure):
466
certificate_credentials_t = ctypes.POINTER(
467
certificate_credentials_st)
468
certificate_type_t = ctypes.c_int
469
class datum_t(ctypes.Structure):
470
_fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
471
('size', ctypes.c_uint)]
472
class openpgp_crt_int(ctypes.Structure):
474
openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
475
openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
476
log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
477
credentials_type_t = ctypes.c_int #
478
transport_ptr_t = ctypes.c_void_p
479
close_request_t = ctypes.c_int
482
class Error(Exception):
483
# We need to use the class name "GnuTLS" here, since this
484
# exception might be raised from within GnuTLS.__init__,
485
# which is called before the assignment to the "gnutls"
486
# global variable happens.
487
def __init__(self, message = None, code = None, args=()):
488
# Default usage is by a message string, but if a return
489
# code is passed, convert it to a string with
491
if message is None and code is not None:
492
message = GnuTLS.strerror(code)
493
return super(GnuTLS.Error, self).__init__(
496
class CertificateSecurityError(Error):
500
class Credentials(object):
502
self._c_object = gnutls.certificate_credentials_t()
503
gnutls.certificate_allocate_credentials(
504
ctypes.byref(self._c_object))
505
self.type = gnutls.CRD_CERTIFICATE
508
gnutls.certificate_free_credentials(self._c_object)
510
class ClientSession(object):
511
def __init__(self, socket, credentials = None):
512
self._c_object = gnutls.session_t()
513
gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT)
514
gnutls.set_default_priority(self._c_object)
515
gnutls.transport_set_ptr(self._c_object, socket.fileno())
516
gnutls.handshake_set_private_extensions(self._c_object,
519
if credentials is None:
520
credentials = gnutls.Credentials()
521
gnutls.credentials_set(self._c_object, credentials.type,
522
ctypes.cast(credentials._c_object,
524
self.credentials = credentials
527
gnutls.deinit(self._c_object)
530
return gnutls.handshake(self._c_object)
532
def send(self, data):
536
return gnutls.record_send(self._c_object, data, len(data))
539
return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
541
# Error handling function
542
def _error_code(result):
543
"""A function to raise exceptions on errors, suitable
544
for the 'restype' attribute on ctypes functions"""
547
if result == gnutls.E_NO_CERTIFICATE_FOUND:
548
raise gnutls.CertificateSecurityError(code = result)
549
raise gnutls.Error(code = result)
551
# Unless otherwise indicated, the function declarations below are
552
# all from the gnutls/gnutls.h C header file.
555
priority_set_direct = _library.gnutls_priority_set_direct
556
priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
557
ctypes.POINTER(ctypes.c_char_p)]
558
priority_set_direct.restype = _error_code
560
init = _library.gnutls_init
561
init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
562
init.restype = _error_code
564
set_default_priority = _library.gnutls_set_default_priority
565
set_default_priority.argtypes = [session_t]
566
set_default_priority.restype = _error_code
568
record_send = _library.gnutls_record_send
569
record_send.argtypes = [session_t, ctypes.c_void_p,
571
record_send.restype = ctypes.c_ssize_t
573
certificate_allocate_credentials = (
574
_library.gnutls_certificate_allocate_credentials)
575
certificate_allocate_credentials.argtypes = [
576
ctypes.POINTER(certificate_credentials_t)]
577
certificate_allocate_credentials.restype = _error_code
579
certificate_free_credentials = (
580
_library.gnutls_certificate_free_credentials)
581
certificate_free_credentials.argtypes = [certificate_credentials_t]
582
certificate_free_credentials.restype = None
584
handshake_set_private_extensions = (
585
_library.gnutls_handshake_set_private_extensions)
586
handshake_set_private_extensions.argtypes = [session_t,
588
handshake_set_private_extensions.restype = None
590
credentials_set = _library.gnutls_credentials_set
591
credentials_set.argtypes = [session_t, credentials_type_t,
593
credentials_set.restype = _error_code
595
strerror = _library.gnutls_strerror
596
strerror.argtypes = [ctypes.c_int]
597
strerror.restype = ctypes.c_char_p
599
certificate_type_get = _library.gnutls_certificate_type_get
600
certificate_type_get.argtypes = [session_t]
601
certificate_type_get.restype = _error_code
603
certificate_get_peers = _library.gnutls_certificate_get_peers
604
certificate_get_peers.argtypes = [session_t,
605
ctypes.POINTER(ctypes.c_uint)]
606
certificate_get_peers.restype = ctypes.POINTER(datum_t)
608
global_set_log_level = _library.gnutls_global_set_log_level
609
global_set_log_level.argtypes = [ctypes.c_int]
610
global_set_log_level.restype = None
612
global_set_log_function = _library.gnutls_global_set_log_function
613
global_set_log_function.argtypes = [log_func]
614
global_set_log_function.restype = None
616
deinit = _library.gnutls_deinit
617
deinit.argtypes = [session_t]
618
deinit.restype = None
620
handshake = _library.gnutls_handshake
621
handshake.argtypes = [session_t]
622
handshake.restype = _error_code
624
transport_set_ptr = _library.gnutls_transport_set_ptr
625
transport_set_ptr.argtypes = [session_t, transport_ptr_t]
626
transport_set_ptr.restype = None
628
bye = _library.gnutls_bye
629
bye.argtypes = [session_t, close_request_t]
630
bye.restype = _error_code
632
check_version = _library.gnutls_check_version
633
check_version.argtypes = [ctypes.c_char_p]
634
check_version.restype = ctypes.c_char_p
636
# All the function declarations below are from gnutls/openpgp.h
638
openpgp_crt_init = _library.gnutls_openpgp_crt_init
639
openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
640
openpgp_crt_init.restype = _error_code
642
openpgp_crt_import = _library.gnutls_openpgp_crt_import
643
openpgp_crt_import.argtypes = [openpgp_crt_t,
644
ctypes.POINTER(datum_t),
646
openpgp_crt_import.restype = _error_code
648
openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
649
openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
650
ctypes.POINTER(ctypes.c_uint)]
651
openpgp_crt_verify_self.restype = _error_code
653
openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
654
openpgp_crt_deinit.argtypes = [openpgp_crt_t]
655
openpgp_crt_deinit.restype = None
657
openpgp_crt_get_fingerprint = (
658
_library.gnutls_openpgp_crt_get_fingerprint)
659
openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
663
openpgp_crt_get_fingerprint.restype = _error_code
665
# Remove non-public function
667
# Create the global "gnutls" object, simulating a module
670
def call_pipe(connection, # : multiprocessing.Connection
671
func, *args, **kwargs):
672
"""This function is meant to be called by multiprocessing.Process
674
This function runs func(*args, **kwargs), and writes the resulting
675
return value on the provided multiprocessing.Connection.
677
connection.send(func(*args, **kwargs))
392
def timedelta_to_milliseconds(td):
393
"Convert a datetime.timedelta() to milliseconds"
394
return ((td.days * 24 * 60 * 60 * 1000)
395
+ (td.seconds * 1000)
396
+ (td.microseconds // 1000))
680
398
class Client(object):
681
399
"""A representation of a client host served by this server.
720
436
runtime_expansions: Allowed attributes for runtime expansion.
721
437
expires: datetime.datetime(); time (UTC) when a client will be
722
438
disabled, or None
723
server_settings: The server_settings dict from main()
726
441
runtime_expansions = ("approval_delay", "approval_duration",
727
"created", "enabled", "expires",
728
"fingerprint", "host", "interval",
729
"last_approval_request", "last_checked_ok",
442
"created", "enabled", "fingerprint",
443
"host", "interval", "last_checked_ok",
730
444
"last_enabled", "name", "timeout")
733
"extended_timeout": "PT15M",
735
"checker": "fping -q -- %%(host)s",
737
"approval_delay": "PT0S",
738
"approval_duration": "PT1S",
739
"approved_by_default": "True",
445
client_defaults = { "timeout": "5m",
446
"extended_timeout": "15m",
448
"checker": "fping -q -- %%(host)s",
450
"approval_delay": "0s",
451
"approval_duration": "1s",
452
"approved_by_default": "True",
456
def timeout_milliseconds(self):
457
"Return the 'timeout' attribute in milliseconds"
458
return timedelta_to_milliseconds(self.timeout)
460
def extended_timeout_milliseconds(self):
461
"Return the 'extended_timeout' attribute in milliseconds"
462
return timedelta_to_milliseconds(self.extended_timeout)
464
def interval_milliseconds(self):
465
"Return the 'interval' attribute in milliseconds"
466
return timedelta_to_milliseconds(self.interval)
468
def approval_delay_milliseconds(self):
469
return timedelta_to_milliseconds(self.approval_delay)
744
472
def config_parser(config):
878
606
# and every interval from then on.
879
607
if self.checker_initiator_tag is not None:
880
608
gobject.source_remove(self.checker_initiator_tag)
881
self.checker_initiator_tag = gobject.timeout_add(
882
int(self.interval.total_seconds() * 1000),
609
self.checker_initiator_tag = (gobject.timeout_add
610
(self.interval_milliseconds(),
884
612
# Schedule a disable() when 'timeout' has passed
885
613
if self.disable_initiator_tag is not None:
886
614
gobject.source_remove(self.disable_initiator_tag)
887
self.disable_initiator_tag = gobject.timeout_add(
888
int(self.timeout.total_seconds() * 1000), self.disable)
615
self.disable_initiator_tag = (gobject.timeout_add
616
(self.timeout_milliseconds(),
889
618
# Also start a new checker *right now*.
890
619
self.start_checker()
892
def checker_callback(self, source, condition, connection,
621
def checker_callback(self, pid, condition, command):
894
622
"""The checker has completed, so take appropriate actions."""
895
623
self.checker_callback_tag = None
896
624
self.checker = None
897
# Read return code from connection (see call_pipe)
898
returncode = connection.recv()
902
self.last_checker_status = returncode
903
self.last_checker_signal = None
625
if os.WIFEXITED(condition):
626
self.last_checker_status = os.WEXITSTATUS(condition)
904
627
if self.last_checker_status == 0:
905
628
logger.info("Checker for %(name)s succeeded",
907
630
self.checked_ok()
909
logger.info("Checker for %(name)s failed", vars(self))
632
logger.info("Checker for %(name)s failed",
911
635
self.last_checker_status = -1
912
self.last_checker_signal = -returncode
913
636
logger.warning("Checker for %(name)s crashed?",
917
639
def checked_ok(self):
918
640
"""Assert that the client has been seen, alive and well."""
919
641
self.last_checked_ok = datetime.datetime.utcnow()
920
642
self.last_checker_status = 0
921
self.last_checker_signal = None
922
643
self.bump_timeout()
924
645
def bump_timeout(self, timeout=None):
950
672
# than 'timeout' for the client to be disabled, which is as it
953
if self.checker is not None and not self.checker.is_alive():
954
logger.warning("Checker was not alive; joining")
675
# If a checker exists, make sure it is not a zombie
677
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
678
except (AttributeError, OSError) as error:
679
if (isinstance(error, OSError)
680
and error.errno != errno.ECHILD):
684
logger.warning("Checker was a zombie")
685
gobject.source_remove(self.checker_callback_tag)
686
self.checker_callback(pid, status,
687
self.current_checker_command)
957
688
# Start a new checker if needed
958
689
if self.checker is None:
959
# Escape attributes for the shell
961
attr: re.escape(str(getattr(self, attr)))
962
for attr in self.runtime_expansions }
964
command = self.checker_command % escaped_attrs
965
except TypeError as error:
966
logger.error('Could not format string "%s"',
967
self.checker_command,
691
# In case checker_command has exactly one % operator
692
command = self.checker_command % self.host
694
# Escape attributes for the shell
695
escaped_attrs = dict(
697
re.escape(unicode(str(getattr(self, attr, "")),
701
self.runtime_expansions)
704
command = self.checker_command % escaped_attrs
705
except TypeError as error:
706
logger.error('Could not format string "%s"',
707
self.checker_command, exc_info=error)
708
return True # Try again later
709
self.current_checker_command = command
711
logger.info("Starting checker %r for %s",
713
# We don't need to redirect stdout and stderr, since
714
# in normal mode, that is already done by daemon(),
715
# and in debug mode we don't want to. (Stdin is
716
# always replaced by /dev/null.)
717
self.checker = subprocess.Popen(command,
720
self.checker_callback_tag = (gobject.child_watch_add
722
self.checker_callback,
724
# The checker may have completed before the gobject
725
# watch was added. Check for this.
726
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
728
gobject.source_remove(self.checker_callback_tag)
729
self.checker_callback(pid, status, command)
730
except OSError as error:
731
logger.error("Failed to start subprocess",
969
return True # Try again later
970
self.current_checker_command = command
971
logger.info("Starting checker %r for %s", command,
973
# We don't need to redirect stdout and stderr, since
974
# in normal mode, that is already done by daemon(),
975
# and in debug mode we don't want to. (Stdin is
976
# always replaced by /dev/null.)
977
# The exception is when not debugging but nevertheless
978
# running in the foreground; use the previously
980
popen_args = { "close_fds": True,
983
if (not self.server_settings["debug"]
984
and self.server_settings["foreground"]):
985
popen_args.update({"stdout": wnull,
987
pipe = multiprocessing.Pipe(duplex = False)
988
self.checker = multiprocessing.Process(
990
args = (pipe[1], subprocess.call, command),
993
self.checker_callback_tag = gobject.io_add_watch(
994
pipe[0].fileno(), gobject.IO_IN,
995
self.checker_callback, pipe[0], command)
996
733
# Re-run this periodically if run by gobject.timeout_add
1117
853
If called like _is_dbus_thing("method") it returns a function
1118
854
suitable for use as predicate to inspect.getmembers().
1120
return lambda obj: getattr(obj, "_dbus_is_{}".format(thing),
856
return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
1123
859
def _get_all_dbus_things(self, thing):
1124
860
"""Returns a generator of (name, attribute) pairs
1126
return ((getattr(athing.__get__(self), "_dbus_name", name),
862
return ((getattr(athing.__get__(self), "_dbus_name",
1127
864
athing.__get__(self))
1128
865
for cls in self.__class__.__mro__
1129
866
for name, athing in
1130
inspect.getmembers(cls, self._is_dbus_thing(thing)))
1132
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1133
out_signature = "s",
1134
path_keyword = 'object_path',
1135
connection_keyword = 'connection')
1136
def Introspect(self, object_path, connection):
1137
"""Overloading of standard D-Bus method.
1139
Inserts annotation tags on methods and signals.
1141
xmlstring = dbus.service.Object.Introspect(self, object_path,
1144
document = xml.dom.minidom.parseString(xmlstring)
1146
for if_tag in document.getElementsByTagName("interface"):
1147
# Add annotation tags
1148
for typ in ("method", "signal"):
1149
for tag in if_tag.getElementsByTagName(typ):
1151
for name, prop in (self.
1152
_get_all_dbus_things(typ)):
1153
if (name == tag.getAttribute("name")
1154
and prop._dbus_interface
1155
== if_tag.getAttribute("name")):
1156
annots.update(getattr(
1157
prop, "_dbus_annotations", {}))
1158
for name, value in annots.items():
1159
ann_tag = document.createElement(
1161
ann_tag.setAttribute("name", name)
1162
ann_tag.setAttribute("value", value)
1163
tag.appendChild(ann_tag)
1164
# Add interface annotation tags
1165
for annotation, value in dict(
1166
itertools.chain.from_iterable(
1167
annotations().items()
1168
for name, annotations
1169
in self._get_all_dbus_things("interface")
1170
if name == if_tag.getAttribute("name")
1172
ann_tag = document.createElement("annotation")
1173
ann_tag.setAttribute("name", annotation)
1174
ann_tag.setAttribute("value", value)
1175
if_tag.appendChild(ann_tag)
1176
# Fix argument name for the Introspect method itself
1177
if (if_tag.getAttribute("name")
1178
== dbus.INTROSPECTABLE_IFACE):
1179
for cn in if_tag.getElementsByTagName("method"):
1180
if cn.getAttribute("name") == "Introspect":
1181
for arg in cn.getElementsByTagName("arg"):
1182
if (arg.getAttribute("direction")
1184
arg.setAttribute("name",
1186
xmlstring = document.toxml("utf-8")
1188
except (AttributeError, xml.dom.DOMException,
1189
xml.parsers.expat.ExpatError) as error:
1190
logger.error("Failed to override Introspection method",
1195
class DBusObjectWithProperties(DBusObjectWithAnnotations):
1196
"""A D-Bus object with properties.
1198
Classes inheriting from this can use the dbus_service_property
1199
decorator to expose methods as D-Bus properties. It exposes the
1200
standard Get(), Set(), and GetAll() methods on the D-Bus.
867
inspect.getmembers(cls,
868
self._is_dbus_thing(thing)))
1203
870
def _get_dbus_property(self, interface_name, property_name):
1204
871
"""Returns a bound method if one exists which is a D-Bus
1205
872
property with the specified name and interface.
1207
for cls in self.__class__.__mro__:
1208
for name, value in inspect.getmembers(
1209
cls, self._is_dbus_thing("property")):
874
for cls in self.__class__.__mro__:
875
for name, value in (inspect.getmembers
877
self._is_dbus_thing("property"))):
1210
878
if (value._dbus_name == property_name
1211
879
and value._dbus_interface == interface_name):
1212
880
return value.__get__(self)
1214
882
# No such property
1215
raise DBusPropertyNotFound("{}:{}.{}".format(
1216
self.dbus_object_path, interface_name, property_name))
1219
def _get_all_interface_names(cls):
1220
"""Get a sequence of all interfaces supported by an object"""
1221
return (name for name in set(getattr(getattr(x, attr),
1222
"_dbus_interface", None)
1223
for x in (inspect.getmro(cls))
1225
if name is not None)
1227
@dbus.service.method(dbus.PROPERTIES_IFACE,
883
raise DBusPropertyNotFound(self.dbus_object_path + ":"
884
+ interface_name + "."
887
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
1229
888
out_signature="v")
1230
889
def Get(self, interface_name, property_name):
1231
890
"""Standard D-Bus property Get() method, see D-Bus standard.
1320
965
if prop._dbus_interface
1321
966
== if_tag.getAttribute("name")):
1322
967
if_tag.appendChild(tag)
1323
# Add annotation tags for properties
1324
for tag in if_tag.getElementsByTagName("property"):
1326
for name, prop in self._get_all_dbus_things(
1328
if (name == tag.getAttribute("name")
1329
and prop._dbus_interface
1330
== if_tag.getAttribute("name")):
1331
annots.update(getattr(
1332
prop, "_dbus_annotations", {}))
1333
for name, value in annots.items():
1334
ann_tag = document.createElement(
1336
ann_tag.setAttribute("name", name)
1337
ann_tag.setAttribute("value", value)
1338
tag.appendChild(ann_tag)
968
# Add annotation tags
969
for typ in ("method", "signal", "property"):
970
for tag in if_tag.getElementsByTagName(typ):
972
for name, prop in (self.
973
_get_all_dbus_things(typ)):
974
if (name == tag.getAttribute("name")
975
and prop._dbus_interface
976
== if_tag.getAttribute("name")):
977
annots.update(getattr
981
for name, value in annots.iteritems():
982
ann_tag = document.createElement(
984
ann_tag.setAttribute("name", name)
985
ann_tag.setAttribute("value", value)
986
tag.appendChild(ann_tag)
987
# Add interface annotation tags
988
for annotation, value in dict(
989
itertools.chain.from_iterable(
990
annotations().iteritems()
991
for name, annotations in
992
self._get_all_dbus_things("interface")
993
if name == if_tag.getAttribute("name")
995
ann_tag = document.createElement("annotation")
996
ann_tag.setAttribute("name", annotation)
997
ann_tag.setAttribute("value", value)
998
if_tag.appendChild(ann_tag)
1339
999
# Add the names to the return values for the
1340
1000
# "org.freedesktop.DBus.Properties" methods
1341
1001
if (if_tag.getAttribute("name")
1359
1019
exc_info=error)
1360
1020
return xmlstring
1363
dbus.OBJECT_MANAGER_IFACE
1364
except AttributeError:
1365
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
1367
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
1368
"""A D-Bus object with an ObjectManager.
1370
Classes inheriting from this exposes the standard
1371
GetManagedObjects call and the InterfacesAdded and
1372
InterfacesRemoved signals on the standard
1373
"org.freedesktop.DBus.ObjectManager" interface.
1375
Note: No signals are sent automatically; they must be sent
1378
@dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
1379
out_signature = "a{oa{sa{sv}}}")
1380
def GetManagedObjects(self):
1381
"""This function must be overridden"""
1382
raise NotImplementedError()
1384
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1385
signature = "oa{sa{sv}}")
1386
def InterfacesAdded(self, object_path, interfaces_and_properties):
1389
@dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas")
1390
def InterfacesRemoved(self, object_path, interfaces):
1393
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1394
out_signature = "s",
1395
path_keyword = 'object_path',
1396
connection_keyword = 'connection')
1397
def Introspect(self, object_path, connection):
1398
"""Overloading of standard D-Bus method.
1400
Override return argument name of GetManagedObjects to be
1401
"objpath_interfaces_and_properties"
1403
xmlstring = DBusObjectWithAnnotations.Introspect(self,
1407
document = xml.dom.minidom.parseString(xmlstring)
1409
for if_tag in document.getElementsByTagName("interface"):
1410
# Fix argument name for the GetManagedObjects method
1411
if (if_tag.getAttribute("name")
1412
== dbus.OBJECT_MANAGER_IFACE):
1413
for cn in if_tag.getElementsByTagName("method"):
1414
if (cn.getAttribute("name")
1415
== "GetManagedObjects"):
1416
for arg in cn.getElementsByTagName("arg"):
1417
if (arg.getAttribute("direction")
1421
"objpath_interfaces"
1423
xmlstring = document.toxml("utf-8")
1425
except (AttributeError, xml.dom.DOMException,
1426
xml.parsers.expat.ExpatError) as error:
1427
logger.error("Failed to override Introspection method",
1431
def datetime_to_dbus(dt, variant_level=0):
1023
def datetime_to_dbus (dt, variant_level=0):
1432
1024
"""Convert a UTC datetime.datetime() to a D-Bus type."""
1434
1026
return dbus.String("", variant_level = variant_level)
1435
return dbus.String(dt.isoformat(), variant_level=variant_level)
1027
return dbus.String(dt.isoformat(),
1028
variant_level=variant_level)
1438
1031
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
1469
1061
# Ignore non-D-Bus attributes, and D-Bus attributes
1470
1062
# with the wrong interface name
1471
1063
if (not hasattr(attribute, "_dbus_interface")
1472
or not attribute._dbus_interface.startswith(
1473
orig_interface_name)):
1064
or not attribute._dbus_interface
1065
.startswith(orig_interface_name)):
1475
1067
# Create an alternate D-Bus interface name based on
1476
1068
# the current name
1477
alt_interface = attribute._dbus_interface.replace(
1478
orig_interface_name, alt_interface_name)
1069
alt_interface = (attribute._dbus_interface
1070
.replace(orig_interface_name,
1071
alt_interface_name))
1479
1072
interface_names.add(alt_interface)
1480
1073
# Is this a D-Bus signal?
1481
1074
if getattr(attribute, "_dbus_is_signal", False):
1482
if sys.version_info.major == 2:
1483
# Extract the original non-method undecorated
1484
# function by black magic
1485
nonmethod_func = (dict(
1075
# Extract the original non-method function by
1077
nonmethod_func = (dict(
1486
1078
zip(attribute.func_code.co_freevars,
1487
attribute.__closure__))
1488
["func"].cell_contents)
1490
nonmethod_func = attribute
1079
attribute.__closure__))["func"]
1491
1081
# Create a new, but exactly alike, function
1492
1082
# object, and decorate it to be a new D-Bus signal
1493
1083
# with the alternate D-Bus interface name
1494
if sys.version_info.major == 2:
1495
new_function = types.FunctionType(
1496
nonmethod_func.func_code,
1497
nonmethod_func.func_globals,
1498
nonmethod_func.func_name,
1499
nonmethod_func.func_defaults,
1500
nonmethod_func.func_closure)
1502
new_function = types.FunctionType(
1503
nonmethod_func.__code__,
1504
nonmethod_func.__globals__,
1505
nonmethod_func.__name__,
1506
nonmethod_func.__defaults__,
1507
nonmethod_func.__closure__)
1508
new_function = (dbus.service.signal(
1510
attribute._dbus_signature)(new_function))
1084
new_function = (dbus.service.signal
1086
attribute._dbus_signature)
1087
(types.FunctionType(
1088
nonmethod_func.func_code,
1089
nonmethod_func.func_globals,
1090
nonmethod_func.func_name,
1091
nonmethod_func.func_defaults,
1092
nonmethod_func.func_closure)))
1511
1093
# Copy annotations, if any
1513
new_function._dbus_annotations = dict(
1514
attribute._dbus_annotations)
1095
new_function._dbus_annotations = (
1096
dict(attribute._dbus_annotations))
1515
1097
except AttributeError:
1517
1099
# Define a creator of a function to call both the
1741
1302
DBusObjectWithProperties.__del__(self, *args, **kwargs)
1742
1303
Client.__del__(self, *args, **kwargs)
1744
def checker_callback(self, source, condition,
1745
connection, command, *args, **kwargs):
1746
ret = Client.checker_callback(self, source, condition,
1747
connection, command, *args,
1749
exitstatus = self.last_checker_status
1305
def checker_callback(self, pid, condition, command,
1307
self.checker_callback_tag = None
1309
if os.WIFEXITED(condition):
1310
exitstatus = os.WEXITSTATUS(condition)
1751
1311
# Emit D-Bus signal
1752
1312
self.CheckerCompleted(dbus.Int16(exitstatus),
1753
# This is specific to GNU libC
1754
dbus.Int64(exitstatus << 8),
1313
dbus.Int64(condition),
1755
1314
dbus.String(command))
1757
1316
# Emit D-Bus signal
1758
1317
self.CheckerCompleted(dbus.Int16(-1),
1760
# This is specific to GNU libC
1762
| self.last_checker_signal),
1318
dbus.Int64(condition),
1763
1319
dbus.String(command))
1321
return Client.checker_callback(self, pid, condition, command,
1766
1324
def start_checker(self, *args, **kwargs):
1767
old_checker_pid = getattr(self.checker, "pid", None)
1325
old_checker = self.checker
1326
if self.checker is not None:
1327
old_checker_pid = self.checker.pid
1329
old_checker_pid = None
1768
1330
r = Client.start_checker(self, *args, **kwargs)
1769
1331
# Only if new checker process was started
1770
1332
if (self.checker is not None
1885
1448
self.approved_by_default = bool(value)
1887
1450
# ApprovalDelay - property
1888
@dbus_service_property(_interface,
1451
@dbus_service_property(_interface, signature="t",
1890
1452
access="readwrite")
1891
1453
def ApprovalDelay_dbus_property(self, value=None):
1892
1454
if value is None: # get
1893
return dbus.UInt64(self.approval_delay.total_seconds()
1455
return dbus.UInt64(self.approval_delay_milliseconds())
1895
1456
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1897
1458
# ApprovalDuration - property
1898
@dbus_service_property(_interface,
1459
@dbus_service_property(_interface, signature="t",
1900
1460
access="readwrite")
1901
1461
def ApprovalDuration_dbus_property(self, value=None):
1902
1462
if value is None: # get
1903
return dbus.UInt64(self.approval_duration.total_seconds()
1463
return dbus.UInt64(timedelta_to_milliseconds(
1464
self.approval_duration))
1905
1465
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1907
1467
# Name - property
1909
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1910
1468
@dbus_service_property(_interface, signature="s", access="read")
1911
1469
def Name_dbus_property(self):
1912
1470
return dbus.String(self.name)
1914
1472
# Fingerprint - property
1916
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1917
1473
@dbus_service_property(_interface, signature="s", access="read")
1918
1474
def Fingerprint_dbus_property(self):
1919
1475
return dbus.String(self.fingerprint)
1921
1477
# Host - property
1922
@dbus_service_property(_interface,
1478
@dbus_service_property(_interface, signature="s",
1924
1479
access="readwrite")
1925
1480
def Host_dbus_property(self, value=None):
1926
1481
if value is None: # get
1927
1482
return dbus.String(self.host)
1928
self.host = str(value)
1483
self.host = unicode(value)
1930
1485
# Created - property
1932
{"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1933
1486
@dbus_service_property(_interface, signature="s", access="read")
1934
1487
def Created_dbus_property(self):
1935
1488
return datetime_to_dbus(self.created)
1999
1550
gobject.source_remove(self.disable_initiator_tag)
2000
self.disable_initiator_tag = gobject.timeout_add(
2001
int((self.expires - now).total_seconds() * 1000),
1551
self.disable_initiator_tag = (
1552
gobject.timeout_add(
1553
timedelta_to_milliseconds(self.expires - now),
2004
1556
# ExtendedTimeout - property
2005
@dbus_service_property(_interface,
1557
@dbus_service_property(_interface, signature="t",
2007
1558
access="readwrite")
2008
1559
def ExtendedTimeout_dbus_property(self, value=None):
2009
1560
if value is None: # get
2010
return dbus.UInt64(self.extended_timeout.total_seconds()
1561
return dbus.UInt64(self.extended_timeout_milliseconds())
2012
1562
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
2014
1564
# Interval - property
2015
@dbus_service_property(_interface,
1565
@dbus_service_property(_interface, signature="t",
2017
1566
access="readwrite")
2018
1567
def Interval_dbus_property(self, value=None):
2019
1568
if value is None: # get
2020
return dbus.UInt64(self.interval.total_seconds() * 1000)
1569
return dbus.UInt64(self.interval_milliseconds())
2021
1570
self.interval = datetime.timedelta(0, 0, 0, value)
2022
1571
if getattr(self, "checker_initiator_tag", None) is None:
2024
1573
if self.enabled:
2025
1574
# Reschedule checker run
2026
1575
gobject.source_remove(self.checker_initiator_tag)
2027
self.checker_initiator_tag = gobject.timeout_add(
2028
value, self.start_checker)
2029
self.start_checker() # Start one now, too
1576
self.checker_initiator_tag = (gobject.timeout_add
1577
(value, self.start_checker))
1578
self.start_checker() # Start one now, too
2031
1580
# Checker - property
2032
@dbus_service_property(_interface,
1581
@dbus_service_property(_interface, signature="s",
2034
1582
access="readwrite")
2035
1583
def Checker_dbus_property(self, value=None):
2036
1584
if value is None: # get
2037
1585
return dbus.String(self.checker_command)
2038
self.checker_command = str(value)
1586
self.checker_command = unicode(value)
2040
1588
# CheckerRunning - property
2041
@dbus_service_property(_interface,
1589
@dbus_service_property(_interface, signature="b",
2043
1590
access="readwrite")
2044
1591
def CheckerRunning_dbus_property(self, value=None):
2045
1592
if value is None: # get
2266
1818
def fingerprint(openpgp):
2267
1819
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
2268
1820
# New GnuTLS "datum" with the OpenPGP public key
2269
datum = gnutls.datum_t(
2270
ctypes.cast(ctypes.c_char_p(openpgp),
2271
ctypes.POINTER(ctypes.c_ubyte)),
2272
ctypes.c_uint(len(openpgp)))
1821
datum = (gnutls.library.types
1822
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1825
ctypes.c_uint(len(openpgp))))
2273
1826
# New empty GnuTLS certificate
2274
crt = gnutls.openpgp_crt_t()
2275
gnutls.openpgp_crt_init(ctypes.byref(crt))
1827
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1828
(gnutls.library.functions
1829
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
2276
1830
# Import the OpenPGP public key into the certificate
2277
gnutls.openpgp_crt_import(crt, ctypes.byref(datum),
2278
gnutls.OPENPGP_FMT_RAW)
1831
(gnutls.library.functions
1832
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1833
gnutls.library.constants
1834
.GNUTLS_OPENPGP_FMT_RAW))
2279
1835
# Verify the self signature in the key
2280
1836
crtverify = ctypes.c_uint()
2281
gnutls.openpgp_crt_verify_self(crt, 0,
2282
ctypes.byref(crtverify))
1837
(gnutls.library.functions
1838
.gnutls_openpgp_crt_verify_self(crt, 0,
1839
ctypes.byref(crtverify)))
2283
1840
if crtverify.value != 0:
2284
gnutls.openpgp_crt_deinit(crt)
2285
raise gnutls.CertificateSecurityError("Verify failed")
1841
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1842
raise (gnutls.errors.CertificateSecurityError
2286
1844
# New buffer for the fingerprint
2287
1845
buf = ctypes.create_string_buffer(20)
2288
1846
buf_len = ctypes.c_size_t()
2289
1847
# Get the fingerprint from the certificate into the buffer
2290
gnutls.openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
2291
ctypes.byref(buf_len))
1848
(gnutls.library.functions
1849
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1850
ctypes.byref(buf_len)))
2292
1851
# Deinit the certificate
2293
gnutls.openpgp_crt_deinit(crt)
1852
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
2294
1853
# Convert the buffer to a Python bytestring
2295
1854
fpr = ctypes.string_at(buf, buf_len.value)
2296
1855
# Convert the bytestring to hexadecimal notation
2345
1902
interface: None or a network interface name (string)
2346
1903
use_ipv6: Boolean; to use IPv6 or not
2349
1905
def __init__(self, server_address, RequestHandlerClass,
2353
"""If socketfd is set, use that file descriptor instead of
2354
creating a new one with socket.socket().
1906
interface=None, use_ipv6=True):
2356
1907
self.interface = interface
2358
1909
self.address_family = socket.AF_INET6
2359
if socketfd is not None:
2360
# Save the file descriptor
2361
self.socketfd = socketfd
2362
# Save the original socket.socket() function
2363
self.socket_socket = socket.socket
2364
# To implement --socket, we monkey patch socket.socket.
2366
# (When socketserver.TCPServer is a new-style class, we
2367
# could make self.socket into a property instead of monkey
2368
# patching socket.socket.)
2370
# Create a one-time-only replacement for socket.socket()
2371
@functools.wraps(socket.socket)
2372
def socket_wrapper(*args, **kwargs):
2373
# Restore original function so subsequent calls are
2375
socket.socket = self.socket_socket
2376
del self.socket_socket
2377
# This time only, return a new socket object from the
2378
# saved file descriptor.
2379
return socket.fromfd(self.socketfd, *args, **kwargs)
2380
# Replace socket.socket() function with wrapper
2381
socket.socket = socket_wrapper
2382
# The socketserver.TCPServer.__init__ will call
2383
# socket.socket(), which might be our replacement,
2384
# socket_wrapper(), if socketfd was set.
2385
1910
socketserver.TCPServer.__init__(self, server_address,
2386
1911
RequestHandlerClass)
2388
1912
def server_bind(self):
2389
1913
"""This overrides the normal server_bind() function
2390
1914
to bind to an interface if one was specified, and also NOT to
2550
def rfc3339_duration_to_delta(duration):
2551
"""Parse an RFC 3339 "duration" and return a datetime.timedelta
2553
>>> rfc3339_duration_to_delta("P7D")
2554
datetime.timedelta(7)
2555
>>> rfc3339_duration_to_delta("PT60S")
2556
datetime.timedelta(0, 60)
2557
>>> rfc3339_duration_to_delta("PT60M")
2558
datetime.timedelta(0, 3600)
2559
>>> rfc3339_duration_to_delta("PT24H")
2560
datetime.timedelta(1)
2561
>>> rfc3339_duration_to_delta("P1W")
2562
datetime.timedelta(7)
2563
>>> rfc3339_duration_to_delta("PT5M30S")
2564
datetime.timedelta(0, 330)
2565
>>> rfc3339_duration_to_delta("P1DT3M20S")
2566
datetime.timedelta(1, 200)
2569
# Parsing an RFC 3339 duration with regular expressions is not
2570
# possible - there would have to be multiple places for the same
2571
# values, like seconds. The current code, while more esoteric, is
2572
# cleaner without depending on a parsing library. If Python had a
2573
# built-in library for parsing we would use it, but we'd like to
2574
# avoid excessive use of external libraries.
2576
# New type for defining tokens, syntax, and semantics all-in-one
2577
Token = collections.namedtuple("Token", (
2578
"regexp", # To match token; if "value" is not None, must have
2579
# a "group" containing digits
2580
"value", # datetime.timedelta or None
2581
"followers")) # Tokens valid after this token
2582
# RFC 3339 "duration" tokens, syntax, and semantics; taken from
2583
# the "duration" ABNF definition in RFC 3339, Appendix A.
2584
token_end = Token(re.compile(r"$"), None, frozenset())
2585
token_second = Token(re.compile(r"(\d+)S"),
2586
datetime.timedelta(seconds=1),
2587
frozenset((token_end, )))
2588
token_minute = Token(re.compile(r"(\d+)M"),
2589
datetime.timedelta(minutes=1),
2590
frozenset((token_second, token_end)))
2591
token_hour = Token(re.compile(r"(\d+)H"),
2592
datetime.timedelta(hours=1),
2593
frozenset((token_minute, token_end)))
2594
token_time = Token(re.compile(r"T"),
2596
frozenset((token_hour, token_minute,
2598
token_day = Token(re.compile(r"(\d+)D"),
2599
datetime.timedelta(days=1),
2600
frozenset((token_time, token_end)))
2601
token_month = Token(re.compile(r"(\d+)M"),
2602
datetime.timedelta(weeks=4),
2603
frozenset((token_day, token_end)))
2604
token_year = Token(re.compile(r"(\d+)Y"),
2605
datetime.timedelta(weeks=52),
2606
frozenset((token_month, token_end)))
2607
token_week = Token(re.compile(r"(\d+)W"),
2608
datetime.timedelta(weeks=1),
2609
frozenset((token_end, )))
2610
token_duration = Token(re.compile(r"P"), None,
2611
frozenset((token_year, token_month,
2612
token_day, token_time,
2614
# Define starting values
2615
value = datetime.timedelta() # Value so far
2617
followers = frozenset((token_duration, )) # Following valid tokens
2618
s = duration # String left to parse
2619
# Loop until end token is found
2620
while found_token is not token_end:
2621
# Search for any currently valid tokens
2622
for token in followers:
2623
match = token.regexp.match(s)
2624
if match is not None:
2626
if token.value is not None:
2627
# Value found, parse digits
2628
factor = int(match.group(1), 10)
2629
# Add to value so far
2630
value += factor * token.value
2631
# Strip token from string
2632
s = token.regexp.sub("", s, 1)
2635
# Set valid next tokens
2636
followers = found_token.followers
2639
# No currently valid tokens were found
2640
raise ValueError("Invalid RFC 3339 duration: {!r}"
2646
2065
def string_to_delta(interval):
2647
2066
"""Parse a string and return a datetime.timedelta
2777
2182
"debug": "False",
2779
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
2780
":+SIGN-DSA-SHA256",
2184
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2781
2185
"servicename": "Mandos",
2782
2186
"use_dbus": "True",
2783
2187
"use_ipv6": "True",
2784
2188
"debuglevel": "",
2785
2189
"restore": "True",
2787
"statedir": "/var/lib/mandos",
2788
"foreground": "False",
2190
"statedir": "/var/lib/mandos"
2792
2193
# Parse config file for server-global settings
2793
2194
server_config = configparser.SafeConfigParser(server_defaults)
2794
2195
del server_defaults
2795
server_config.read(os.path.join(options.configdir, "mandos.conf"))
2196
server_config.read(os.path.join(options.configdir,
2796
2198
# Convert the SafeConfigParser object to a dict
2797
2199
server_settings = server_config.defaults()
2798
2200
# Use the appropriate methods on the non-string config options
2799
for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
2201
for option in ("debug", "use_dbus", "use_ipv6"):
2800
2202
server_settings[option] = server_config.getboolean("DEFAULT",
2802
2204
if server_settings["port"]:
2803
2205
server_settings["port"] = server_config.getint("DEFAULT",
2805
if server_settings["socket"]:
2806
server_settings["socket"] = server_config.getint("DEFAULT",
2808
# Later, stdin will, and stdout and stderr might, be dup'ed
2809
# over with an opened os.devnull. But we don't want this to
2810
# happen with a supplied network socket.
2811
if 0 <= server_settings["socket"] <= 2:
2812
server_settings["socket"] = os.dup(server_settings
2814
2207
del server_config
2816
2209
# Override the settings from the config file with command line
2817
2210
# options, if set.
2818
2211
for option in ("interface", "address", "port", "debug",
2819
"priority", "servicename", "configdir", "use_dbus",
2820
"use_ipv6", "debuglevel", "restore", "statedir",
2821
"socket", "foreground", "zeroconf"):
2212
"priority", "servicename", "configdir",
2213
"use_dbus", "use_ipv6", "debuglevel", "restore",
2822
2215
value = getattr(options, option)
2823
2216
if value is not None:
2824
2217
server_settings[option] = value
2826
2219
# Force all strings to be unicode
2827
2220
for option in server_settings.keys():
2828
if isinstance(server_settings[option], bytes):
2829
server_settings[option] = (server_settings[option]
2831
# Force all boolean options to be boolean
2832
for option in ("debug", "use_dbus", "use_ipv6", "restore",
2833
"foreground", "zeroconf"):
2834
server_settings[option] = bool(server_settings[option])
2835
# Debug implies foreground
2836
if server_settings["debug"]:
2837
server_settings["foreground"] = True
2221
if type(server_settings[option]) is str:
2222
server_settings[option] = unicode(server_settings[option])
2838
2223
# Now we have our good server settings in "server_settings"
2840
2225
##################################################################
2842
if (not server_settings["zeroconf"]
2843
and not (server_settings["port"]
2844
or server_settings["socket"] != "")):
2845
parser.error("Needs port or socket to work without Zeroconf")
2847
2227
# For convenience
2848
2228
debug = server_settings["debug"]
2849
2229
debuglevel = server_settings["debuglevel"]