11
11
# "AvahiService" class, and some lines in "main".
13
13
# Everything else is
14
# Copyright © 2008-2020 Teddy Hogeborn
15
# Copyright © 2008-2020 Björn Påhlsson
14
# Copyright © 2008-2019 Teddy Hogeborn
15
# Copyright © 2008-2019 Björn Påhlsson
17
17
# This file is part of Mandos.
94
91
if sys.version_info.major == 2:
95
92
__metaclass__ = type
98
# Add collections.abc.Callable if it does not exist
100
collections.abc.Callable
101
except AttributeError:
103
Callable = collections.Callable
104
collections.abc = abc
107
# Add shlex.quote if it does not exist
110
except AttributeError:
111
shlex.quote = re.escape
113
# Show warnings by default
114
if not sys.warnoptions:
116
warnings.simplefilter("default")
118
94
# Try to find the value of SO_BINDTODEVICE:
141
117
SO_BINDTODEVICE = None
119
if sys.version_info.major == 2:
143
122
if sys.version_info < (3, 2):
144
123
configparser.Configparser = configparser.SafeConfigParser
147
126
stored_state_file = "clients.pickle"
149
128
logger = logging.getLogger()
150
logging.captureWarnings(True) # Show warnings via the logging system
524
502
class AvahiServiceToSyslog(AvahiService):
525
503
def rename(self, *args, **kwargs):
526
504
"""Add the new name to the syslog messages"""
527
ret = super(AvahiServiceToSyslog, self).rename(*args,
505
ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
529
506
syslogger.setFormatter(logging.Formatter(
530
507
'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
531
508
.format(self.name)))
661
638
raise gnutls.CertificateSecurityError(code=result)
662
639
raise gnutls.Error(code=result)
664
def _retry_on_error(result, func, arguments,
665
_error_code=_error_code):
641
def _retry_on_error(result, func, arguments):
666
642
"""A function to retry on some errors, suitable
667
643
for the 'errcheck' attribute on ctypes functions"""
668
644
while result < 0:
777
753
x509_crt_fmt_t = ctypes.c_int
779
# All the function declarations below are from
755
# All the function declarations below are from gnutls/abstract.h
781
756
pubkey_init = _library.gnutls_pubkey_init
782
757
pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
783
758
pubkey_init.restype = _error_code
797
772
pubkey_deinit.argtypes = [pubkey_t]
798
773
pubkey_deinit.restype = None
800
# All the function declarations below are from
775
# All the function declarations below are from gnutls/openpgp.h
803
777
openpgp_crt_init = _library.gnutls_openpgp_crt_init
804
778
openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
810
784
openpgp_crt_fmt_t]
811
785
openpgp_crt_import.restype = _error_code
813
openpgp_crt_verify_self = \
814
_library.gnutls_openpgp_crt_verify_self
815
openpgp_crt_verify_self.argtypes = [
818
ctypes.POINTER(ctypes.c_uint),
787
openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
788
openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
789
ctypes.POINTER(ctypes.c_uint)]
820
790
openpgp_crt_verify_self.restype = _error_code
822
792
openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
1060
1030
if self.checker_initiator_tag is not None:
1061
1031
GLib.source_remove(self.checker_initiator_tag)
1062
1032
self.checker_initiator_tag = GLib.timeout_add(
1063
random.randrange(int(self.interval.total_seconds() * 1000
1033
int(self.interval.total_seconds() * 1000),
1065
1034
self.start_checker)
1066
1035
# Schedule a disable() when 'timeout' has passed
1067
1036
if self.disable_initiator_tag is not None:
1142
1110
if self.checker is None:
1143
1111
# Escape attributes for the shell
1144
1112
escaped_attrs = {
1145
attr: shlex.quote(str(getattr(self, attr)))
1113
attr: re.escape(str(getattr(self, attr)))
1146
1114
for attr in self.runtime_expansions}
1148
1116
command = self.checker_command % escaped_attrs
1175
1143
kwargs=popen_args)
1176
1144
self.checker.start()
1177
1145
self.checker_callback_tag = GLib.io_add_watch(
1178
GLib.IOChannel.unix_new(pipe[0].fileno()),
1179
GLib.PRIORITY_DEFAULT, GLib.IO_IN,
1146
pipe[0].fileno(), GLib.IO_IN,
1180
1147
self.checker_callback, pipe[0], command)
1181
1148
# Re-run this periodically if run by GLib.timeout_add
1437
1404
raise ValueError("Byte arrays not supported for non-"
1438
1405
"'ay' signature {!r}"
1439
1406
.format(prop._dbus_signature))
1440
value = dbus.ByteArray(bytes(value))
1407
value = dbus.ByteArray(b''.join(chr(byte)
1443
1411
@dbus.service.method(dbus.PROPERTIES_IFACE,
2476
2444
buf = ctypes.create_string_buffer(32)
2477
2445
buf_len = ctypes.c_size_t(len(buf))
2478
2446
# Get the key ID from the raw public key into the buffer
2479
gnutls.pubkey_get_key_id(
2481
gnutls.KEYID_USE_SHA256,
2482
ctypes.cast(ctypes.byref(buf),
2483
ctypes.POINTER(ctypes.c_ubyte)),
2484
ctypes.byref(buf_len))
2447
gnutls.pubkey_get_key_id(pubkey,
2448
gnutls.KEYID_USE_SHA256,
2449
ctypes.cast(ctypes.byref(buf),
2450
ctypes.POINTER(ctypes.c_ubyte)),
2451
ctypes.byref(buf_len))
2485
2452
# Deinit the certificate
2486
2453
gnutls.pubkey_deinit(pubkey)
2706
2673
def add_pipe(self, parent_pipe, proc):
2707
2674
# Call "handle_ipc" for both data and EOF events
2708
2675
GLib.io_add_watch(
2709
GLib.IOChannel.unix_new(parent_pipe.fileno()),
2710
GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
2676
parent_pipe.fileno(),
2677
GLib.IO_IN | GLib.IO_HUP,
2711
2678
functools.partial(self.handle_ipc,
2712
2679
parent_pipe=parent_pipe,
2732
2699
address = request[3]
2734
2701
for c in self.clients.values():
2735
if key_id == ("E3B0C44298FC1C149AFBF4C8996FB924"
2736
"27AE41E4649B934CA495991B7852B855"):
2702
if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
2738
2704
if key_id and c.key_id == key_id:
2754
2720
GLib.io_add_watch(
2755
GLib.IOChannel.unix_new(parent_pipe.fileno()),
2756
GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
2721
parent_pipe.fileno(),
2722
GLib.IO_IN | GLib.IO_HUP,
2757
2723
functools.partial(self.handle_ipc,
2758
2724
parent_pipe=parent_pipe,
2791
2757
def rfc3339_duration_to_delta(duration):
2792
2758
"""Parse an RFC 3339 "duration" and return a datetime.timedelta
2794
>>> timedelta = datetime.timedelta
2795
>>> rfc3339_duration_to_delta("P7D") == timedelta(7)
2797
>>> rfc3339_duration_to_delta("PT60S") == timedelta(0, 60)
2799
>>> rfc3339_duration_to_delta("PT60M") == timedelta(0, 3600)
2801
>>> rfc3339_duration_to_delta("PT24H") == timedelta(1)
2803
>>> rfc3339_duration_to_delta("P1W") == timedelta(7)
2805
>>> rfc3339_duration_to_delta("PT5M30S") == timedelta(0, 330)
2807
>>> rfc3339_duration_to_delta("P1DT3M20S") == timedelta(1, 200)
2760
>>> rfc3339_duration_to_delta("P7D")
2761
datetime.timedelta(7)
2762
>>> rfc3339_duration_to_delta("PT60S")
2763
datetime.timedelta(0, 60)
2764
>>> rfc3339_duration_to_delta("PT60M")
2765
datetime.timedelta(0, 3600)
2766
>>> rfc3339_duration_to_delta("PT24H")
2767
datetime.timedelta(1)
2768
>>> rfc3339_duration_to_delta("P1W")
2769
datetime.timedelta(7)
2770
>>> rfc3339_duration_to_delta("PT5M30S")
2771
datetime.timedelta(0, 330)
2772
>>> rfc3339_duration_to_delta("P1DT3M20S")
2773
datetime.timedelta(1, 200)
2812
2776
# Parsing an RFC 3339 duration with regular expressions is not
2892
2856
def string_to_delta(interval):
2893
2857
"""Parse a string and return a datetime.timedelta
2895
>>> string_to_delta('7d') == datetime.timedelta(7)
2897
>>> string_to_delta('60s') == datetime.timedelta(0, 60)
2899
>>> string_to_delta('60m') == datetime.timedelta(0, 3600)
2901
>>> string_to_delta('24h') == datetime.timedelta(1)
2903
>>> string_to_delta('1w') == datetime.timedelta(7)
2905
>>> string_to_delta('5m 30s') == datetime.timedelta(0, 330)
2859
>>> string_to_delta('7d')
2860
datetime.timedelta(7)
2861
>>> string_to_delta('60s')
2862
datetime.timedelta(0, 60)
2863
>>> string_to_delta('60m')
2864
datetime.timedelta(0, 3600)
2865
>>> string_to_delta('24h')
2866
datetime.timedelta(1)
2867
>>> string_to_delta('1w')
2868
datetime.timedelta(7)
2869
>>> string_to_delta('5m 30s')
2870
datetime.timedelta(0, 330)
3012
2976
options = parser.parse_args()
2980
fail_count, test_count = doctest.testmod()
2981
sys.exit(os.EX_OK if fail_count == 0 else 1)
3014
2983
# Default values for config file for server-global settings
3015
2984
if gnutls.has_rawpk:
3016
2985
priority = ("SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA"
3279
3248
if isinstance(s, bytes)
3280
3249
else s) for s in
3281
3250
value["client_structure"]]
3282
# .name, .host, and .checker_command
3283
for k in ("name", "host", "checker_command"):
3252
for k in ("name", "host"):
3284
3253
if isinstance(value[k], bytes):
3285
3254
value[k] = value[k].decode("utf-8")
3286
3255
if "key_id" not in value:
3296
3265
for key, value in
3297
3266
bytes_old_client_settings.items()}
3298
3267
del bytes_old_client_settings
3299
# .host and .checker_command
3300
3269
for value in old_client_settings.values():
3301
for attribute in ("host", "checker_command"):
3302
if isinstance(value[attribute], bytes):
3303
value[attribute] = (value[attribute]
3270
if isinstance(value["host"], bytes):
3271
value["host"] = (value["host"]
3305
3273
os.remove(stored_state_path)
3306
3274
except IOError as e:
3307
3275
if e.errno == errno.ENOENT:
3633
3601
# End of Avahi example code
3636
GLib.IOChannel.unix_new(tcp_server.fileno()),
3637
GLib.PRIORITY_DEFAULT, GLib.IO_IN,
3638
lambda *args, **kwargs: (tcp_server.handle_request
3639
(*args[2:], **kwargs) or True))
3603
GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN,
3604
lambda *args, **kwargs:
3605
(tcp_server.handle_request
3606
(*args[2:], **kwargs) or True))
3641
3608
logger.debug("Starting main loop")
3642
3609
main_loop.run()
3652
3619
# Must run before the D-Bus bus name gets deregistered
3656
def should_only_run_tests():
3657
parser = argparse.ArgumentParser(add_help=False)
3658
parser.add_argument("--check", action='store_true')
3659
args, unknown_args = parser.parse_known_args()
3660
run_tests = args.check
3662
# Remove --check argument from sys.argv
3663
sys.argv[1:] = unknown_args
3666
# Add all tests from doctest strings
3667
def load_tests(loader, tests, none):
3669
tests.addTests(doctest.DocTestSuite())
3672
3623
if __name__ == '__main__':
3674
if should_only_run_tests():
3675
# Call using ./mandos --check [--verbose]