/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2015-05-31 16:13:39 UTC
  • Revision ID: teddy@recompile.se-20150531161339-igb3l0ip3c10ejpz
mandos-ctl: Generate better messages in exceptions.

mandos (rfc3339_duration_to_delta): Remove dead code.  Adjust white
                                    space.
mandos-ctl (rfc3339_duration_to_delta): Do minor formatting and
                                        whitespace adjustments, and
                                        Generate better message in
                                        exception.
(string_to_delta): Adjust quote character in doc string.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
# "AvahiService" class, and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2008-2014 Teddy Hogeborn
15
 
# Copyright © 2008-2014 Björn Påhlsson
 
14
# Copyright © 2008-2015 Teddy Hogeborn
 
15
# Copyright © 2008-2015 Björn Påhlsson
16
16
17
17
# This program is free software: you can redistribute it and/or modify
18
18
# it under the terms of the GNU General Public License as published by
36
36
 
37
37
from future_builtins import *
38
38
 
39
 
import SocketServer as socketserver
 
39
try:
 
40
    import SocketServer as socketserver
 
41
except ImportError:
 
42
    import socketserver
40
43
import socket
41
44
import argparse
42
45
import datetime
47
50
import gnutls.library.functions
48
51
import gnutls.library.constants
49
52
import gnutls.library.types
50
 
import ConfigParser as configparser
 
53
try:
 
54
    import ConfigParser as configparser
 
55
except ImportError:
 
56
    import configparser
51
57
import sys
52
58
import re
53
59
import os
62
68
import struct
63
69
import fcntl
64
70
import functools
65
 
import cPickle as pickle
 
71
try:
 
72
    import cPickle as pickle
 
73
except ImportError:
 
74
    import pickle
66
75
import multiprocessing
67
76
import types
68
77
import binascii
69
78
import tempfile
70
79
import itertools
71
80
import collections
 
81
import codecs
72
82
 
73
83
import dbus
74
84
import dbus.service
75
 
import gobject
 
85
try:
 
86
    import gobject
 
87
except ImportError:
 
88
    from gi.repository import GObject as gobject
76
89
import avahi
77
90
from dbus.mainloop.glib import DBusGMainLoop
78
91
import ctypes
91
104
if sys.version_info.major == 2:
92
105
    str = unicode
93
106
 
94
 
version = "1.6.8"
 
107
version = "1.6.9"
95
108
stored_state_file = "clients.pickle"
96
109
 
97
110
logger = logging.getLogger()
98
111
syslogger = None
99
112
 
100
113
try:
101
 
    if_nametoindex = (ctypes.cdll.LoadLibrary
102
 
                      (ctypes.util.find_library("c"))
103
 
                      .if_nametoindex)
 
114
    if_nametoindex = ctypes.cdll.LoadLibrary(
 
115
        ctypes.util.find_library("c")).if_nametoindex
104
116
except (OSError, AttributeError):
 
117
    
105
118
    def if_nametoindex(interface):
106
119
        "Get an interface index the hard way, i.e. using fcntl()"
107
120
        SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
116
129
    """init logger and add loglevel"""
117
130
    
118
131
    global syslogger
119
 
    syslogger = (logging.handlers.SysLogHandler
120
 
                 (facility =
121
 
                  logging.handlers.SysLogHandler.LOG_DAEMON,
122
 
                  address = "/dev/log"))
 
132
    syslogger = (logging.handlers.SysLogHandler(
 
133
        facility = logging.handlers.SysLogHandler.LOG_DAEMON,
 
134
        address = "/dev/log"))
123
135
    syslogger.setFormatter(logging.Formatter
124
136
                           ('Mandos [%(process)d]: %(levelname)s:'
125
137
                            ' %(message)s'))
142
154
 
143
155
class PGPEngine(object):
144
156
    """A simple class for OpenPGP symmetric encryption & decryption"""
 
157
    
145
158
    def __init__(self):
146
159
        self.tempdir = tempfile.mkdtemp(prefix="mandos-")
147
160
        self.gnupgargs = ['--batch',
186
199
    
187
200
    def encrypt(self, data, password):
188
201
        passphrase = self.password_encode(password)
189
 
        with tempfile.NamedTemporaryFile(dir=self.tempdir
190
 
                                         ) as passfile:
 
202
        with tempfile.NamedTemporaryFile(
 
203
                dir=self.tempdir) as passfile:
191
204
            passfile.write(passphrase)
192
205
            passfile.flush()
193
206
            proc = subprocess.Popen(['gpg', '--symmetric',
204
217
    
205
218
    def decrypt(self, data, password):
206
219
        passphrase = self.password_encode(password)
207
 
        with tempfile.NamedTemporaryFile(dir = self.tempdir
208
 
                                         ) as passfile:
 
220
        with tempfile.NamedTemporaryFile(
 
221
                dir = self.tempdir) as passfile:
209
222
            passfile.write(passphrase)
210
223
            passfile.flush()
211
224
            proc = subprocess.Popen(['gpg', '--decrypt',
215
228
                                    stdin = subprocess.PIPE,
216
229
                                    stdout = subprocess.PIPE,
217
230
                                    stderr = subprocess.PIPE)
218
 
            decrypted_plaintext, err = proc.communicate(input
219
 
                                                        = data)
 
231
            decrypted_plaintext, err = proc.communicate(input = data)
220
232
        if proc.returncode != 0:
221
233
            raise PGPError(err)
222
234
        return decrypted_plaintext
228
240
        return super(AvahiError, self).__init__(value, *args,
229
241
                                                **kwargs)
230
242
 
 
243
 
231
244
class AvahiServiceError(AvahiError):
232
245
    pass
233
246
 
 
247
 
234
248
class AvahiGroupError(AvahiError):
235
249
    pass
236
250
 
256
270
    bus: dbus.SystemBus()
257
271
    """
258
272
    
259
 
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
260
 
                 servicetype = None, port = None, TXT = None,
261
 
                 domain = "", host = "", max_renames = 32768,
262
 
                 protocol = avahi.PROTO_UNSPEC, bus = None):
 
273
    def __init__(self,
 
274
                 interface = avahi.IF_UNSPEC,
 
275
                 name = None,
 
276
                 servicetype = None,
 
277
                 port = None,
 
278
                 TXT = None,
 
279
                 domain = "",
 
280
                 host = "",
 
281
                 max_renames = 32768,
 
282
                 protocol = avahi.PROTO_UNSPEC,
 
283
                 bus = None):
263
284
        self.interface = interface
264
285
        self.name = name
265
286
        self.type = servicetype
275
296
        self.bus = bus
276
297
        self.entry_group_state_changed_match = None
277
298
    
278
 
    def rename(self):
 
299
    def rename(self, remove=True):
279
300
        """Derived from the Avahi example code"""
280
301
        if self.rename_count >= self.max_renames:
281
302
            logger.critical("No suitable Zeroconf service name found"
282
303
                            " after %i retries, exiting.",
283
304
                            self.rename_count)
284
305
            raise AvahiServiceError("Too many renames")
285
 
        self.name = str(self.server
286
 
                        .GetAlternativeServiceName(self.name))
 
306
        self.name = str(
 
307
            self.server.GetAlternativeServiceName(self.name))
 
308
        self.rename_count += 1
287
309
        logger.info("Changing Zeroconf service name to %r ...",
288
310
                    self.name)
289
 
        self.remove()
 
311
        if remove:
 
312
            self.remove()
290
313
        try:
291
314
            self.add()
292
315
        except dbus.exceptions.DBusException as error:
293
 
            logger.critical("D-Bus Exception", exc_info=error)
294
 
            self.cleanup()
295
 
            os._exit(1)
296
 
        self.rename_count += 1
 
316
            if (error.get_dbus_name()
 
317
                == "org.freedesktop.Avahi.CollisionError"):
 
318
                logger.info("Local Zeroconf service name collision.")
 
319
                return self.rename(remove=False)
 
320
            else:
 
321
                logger.critical("D-Bus Exception", exc_info=error)
 
322
                self.cleanup()
 
323
                os._exit(1)
297
324
    
298
325
    def remove(self):
299
326
        """Derived from the Avahi example code"""
338
365
        elif state == avahi.ENTRY_GROUP_FAILURE:
339
366
            logger.critical("Avahi: Error in group state changed %s",
340
367
                            str(error))
341
 
            raise AvahiGroupError("State changed: {!s}"
342
 
                                  .format(error))
 
368
            raise AvahiGroupError("State changed: {!s}".format(error))
343
369
    
344
370
    def cleanup(self):
345
371
        """Derived from the Avahi example code"""
355
381
    def server_state_changed(self, state, error=None):
356
382
        """Derived from the Avahi example code"""
357
383
        logger.debug("Avahi server state change: %i", state)
358
 
        bad_states = { avahi.SERVER_INVALID:
359
 
                           "Zeroconf server invalid",
360
 
                       avahi.SERVER_REGISTERING: None,
361
 
                       avahi.SERVER_COLLISION:
362
 
                           "Zeroconf server name collision",
363
 
                       avahi.SERVER_FAILURE:
364
 
                           "Zeroconf server failure" }
 
384
        bad_states = {
 
385
            avahi.SERVER_INVALID: "Zeroconf server invalid",
 
386
            avahi.SERVER_REGISTERING: None,
 
387
            avahi.SERVER_COLLISION: "Zeroconf server name collision",
 
388
            avahi.SERVER_FAILURE: "Zeroconf server failure",
 
389
        }
365
390
        if state in bad_states:
366
391
            if bad_states[state] is not None:
367
392
                if error is None:
386
411
                                    follow_name_owner_changes=True),
387
412
                avahi.DBUS_INTERFACE_SERVER)
388
413
        self.server.connect_to_signal("StateChanged",
389
 
                                 self.server_state_changed)
 
414
                                      self.server_state_changed)
390
415
        self.server_state_changed(self.server.GetState())
391
416
 
392
417
 
393
418
class AvahiServiceToSyslog(AvahiService):
394
 
    def rename(self):
 
419
    def rename(self, *args, **kwargs):
395
420
        """Add the new name to the syslog messages"""
396
 
        ret = AvahiService.rename(self)
397
 
        syslogger.setFormatter(logging.Formatter
398
 
                               ('Mandos ({}) [%(process)d]:'
399
 
                                ' %(levelname)s: %(message)s'
400
 
                                .format(self.name)))
 
421
        ret = AvahiService.rename(self, *args, **kwargs)
 
422
        syslogger.setFormatter(logging.Formatter(
 
423
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
 
424
            .format(self.name)))
401
425
        return ret
402
426
 
403
427
 
450
474
                          "fingerprint", "host", "interval",
451
475
                          "last_approval_request", "last_checked_ok",
452
476
                          "last_enabled", "name", "timeout")
453
 
    client_defaults = { "timeout": "PT5M",
454
 
                        "extended_timeout": "PT15M",
455
 
                        "interval": "PT2M",
456
 
                        "checker": "fping -q -- %%(host)s",
457
 
                        "host": "",
458
 
                        "approval_delay": "PT0S",
459
 
                        "approval_duration": "PT1S",
460
 
                        "approved_by_default": "True",
461
 
                        "enabled": "True",
462
 
                        }
 
477
    client_defaults = {
 
478
        "timeout": "PT5M",
 
479
        "extended_timeout": "PT15M",
 
480
        "interval": "PT2M",
 
481
        "checker": "fping -q -- %%(host)s",
 
482
        "host": "",
 
483
        "approval_delay": "PT0S",
 
484
        "approval_duration": "PT1S",
 
485
        "approved_by_default": "True",
 
486
        "enabled": "True",
 
487
    }
463
488
    
464
489
    @staticmethod
465
490
    def config_parser(config):
481
506
            client["enabled"] = config.getboolean(client_name,
482
507
                                                  "enabled")
483
508
            
 
509
            # Uppercase and remove spaces from fingerprint for later
 
510
            # comparison purposes with return value from the
 
511
            # fingerprint() function
484
512
            client["fingerprint"] = (section["fingerprint"].upper()
485
513
                                     .replace(" ", ""))
486
514
            if "secret" in section:
528
556
            self.expires = None
529
557
        
530
558
        logger.debug("Creating client %r", self.name)
531
 
        # Uppercase and remove spaces from fingerprint for later
532
 
        # comparison purposes with return value from the fingerprint()
533
 
        # function
534
559
        logger.debug("  Fingerprint: %s", self.fingerprint)
535
560
        self.created = settings.get("created",
536
561
                                    datetime.datetime.utcnow())
543
568
        self.current_checker_command = None
544
569
        self.approved = None
545
570
        self.approvals_pending = 0
546
 
        self.changedstate = (multiprocessing_manager
547
 
                             .Condition(multiprocessing_manager
548
 
                                        .Lock()))
549
 
        self.client_structure = [attr for attr in
550
 
                                 self.__dict__.iterkeys()
 
571
        self.changedstate = multiprocessing_manager.Condition(
 
572
            multiprocessing_manager.Lock())
 
573
        self.client_structure = [attr
 
574
                                 for attr in self.__dict__.iterkeys()
551
575
                                 if not attr.startswith("_")]
552
576
        self.client_structure.append("client_structure")
553
577
        
554
 
        for name, t in inspect.getmembers(type(self),
555
 
                                          lambda obj:
556
 
                                              isinstance(obj,
557
 
                                                         property)):
 
578
        for name, t in inspect.getmembers(
 
579
                type(self), lambda obj: isinstance(obj, property)):
558
580
            if not name.startswith("_"):
559
581
                self.client_structure.append(name)
560
582
    
602
624
        # and every interval from then on.
603
625
        if self.checker_initiator_tag is not None:
604
626
            gobject.source_remove(self.checker_initiator_tag)
605
 
        self.checker_initiator_tag = (gobject.timeout_add
606
 
                                      (int(self.interval
607
 
                                           .total_seconds() * 1000),
608
 
                                       self.start_checker))
 
627
        self.checker_initiator_tag = gobject.timeout_add(
 
628
            int(self.interval.total_seconds() * 1000),
 
629
            self.start_checker)
609
630
        # Schedule a disable() when 'timeout' has passed
610
631
        if self.disable_initiator_tag is not None:
611
632
            gobject.source_remove(self.disable_initiator_tag)
612
 
        self.disable_initiator_tag = (gobject.timeout_add
613
 
                                      (int(self.timeout
614
 
                                           .total_seconds() * 1000),
615
 
                                       self.disable))
 
633
        self.disable_initiator_tag = gobject.timeout_add(
 
634
            int(self.timeout.total_seconds() * 1000), self.disable)
616
635
        # Also start a new checker *right now*.
617
636
        self.start_checker()
618
637
    
627
646
                            vars(self))
628
647
                self.checked_ok()
629
648
            else:
630
 
                logger.info("Checker for %(name)s failed",
631
 
                            vars(self))
 
649
                logger.info("Checker for %(name)s failed", vars(self))
632
650
        else:
633
651
            self.last_checker_status = -1
634
652
            logger.warning("Checker for %(name)s crashed?",
648
666
            gobject.source_remove(self.disable_initiator_tag)
649
667
            self.disable_initiator_tag = None
650
668
        if getattr(self, "enabled", False):
651
 
            self.disable_initiator_tag = (gobject.timeout_add
652
 
                                          (int(timeout.total_seconds()
653
 
                                               * 1000), self.disable))
 
669
            self.disable_initiator_tag = gobject.timeout_add(
 
670
                int(timeout.total_seconds() * 1000), self.disable)
654
671
            self.expires = datetime.datetime.utcnow() + timeout
655
672
    
656
673
    def need_approval(self):
687
704
        # Start a new checker if needed
688
705
        if self.checker is None:
689
706
            # Escape attributes for the shell
690
 
            escaped_attrs = { attr:
691
 
                                  re.escape(str(getattr(self, attr)))
692
 
                              for attr in self.runtime_expansions }
 
707
            escaped_attrs = {
 
708
                attr: re.escape(str(getattr(self, attr)))
 
709
                for attr in self.runtime_expansions }
693
710
            try:
694
711
                command = self.checker_command % escaped_attrs
695
712
            except TypeError as error:
696
713
                logger.error('Could not format string "%s"',
697
 
                             self.checker_command, exc_info=error)
698
 
                return True # Try again later
 
714
                             self.checker_command,
 
715
                             exc_info=error)
 
716
                return True     # Try again later
699
717
            self.current_checker_command = command
700
718
            try:
701
 
                logger.info("Starting checker %r for %s",
702
 
                            command, self.name)
 
719
                logger.info("Starting checker %r for %s", command,
 
720
                            self.name)
703
721
                # We don't need to redirect stdout and stderr, since
704
722
                # in normal mode, that is already done by daemon(),
705
723
                # and in debug mode we don't want to.  (Stdin is
714
732
                                       "stderr": wnull })
715
733
                self.checker = subprocess.Popen(command,
716
734
                                                close_fds=True,
717
 
                                                shell=True, cwd="/",
 
735
                                                shell=True,
 
736
                                                cwd="/",
718
737
                                                **popen_args)
719
738
            except OSError as error:
720
739
                logger.error("Failed to start subprocess",
721
740
                             exc_info=error)
722
741
                return True
723
 
            self.checker_callback_tag = (gobject.child_watch_add
724
 
                                         (self.checker.pid,
725
 
                                          self.checker_callback,
726
 
                                          data=command))
 
742
            self.checker_callback_tag = gobject.child_watch_add(
 
743
                self.checker.pid, self.checker_callback, data=command)
727
744
            # The checker may have completed before the gobject
728
745
            # watch was added.  Check for this.
729
746
            try:
760
777
        self.checker = None
761
778
 
762
779
 
763
 
def dbus_service_property(dbus_interface, signature="v",
764
 
                          access="readwrite", byte_arrays=False):
 
780
def dbus_service_property(dbus_interface,
 
781
                          signature="v",
 
782
                          access="readwrite",
 
783
                          byte_arrays=False):
765
784
    """Decorators for marking methods of a DBusObjectWithProperties to
766
785
    become properties on the D-Bus.
767
786
    
777
796
    if byte_arrays and signature != "ay":
778
797
        raise ValueError("Byte arrays not supported for non-'ay'"
779
798
                         " signature {!r}".format(signature))
 
799
    
780
800
    def decorator(func):
781
801
        func._dbus_is_property = True
782
802
        func._dbus_interface = dbus_interface
787
807
            func._dbus_name = func._dbus_name[:-14]
788
808
        func._dbus_get_args_options = {'byte_arrays': byte_arrays }
789
809
        return func
 
810
    
790
811
    return decorator
791
812
 
792
813
 
801
822
                "org.freedesktop.DBus.Property.EmitsChangedSignal":
802
823
                    "false"}
803
824
    """
 
825
    
804
826
    def decorator(func):
805
827
        func._dbus_is_interface = True
806
828
        func._dbus_interface = dbus_interface
807
829
        func._dbus_name = dbus_interface
808
830
        return func
 
831
    
809
832
    return decorator
810
833
 
811
834
 
821
844
    def Property_dbus_property(self):
822
845
        return dbus.Boolean(False)
823
846
    """
 
847
    
824
848
    def decorator(func):
825
849
        func._dbus_annotations = annotations
826
850
        return func
 
851
    
827
852
    return decorator
828
853
 
829
854
 
832
857
    """
833
858
    pass
834
859
 
 
860
 
835
861
class DBusPropertyAccessException(DBusPropertyException):
836
862
    """A property's access permissions disallows an operation.
837
863
    """
865
891
    def _get_all_dbus_things(self, thing):
866
892
        """Returns a generator of (name, attribute) pairs
867
893
        """
868
 
        return ((getattr(athing.__get__(self), "_dbus_name",
869
 
                         name),
 
894
        return ((getattr(athing.__get__(self), "_dbus_name", name),
870
895
                 athing.__get__(self))
871
896
                for cls in self.__class__.__mro__
872
897
                for name, athing in
873
 
                inspect.getmembers(cls,
874
 
                                   self._is_dbus_thing(thing)))
 
898
                inspect.getmembers(cls, self._is_dbus_thing(thing)))
875
899
    
876
900
    def _get_dbus_property(self, interface_name, property_name):
877
901
        """Returns a bound method if one exists which is a D-Bus
878
902
        property with the specified name and interface.
879
903
        """
880
 
        for cls in  self.__class__.__mro__:
881
 
            for name, value in (inspect.getmembers
882
 
                                (cls,
883
 
                                 self._is_dbus_thing("property"))):
 
904
        for cls in self.__class__.__mro__:
 
905
            for name, value in inspect.getmembers(
 
906
                    cls, self._is_dbus_thing("property")):
884
907
                if (value._dbus_name == property_name
885
908
                    and value._dbus_interface == interface_name):
886
909
                    return value.__get__(self)
887
910
        
888
911
        # No such property
889
 
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
890
 
                                   + interface_name + "."
891
 
                                   + property_name)
 
912
        raise DBusPropertyNotFound("{}:{}.{}".format(
 
913
            self.dbus_object_path, interface_name, property_name))
892
914
    
893
 
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
 
915
    @dbus.service.method(dbus.PROPERTIES_IFACE,
 
916
                         in_signature="ss",
894
917
                         out_signature="v")
895
918
    def Get(self, interface_name, property_name):
896
919
        """Standard D-Bus property Get() method, see D-Bus standard.
921
944
                                            for byte in value))
922
945
        prop(value)
923
946
    
924
 
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
 
947
    @dbus.service.method(dbus.PROPERTIES_IFACE,
 
948
                         in_signature="s",
925
949
                         out_signature="a{sv}")
926
950
    def GetAll(self, interface_name):
927
951
        """Standard D-Bus property GetAll() method, see D-Bus
942
966
            if not hasattr(value, "variant_level"):
943
967
                properties[name] = value
944
968
                continue
945
 
            properties[name] = type(value)(value, variant_level=
946
 
                                           value.variant_level+1)
 
969
            properties[name] = type(value)(
 
970
                value, variant_level = value.variant_level + 1)
947
971
        return dbus.Dictionary(properties, signature="sv")
948
972
    
949
973
    @dbus.service.signal(dbus.PROPERTIES_IFACE, signature="sa{sv}as")
967
991
                                                   connection)
968
992
        try:
969
993
            document = xml.dom.minidom.parseString(xmlstring)
 
994
            
970
995
            def make_tag(document, name, prop):
971
996
                e = document.createElement("property")
972
997
                e.setAttribute("name", name)
973
998
                e.setAttribute("type", prop._dbus_signature)
974
999
                e.setAttribute("access", prop._dbus_access)
975
1000
                return e
 
1001
            
976
1002
            for if_tag in document.getElementsByTagName("interface"):
977
1003
                # Add property tags
978
1004
                for tag in (make_tag(document, name, prop)
990
1016
                            if (name == tag.getAttribute("name")
991
1017
                                and prop._dbus_interface
992
1018
                                == if_tag.getAttribute("name")):
993
 
                                annots.update(getattr
994
 
                                              (prop,
995
 
                                               "_dbus_annotations",
996
 
                                               {}))
 
1019
                                annots.update(getattr(
 
1020
                                    prop, "_dbus_annotations", {}))
997
1021
                        for name, value in annots.items():
998
1022
                            ann_tag = document.createElement(
999
1023
                                "annotation")
1004
1028
                for annotation, value in dict(
1005
1029
                    itertools.chain.from_iterable(
1006
1030
                        annotations().items()
1007
 
                        for name, annotations in
1008
 
                        self._get_all_dbus_things("interface")
 
1031
                        for name, annotations
 
1032
                        in self._get_all_dbus_things("interface")
1009
1033
                        if name == if_tag.getAttribute("name")
1010
1034
                        )).items():
1011
1035
                    ann_tag = document.createElement("annotation")
1040
1064
    """Convert a UTC datetime.datetime() to a D-Bus type."""
1041
1065
    if dt is None:
1042
1066
        return dbus.String("", variant_level = variant_level)
1043
 
    return dbus.String(dt.isoformat(),
1044
 
                       variant_level=variant_level)
 
1067
    return dbus.String(dt.isoformat(), variant_level=variant_level)
1045
1068
 
1046
1069
 
1047
1070
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
1067
1090
    (from DBusObjectWithProperties) and interfaces (from the
1068
1091
    dbus_interface_annotations decorator).
1069
1092
    """
 
1093
    
1070
1094
    def wrapper(cls):
1071
1095
        for orig_interface_name, alt_interface_name in (
1072
 
            alt_interface_names.items()):
 
1096
                alt_interface_names.items()):
1073
1097
            attr = {}
1074
1098
            interface_names = set()
1075
1099
            # Go though all attributes of the class
1077
1101
                # Ignore non-D-Bus attributes, and D-Bus attributes
1078
1102
                # with the wrong interface name
1079
1103
                if (not hasattr(attribute, "_dbus_interface")
1080
 
                    or not attribute._dbus_interface
1081
 
                    .startswith(orig_interface_name)):
 
1104
                    or not attribute._dbus_interface.startswith(
 
1105
                        orig_interface_name)):
1082
1106
                    continue
1083
1107
                # Create an alternate D-Bus interface name based on
1084
1108
                # the current name
1085
 
                alt_interface = (attribute._dbus_interface
1086
 
                                 .replace(orig_interface_name,
1087
 
                                          alt_interface_name))
 
1109
                alt_interface = attribute._dbus_interface.replace(
 
1110
                    orig_interface_name, alt_interface_name)
1088
1111
                interface_names.add(alt_interface)
1089
1112
                # Is this a D-Bus signal?
1090
1113
                if getattr(attribute, "_dbus_is_signal", False):
1091
1114
                    # Extract the original non-method undecorated
1092
1115
                    # function by black magic
1093
1116
                    nonmethod_func = (dict(
1094
 
                            zip(attribute.func_code.co_freevars,
1095
 
                                attribute.__closure__))["func"]
1096
 
                                      .cell_contents)
 
1117
                        zip(attribute.func_code.co_freevars,
 
1118
                            attribute.__closure__))
 
1119
                                      ["func"].cell_contents)
1097
1120
                    # Create a new, but exactly alike, function
1098
1121
                    # object, and decorate it to be a new D-Bus signal
1099
1122
                    # with the alternate D-Bus interface name
1100
 
                    new_function = (dbus.service.signal
1101
 
                                    (alt_interface,
1102
 
                                     attribute._dbus_signature)
 
1123
                    new_function = (dbus.service.signal(
 
1124
                        alt_interface, attribute._dbus_signature)
1103
1125
                                    (types.FunctionType(
1104
 
                                nonmethod_func.func_code,
1105
 
                                nonmethod_func.func_globals,
1106
 
                                nonmethod_func.func_name,
1107
 
                                nonmethod_func.func_defaults,
1108
 
                                nonmethod_func.func_closure)))
 
1126
                                        nonmethod_func.func_code,
 
1127
                                        nonmethod_func.func_globals,
 
1128
                                        nonmethod_func.func_name,
 
1129
                                        nonmethod_func.func_defaults,
 
1130
                                        nonmethod_func.func_closure)))
1109
1131
                    # Copy annotations, if any
1110
1132
                    try:
1111
 
                        new_function._dbus_annotations = (
1112
 
                            dict(attribute._dbus_annotations))
 
1133
                        new_function._dbus_annotations = dict(
 
1134
                            attribute._dbus_annotations)
1113
1135
                    except AttributeError:
1114
1136
                        pass
1115
1137
                    # Define a creator of a function to call both the
1120
1142
                        """This function is a scope container to pass
1121
1143
                        func1 and func2 to the "call_both" function
1122
1144
                        outside of its arguments"""
 
1145
                        
1123
1146
                        def call_both(*args, **kwargs):
1124
1147
                            """This function will emit two D-Bus
1125
1148
                            signals by calling func1 and func2"""
1126
1149
                            func1(*args, **kwargs)
1127
1150
                            func2(*args, **kwargs)
 
1151
                        
1128
1152
                        return call_both
1129
1153
                    # Create the "call_both" function and add it to
1130
1154
                    # the class
1135
1159
                    # object.  Decorate it to be a new D-Bus method
1136
1160
                    # with the alternate D-Bus interface name.  Add it
1137
1161
                    # to the class.
1138
 
                    attr[attrname] = (dbus.service.method
1139
 
                                      (alt_interface,
1140
 
                                       attribute._dbus_in_signature,
1141
 
                                       attribute._dbus_out_signature)
1142
 
                                      (types.FunctionType
1143
 
                                       (attribute.func_code,
1144
 
                                        attribute.func_globals,
1145
 
                                        attribute.func_name,
1146
 
                                        attribute.func_defaults,
1147
 
                                        attribute.func_closure)))
 
1162
                    attr[attrname] = (
 
1163
                        dbus.service.method(
 
1164
                            alt_interface,
 
1165
                            attribute._dbus_in_signature,
 
1166
                            attribute._dbus_out_signature)
 
1167
                        (types.FunctionType(attribute.func_code,
 
1168
                                            attribute.func_globals,
 
1169
                                            attribute.func_name,
 
1170
                                            attribute.func_defaults,
 
1171
                                            attribute.func_closure)))
1148
1172
                    # Copy annotations, if any
1149
1173
                    try:
1150
 
                        attr[attrname]._dbus_annotations = (
1151
 
                            dict(attribute._dbus_annotations))
 
1174
                        attr[attrname]._dbus_annotations = dict(
 
1175
                            attribute._dbus_annotations)
1152
1176
                    except AttributeError:
1153
1177
                        pass
1154
1178
                # Is this a D-Bus property?
1157
1181
                    # object, and decorate it to be a new D-Bus
1158
1182
                    # property with the alternate D-Bus interface
1159
1183
                    # name.  Add it to the class.
1160
 
                    attr[attrname] = (dbus_service_property
1161
 
                                      (alt_interface,
1162
 
                                       attribute._dbus_signature,
1163
 
                                       attribute._dbus_access,
1164
 
                                       attribute
1165
 
                                       ._dbus_get_args_options
1166
 
                                       ["byte_arrays"])
1167
 
                                      (types.FunctionType
1168
 
                                       (attribute.func_code,
1169
 
                                        attribute.func_globals,
1170
 
                                        attribute.func_name,
1171
 
                                        attribute.func_defaults,
1172
 
                                        attribute.func_closure)))
 
1184
                    attr[attrname] = (dbus_service_property(
 
1185
                        alt_interface, attribute._dbus_signature,
 
1186
                        attribute._dbus_access,
 
1187
                        attribute._dbus_get_args_options
 
1188
                        ["byte_arrays"])
 
1189
                                      (types.FunctionType(
 
1190
                                          attribute.func_code,
 
1191
                                          attribute.func_globals,
 
1192
                                          attribute.func_name,
 
1193
                                          attribute.func_defaults,
 
1194
                                          attribute.func_closure)))
1173
1195
                    # Copy annotations, if any
1174
1196
                    try:
1175
 
                        attr[attrname]._dbus_annotations = (
1176
 
                            dict(attribute._dbus_annotations))
 
1197
                        attr[attrname]._dbus_annotations = dict(
 
1198
                            attribute._dbus_annotations)
1177
1199
                    except AttributeError:
1178
1200
                        pass
1179
1201
                # Is this a D-Bus interface?
1182
1204
                    # object.  Decorate it to be a new D-Bus interface
1183
1205
                    # with the alternate D-Bus interface name.  Add it
1184
1206
                    # to the class.
1185
 
                    attr[attrname] = (dbus_interface_annotations
1186
 
                                      (alt_interface)
1187
 
                                      (types.FunctionType
1188
 
                                       (attribute.func_code,
1189
 
                                        attribute.func_globals,
1190
 
                                        attribute.func_name,
1191
 
                                        attribute.func_defaults,
1192
 
                                        attribute.func_closure)))
 
1207
                    attr[attrname] = (
 
1208
                        dbus_interface_annotations(alt_interface)
 
1209
                        (types.FunctionType(attribute.func_code,
 
1210
                                            attribute.func_globals,
 
1211
                                            attribute.func_name,
 
1212
                                            attribute.func_defaults,
 
1213
                                            attribute.func_closure)))
1193
1214
            if deprecate:
1194
1215
                # Deprecate all alternate interfaces
1195
1216
                iname="_AlternateDBusNames_interface_annotation{}"
1196
1217
                for interface_name in interface_names:
 
1218
                    
1197
1219
                    @dbus_interface_annotations(interface_name)
1198
1220
                    def func(self):
1199
1221
                        return { "org.freedesktop.DBus.Deprecated":
1200
 
                                     "true" }
 
1222
                                 "true" }
1201
1223
                    # Find an unused name
1202
1224
                    for aname in (iname.format(i)
1203
1225
                                  for i in itertools.count()):
1208
1230
                # Replace the class with a new subclass of it with
1209
1231
                # methods, signals, etc. as created above.
1210
1232
                cls = type(b"{}Alternate".format(cls.__name__),
1211
 
                           (cls,), attr)
 
1233
                           (cls, ), attr)
1212
1234
        return cls
 
1235
    
1213
1236
    return wrapper
1214
1237
 
1215
1238
 
1216
1239
@alternate_dbus_interfaces({"se.recompile.Mandos":
1217
 
                                "se.bsnet.fukt.Mandos"})
 
1240
                            "se.bsnet.fukt.Mandos"})
1218
1241
class ClientDBus(Client, DBusObjectWithProperties):
1219
1242
    """A Client class using D-Bus
1220
1243
    
1224
1247
    """
1225
1248
    
1226
1249
    runtime_expansions = (Client.runtime_expansions
1227
 
                          + ("dbus_object_path",))
 
1250
                          + ("dbus_object_path", ))
1228
1251
    
1229
1252
    _interface = "se.recompile.Mandos.Client"
1230
1253
    
1238
1261
        client_object_name = str(self.name).translate(
1239
1262
            {ord("."): ord("_"),
1240
1263
             ord("-"): ord("_")})
1241
 
        self.dbus_object_path = (dbus.ObjectPath
1242
 
                                 ("/clients/" + client_object_name))
 
1264
        self.dbus_object_path = dbus.ObjectPath(
 
1265
            "/clients/" + client_object_name)
1243
1266
        DBusObjectWithProperties.__init__(self, self.bus,
1244
1267
                                          self.dbus_object_path)
1245
1268
    
1246
 
    def notifychangeproperty(transform_func,
1247
 
                             dbus_name, type_func=lambda x: x,
1248
 
                             variant_level=1, invalidate_only=False,
 
1269
    def notifychangeproperty(transform_func, dbus_name,
 
1270
                             type_func=lambda x: x,
 
1271
                             variant_level=1,
 
1272
                             invalidate_only=False,
1249
1273
                             _interface=_interface):
1250
1274
        """ Modify a variable so that it's a property which announces
1251
1275
        its changes to DBus.
1258
1282
        variant_level: D-Bus variant level.  Default: 1
1259
1283
        """
1260
1284
        attrname = "_{}".format(dbus_name)
 
1285
        
1261
1286
        def setter(self, value):
1262
1287
            if hasattr(self, "dbus_object_path"):
1263
1288
                if (not hasattr(self, attrname) or
1264
1289
                    type_func(getattr(self, attrname, None))
1265
1290
                    != type_func(value)):
1266
1291
                    if invalidate_only:
1267
 
                        self.PropertiesChanged(_interface,
1268
 
                                               dbus.Dictionary(),
1269
 
                                               dbus.Array
1270
 
                                               ((dbus_name,)))
 
1292
                        self.PropertiesChanged(
 
1293
                            _interface, dbus.Dictionary(),
 
1294
                            dbus.Array((dbus_name, )))
1271
1295
                    else:
1272
 
                        dbus_value = transform_func(type_func(value),
1273
 
                                                    variant_level
1274
 
                                                    =variant_level)
 
1296
                        dbus_value = transform_func(
 
1297
                            type_func(value),
 
1298
                            variant_level = variant_level)
1275
1299
                        self.PropertyChanged(dbus.String(dbus_name),
1276
1300
                                             dbus_value)
1277
 
                        self.PropertiesChanged(_interface,
1278
 
                                               dbus.Dictionary({
1279
 
                                    dbus.String(dbus_name):
1280
 
                                        dbus_value }), dbus.Array())
 
1301
                        self.PropertiesChanged(
 
1302
                            _interface,
 
1303
                            dbus.Dictionary({ dbus.String(dbus_name):
 
1304
                                              dbus_value }),
 
1305
                            dbus.Array())
1281
1306
            setattr(self, attrname, value)
1282
1307
        
1283
1308
        return property(lambda self: getattr(self, attrname), setter)
1289
1314
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1290
1315
    last_enabled = notifychangeproperty(datetime_to_dbus,
1291
1316
                                        "LastEnabled")
1292
 
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
1293
 
                                   type_func = lambda checker:
1294
 
                                       checker is not None)
 
1317
    checker = notifychangeproperty(
 
1318
        dbus.Boolean, "CheckerRunning",
 
1319
        type_func = lambda checker: checker is not None)
1295
1320
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1296
1321
                                           "LastCheckedOK")
1297
1322
    last_checker_status = notifychangeproperty(dbus.Int16,
1300
1325
        datetime_to_dbus, "LastApprovalRequest")
1301
1326
    approved_by_default = notifychangeproperty(dbus.Boolean,
1302
1327
                                               "ApprovedByDefault")
1303
 
    approval_delay = notifychangeproperty(dbus.UInt64,
1304
 
                                          "ApprovalDelay",
1305
 
                                          type_func =
1306
 
                                          lambda td: td.total_seconds()
1307
 
                                          * 1000)
 
1328
    approval_delay = notifychangeproperty(
 
1329
        dbus.UInt64, "ApprovalDelay",
 
1330
        type_func = lambda td: td.total_seconds() * 1000)
1308
1331
    approval_duration = notifychangeproperty(
1309
1332
        dbus.UInt64, "ApprovalDuration",
1310
1333
        type_func = lambda td: td.total_seconds() * 1000)
1311
1334
    host = notifychangeproperty(dbus.String, "Host")
1312
 
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1313
 
                                   type_func = lambda td:
1314
 
                                       td.total_seconds() * 1000)
 
1335
    timeout = notifychangeproperty(
 
1336
        dbus.UInt64, "Timeout",
 
1337
        type_func = lambda td: td.total_seconds() * 1000)
1315
1338
    extended_timeout = notifychangeproperty(
1316
1339
        dbus.UInt64, "ExtendedTimeout",
1317
1340
        type_func = lambda td: td.total_seconds() * 1000)
1318
 
    interval = notifychangeproperty(dbus.UInt64,
1319
 
                                    "Interval",
1320
 
                                    type_func =
1321
 
                                    lambda td: td.total_seconds()
1322
 
                                    * 1000)
 
1341
    interval = notifychangeproperty(
 
1342
        dbus.UInt64, "Interval",
 
1343
        type_func = lambda td: td.total_seconds() * 1000)
1323
1344
    checker_command = notifychangeproperty(dbus.String, "Checker")
1324
1345
    secret = notifychangeproperty(dbus.ByteArray, "Secret",
1325
1346
                                  invalidate_only=True)
1463
1484
        return dbus.Boolean(bool(self.approvals_pending))
1464
1485
    
1465
1486
    # ApprovedByDefault - property
1466
 
    @dbus_service_property(_interface, signature="b",
 
1487
    @dbus_service_property(_interface,
 
1488
                           signature="b",
1467
1489
                           access="readwrite")
1468
1490
    def ApprovedByDefault_dbus_property(self, value=None):
1469
1491
        if value is None:       # get
1471
1493
        self.approved_by_default = bool(value)
1472
1494
    
1473
1495
    # ApprovalDelay - property
1474
 
    @dbus_service_property(_interface, signature="t",
 
1496
    @dbus_service_property(_interface,
 
1497
                           signature="t",
1475
1498
                           access="readwrite")
1476
1499
    def ApprovalDelay_dbus_property(self, value=None):
1477
1500
        if value is None:       # get
1480
1503
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
1481
1504
    
1482
1505
    # ApprovalDuration - property
1483
 
    @dbus_service_property(_interface, signature="t",
 
1506
    @dbus_service_property(_interface,
 
1507
                           signature="t",
1484
1508
                           access="readwrite")
1485
1509
    def ApprovalDuration_dbus_property(self, value=None):
1486
1510
        if value is None:       # get
1499
1523
        return dbus.String(self.fingerprint)
1500
1524
    
1501
1525
    # Host - property
1502
 
    @dbus_service_property(_interface, signature="s",
 
1526
    @dbus_service_property(_interface,
 
1527
                           signature="s",
1503
1528
                           access="readwrite")
1504
1529
    def Host_dbus_property(self, value=None):
1505
1530
        if value is None:       # get
1517
1542
        return datetime_to_dbus(self.last_enabled)
1518
1543
    
1519
1544
    # Enabled - property
1520
 
    @dbus_service_property(_interface, signature="b",
 
1545
    @dbus_service_property(_interface,
 
1546
                           signature="b",
1521
1547
                           access="readwrite")
1522
1548
    def Enabled_dbus_property(self, value=None):
1523
1549
        if value is None:       # get
1528
1554
            self.disable()
1529
1555
    
1530
1556
    # LastCheckedOK - property
1531
 
    @dbus_service_property(_interface, signature="s",
 
1557
    @dbus_service_property(_interface,
 
1558
                           signature="s",
1532
1559
                           access="readwrite")
1533
1560
    def LastCheckedOK_dbus_property(self, value=None):
1534
1561
        if value is not None:
1537
1564
        return datetime_to_dbus(self.last_checked_ok)
1538
1565
    
1539
1566
    # LastCheckerStatus - property
1540
 
    @dbus_service_property(_interface, signature="n",
1541
 
                           access="read")
 
1567
    @dbus_service_property(_interface, signature="n", access="read")
1542
1568
    def LastCheckerStatus_dbus_property(self):
1543
1569
        return dbus.Int16(self.last_checker_status)
1544
1570
    
1553
1579
        return datetime_to_dbus(self.last_approval_request)
1554
1580
    
1555
1581
    # Timeout - property
1556
 
    @dbus_service_property(_interface, signature="t",
 
1582
    @dbus_service_property(_interface,
 
1583
                           signature="t",
1557
1584
                           access="readwrite")
1558
1585
    def Timeout_dbus_property(self, value=None):
1559
1586
        if value is None:       # get
1572
1599
                    is None):
1573
1600
                    return
1574
1601
                gobject.source_remove(self.disable_initiator_tag)
1575
 
                self.disable_initiator_tag = (
1576
 
                    gobject.timeout_add(
1577
 
                        int((self.expires - now).total_seconds()
1578
 
                            * 1000), self.disable))
 
1602
                self.disable_initiator_tag = gobject.timeout_add(
 
1603
                    int((self.expires - now).total_seconds() * 1000),
 
1604
                    self.disable)
1579
1605
    
1580
1606
    # ExtendedTimeout - property
1581
 
    @dbus_service_property(_interface, signature="t",
 
1607
    @dbus_service_property(_interface,
 
1608
                           signature="t",
1582
1609
                           access="readwrite")
1583
1610
    def ExtendedTimeout_dbus_property(self, value=None):
1584
1611
        if value is None:       # get
1587
1614
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1588
1615
    
1589
1616
    # Interval - property
1590
 
    @dbus_service_property(_interface, signature="t",
 
1617
    @dbus_service_property(_interface,
 
1618
                           signature="t",
1591
1619
                           access="readwrite")
1592
1620
    def Interval_dbus_property(self, value=None):
1593
1621
        if value is None:       # get
1598
1626
        if self.enabled:
1599
1627
            # Reschedule checker run
1600
1628
            gobject.source_remove(self.checker_initiator_tag)
1601
 
            self.checker_initiator_tag = (gobject.timeout_add
1602
 
                                          (value, self.start_checker))
1603
 
            self.start_checker()    # Start one now, too
 
1629
            self.checker_initiator_tag = gobject.timeout_add(
 
1630
                value, self.start_checker)
 
1631
            self.start_checker() # Start one now, too
1604
1632
    
1605
1633
    # Checker - property
1606
 
    @dbus_service_property(_interface, signature="s",
 
1634
    @dbus_service_property(_interface,
 
1635
                           signature="s",
1607
1636
                           access="readwrite")
1608
1637
    def Checker_dbus_property(self, value=None):
1609
1638
        if value is None:       # get
1611
1640
        self.checker_command = str(value)
1612
1641
    
1613
1642
    # CheckerRunning - property
1614
 
    @dbus_service_property(_interface, signature="b",
 
1643
    @dbus_service_property(_interface,
 
1644
                           signature="b",
1615
1645
                           access="readwrite")
1616
1646
    def CheckerRunning_dbus_property(self, value=None):
1617
1647
        if value is None:       # get
1627
1657
        return self.dbus_object_path # is already a dbus.ObjectPath
1628
1658
    
1629
1659
    # Secret = property
1630
 
    @dbus_service_property(_interface, signature="ay",
1631
 
                           access="write", byte_arrays=True)
 
1660
    @dbus_service_property(_interface,
 
1661
                           signature="ay",
 
1662
                           access="write",
 
1663
                           byte_arrays=True)
1632
1664
    def Secret_dbus_property(self, value):
1633
1665
        self.secret = bytes(value)
1634
1666
    
1640
1672
        self._pipe = child_pipe
1641
1673
        self._pipe.send(('init', fpr, address))
1642
1674
        if not self._pipe.recv():
1643
 
            raise KeyError()
 
1675
            raise KeyError(fpr)
1644
1676
    
1645
1677
    def __getattribute__(self, name):
1646
1678
        if name == '_pipe':
1650
1682
        if data[0] == 'data':
1651
1683
            return data[1]
1652
1684
        if data[0] == 'function':
 
1685
            
1653
1686
            def func(*args, **kwargs):
1654
1687
                self._pipe.send(('funcall', name, args, kwargs))
1655
1688
                return self._pipe.recv()[1]
 
1689
            
1656
1690
            return func
1657
1691
    
1658
1692
    def __setattr__(self, name, value):
1674
1708
            logger.debug("Pipe FD: %d",
1675
1709
                         self.server.child_pipe.fileno())
1676
1710
            
1677
 
            session = (gnutls.connection
1678
 
                       .ClientSession(self.request,
1679
 
                                      gnutls.connection
1680
 
                                      .X509Credentials()))
 
1711
            session = gnutls.connection.ClientSession(
 
1712
                self.request, gnutls.connection .X509Credentials())
1681
1713
            
1682
1714
            # Note: gnutls.connection.X509Credentials is really a
1683
1715
            # generic GnuTLS certificate credentials object so long as
1692
1724
            priority = self.server.gnutls_priority
1693
1725
            if priority is None:
1694
1726
                priority = "NORMAL"
1695
 
            (gnutls.library.functions
1696
 
             .gnutls_priority_set_direct(session._c_object,
1697
 
                                         priority, None))
 
1727
            gnutls.library.functions.gnutls_priority_set_direct(
 
1728
                session._c_object, priority, None)
1698
1729
            
1699
1730
            # Start communication using the Mandos protocol
1700
1731
            # Get protocol number
1720
1751
            approval_required = False
1721
1752
            try:
1722
1753
                try:
1723
 
                    fpr = self.fingerprint(self.peer_certificate
1724
 
                                           (session))
 
1754
                    fpr = self.fingerprint(
 
1755
                        self.peer_certificate(session))
1725
1756
                except (TypeError,
1726
1757
                        gnutls.errors.GNUTLSError) as error:
1727
1758
                    logger.warning("Bad certificate: %s", error)
1742
1773
                while True:
1743
1774
                    if not client.enabled:
1744
1775
                        logger.info("Client %s is disabled",
1745
 
                                       client.name)
 
1776
                                    client.name)
1746
1777
                        if self.server.use_dbus:
1747
1778
                            # Emit D-Bus signal
1748
1779
                            client.Rejected("Disabled")
1795
1826
                        logger.warning("gnutls send failed",
1796
1827
                                       exc_info=error)
1797
1828
                        return
1798
 
                    logger.debug("Sent: %d, remaining: %d",
1799
 
                                 sent, len(client.secret)
1800
 
                                 - (sent_size + sent))
 
1829
                    logger.debug("Sent: %d, remaining: %d", sent,
 
1830
                                 len(client.secret) - (sent_size
 
1831
                                                       + sent))
1801
1832
                    sent_size += sent
1802
1833
                
1803
1834
                logger.info("Sending secret to %s", client.name)
1820
1851
    def peer_certificate(session):
1821
1852
        "Return the peer's OpenPGP certificate as a bytestring"
1822
1853
        # If not an OpenPGP certificate...
1823
 
        if (gnutls.library.functions
1824
 
            .gnutls_certificate_type_get(session._c_object)
 
1854
        if (gnutls.library.functions.gnutls_certificate_type_get(
 
1855
                session._c_object)
1825
1856
            != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1826
1857
            # ...do the normal thing
1827
1858
            return session.peer_certificate
1841
1872
    def fingerprint(openpgp):
1842
1873
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
1843
1874
        # New GnuTLS "datum" with the OpenPGP public key
1844
 
        datum = (gnutls.library.types
1845
 
                 .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1846
 
                                             ctypes.POINTER
1847
 
                                             (ctypes.c_ubyte)),
1848
 
                                 ctypes.c_uint(len(openpgp))))
 
1875
        datum = gnutls.library.types.gnutls_datum_t(
 
1876
            ctypes.cast(ctypes.c_char_p(openpgp),
 
1877
                        ctypes.POINTER(ctypes.c_ubyte)),
 
1878
            ctypes.c_uint(len(openpgp)))
1849
1879
        # New empty GnuTLS certificate
1850
1880
        crt = gnutls.library.types.gnutls_openpgp_crt_t()
1851
 
        (gnutls.library.functions
1852
 
         .gnutls_openpgp_crt_init(ctypes.byref(crt)))
 
1881
        gnutls.library.functions.gnutls_openpgp_crt_init(
 
1882
            ctypes.byref(crt))
1853
1883
        # Import the OpenPGP public key into the certificate
1854
 
        (gnutls.library.functions
1855
 
         .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1856
 
                                    gnutls.library.constants
1857
 
                                    .GNUTLS_OPENPGP_FMT_RAW))
 
1884
        gnutls.library.functions.gnutls_openpgp_crt_import(
 
1885
            crt, ctypes.byref(datum),
 
1886
            gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
1858
1887
        # Verify the self signature in the key
1859
1888
        crtverify = ctypes.c_uint()
1860
 
        (gnutls.library.functions
1861
 
         .gnutls_openpgp_crt_verify_self(crt, 0,
1862
 
                                         ctypes.byref(crtverify)))
 
1889
        gnutls.library.functions.gnutls_openpgp_crt_verify_self(
 
1890
            crt, 0, ctypes.byref(crtverify))
1863
1891
        if crtverify.value != 0:
1864
1892
            gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1865
 
            raise (gnutls.errors.CertificateSecurityError
1866
 
                   ("Verify failed"))
 
1893
            raise gnutls.errors.CertificateSecurityError(
 
1894
                "Verify failed")
1867
1895
        # New buffer for the fingerprint
1868
1896
        buf = ctypes.create_string_buffer(20)
1869
1897
        buf_len = ctypes.c_size_t()
1870
1898
        # Get the fingerprint from the certificate into the buffer
1871
 
        (gnutls.library.functions
1872
 
         .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1873
 
                                             ctypes.byref(buf_len)))
 
1899
        gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint(
 
1900
            crt, ctypes.byref(buf), ctypes.byref(buf_len))
1874
1901
        # Deinit the certificate
1875
1902
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1876
1903
        # Convert the buffer to a Python bytestring
1882
1909
 
1883
1910
class MultiprocessingMixIn(object):
1884
1911
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
 
1912
    
1885
1913
    def sub_process_main(self, request, address):
1886
1914
        try:
1887
1915
            self.finish_request(request, address)
1899
1927
 
1900
1928
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1901
1929
    """ adds a pipe to the MixIn """
 
1930
    
1902
1931
    def process_request(self, request, client_address):
1903
1932
        """Overrides and wraps the original process_request().
1904
1933
        
1925
1954
        interface:      None or a network interface name (string)
1926
1955
        use_ipv6:       Boolean; to use IPv6 or not
1927
1956
    """
 
1957
    
1928
1958
    def __init__(self, server_address, RequestHandlerClass,
1929
 
                 interface=None, use_ipv6=True, socketfd=None):
 
1959
                 interface=None,
 
1960
                 use_ipv6=True,
 
1961
                 socketfd=None):
1930
1962
        """If socketfd is set, use that file descriptor instead of
1931
1963
        creating a new one with socket.socket().
1932
1964
        """
1973
2005
                             self.interface)
1974
2006
            else:
1975
2007
                try:
1976
 
                    self.socket.setsockopt(socket.SOL_SOCKET,
1977
 
                                           SO_BINDTODEVICE,
1978
 
                                           (self.interface + "\0")
1979
 
                                           .encode("utf-8"))
 
2008
                    self.socket.setsockopt(
 
2009
                        socket.SOL_SOCKET, SO_BINDTODEVICE,
 
2010
                        (self.interface + "\0").encode("utf-8"))
1980
2011
                except socket.error as error:
1981
2012
                    if error.errno == errno.EPERM:
1982
2013
                        logger.error("No permission to bind to"
2000
2031
                self.server_address = (any_address,
2001
2032
                                       self.server_address[1])
2002
2033
            elif not self.server_address[1]:
2003
 
                self.server_address = (self.server_address[0],
2004
 
                                       0)
 
2034
                self.server_address = (self.server_address[0], 0)
2005
2035
#                 if self.interface:
2006
2036
#                     self.server_address = (self.server_address[0],
2007
2037
#                                            0, # port
2021
2051
    
2022
2052
    Assumes a gobject.MainLoop event loop.
2023
2053
    """
 
2054
    
2024
2055
    def __init__(self, server_address, RequestHandlerClass,
2025
 
                 interface=None, use_ipv6=True, clients=None,
2026
 
                 gnutls_priority=None, use_dbus=True, socketfd=None):
 
2056
                 interface=None,
 
2057
                 use_ipv6=True,
 
2058
                 clients=None,
 
2059
                 gnutls_priority=None,
 
2060
                 use_dbus=True,
 
2061
                 socketfd=None):
2027
2062
        self.enabled = False
2028
2063
        self.clients = clients
2029
2064
        if self.clients is None:
2035
2070
                                interface = interface,
2036
2071
                                use_ipv6 = use_ipv6,
2037
2072
                                socketfd = socketfd)
 
2073
    
2038
2074
    def server_activate(self):
2039
2075
        if self.enabled:
2040
2076
            return socketserver.TCPServer.server_activate(self)
2044
2080
    
2045
2081
    def add_pipe(self, parent_pipe, proc):
2046
2082
        # Call "handle_ipc" for both data and EOF events
2047
 
        gobject.io_add_watch(parent_pipe.fileno(),
2048
 
                             gobject.IO_IN | gobject.IO_HUP,
2049
 
                             functools.partial(self.handle_ipc,
2050
 
                                               parent_pipe =
2051
 
                                               parent_pipe,
2052
 
                                               proc = proc))
 
2083
        gobject.io_add_watch(
 
2084
            parent_pipe.fileno(),
 
2085
            gobject.IO_IN | gobject.IO_HUP,
 
2086
            functools.partial(self.handle_ipc,
 
2087
                              parent_pipe = parent_pipe,
 
2088
                              proc = proc))
2053
2089
    
2054
 
    def handle_ipc(self, source, condition, parent_pipe=None,
2055
 
                   proc = None, client_object=None):
 
2090
    def handle_ipc(self, source, condition,
 
2091
                   parent_pipe=None,
 
2092
                   proc = None,
 
2093
                   client_object=None):
2056
2094
        # error, or the other end of multiprocessing.Pipe has closed
2057
2095
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
2058
2096
            # Wait for other process to exit
2081
2119
                parent_pipe.send(False)
2082
2120
                return False
2083
2121
            
2084
 
            gobject.io_add_watch(parent_pipe.fileno(),
2085
 
                                 gobject.IO_IN | gobject.IO_HUP,
2086
 
                                 functools.partial(self.handle_ipc,
2087
 
                                                   parent_pipe =
2088
 
                                                   parent_pipe,
2089
 
                                                   proc = proc,
2090
 
                                                   client_object =
2091
 
                                                   client))
 
2122
            gobject.io_add_watch(
 
2123
                parent_pipe.fileno(),
 
2124
                gobject.IO_IN | gobject.IO_HUP,
 
2125
                functools.partial(self.handle_ipc,
 
2126
                                  parent_pipe = parent_pipe,
 
2127
                                  proc = proc,
 
2128
                                  client_object = client))
2092
2129
            parent_pipe.send(True)
2093
2130
            # remove the old hook in favor of the new above hook on
2094
2131
            # same fileno
2100
2137
            
2101
2138
            parent_pipe.send(('data', getattr(client_object,
2102
2139
                                              funcname)(*args,
2103
 
                                                         **kwargs)))
 
2140
                                                        **kwargs)))
2104
2141
        
2105
2142
        if command == 'getattr':
2106
2143
            attrname = request[1]
2107
2144
            if callable(client_object.__getattribute__(attrname)):
2108
 
                parent_pipe.send(('function',))
 
2145
                parent_pipe.send(('function', ))
2109
2146
            else:
2110
 
                parent_pipe.send(('data', client_object
2111
 
                                  .__getattribute__(attrname)))
 
2147
                parent_pipe.send((
 
2148
                    'data', client_object.__getattribute__(attrname)))
2112
2149
        
2113
2150
        if command == 'setattr':
2114
2151
            attrname = request[1]
2145
2182
    # avoid excessive use of external libraries.
2146
2183
    
2147
2184
    # New type for defining tokens, syntax, and semantics all-in-one
2148
 
    Token = collections.namedtuple("Token",
2149
 
                                   ("regexp", # To match token; if
2150
 
                                              # "value" is not None,
2151
 
                                              # must have a "group"
2152
 
                                              # containing digits
2153
 
                                    "value",  # datetime.timedelta or
2154
 
                                              # None
2155
 
                                    "followers")) # Tokens valid after
2156
 
                                                  # this token
 
2185
    Token = collections.namedtuple("Token", (
 
2186
        "regexp",  # To match token; if "value" is not None, must have
 
2187
                   # a "group" containing digits
 
2188
        "value",   # datetime.timedelta or None
 
2189
        "followers"))           # Tokens valid after this token
2157
2190
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
2158
2191
    # the "duration" ABNF definition in RFC 3339, Appendix A.
2159
2192
    token_end = Token(re.compile(r"$"), None, frozenset())
2160
2193
    token_second = Token(re.compile(r"(\d+)S"),
2161
2194
                         datetime.timedelta(seconds=1),
2162
 
                         frozenset((token_end,)))
 
2195
                         frozenset((token_end, )))
2163
2196
    token_minute = Token(re.compile(r"(\d+)M"),
2164
2197
                         datetime.timedelta(minutes=1),
2165
2198
                         frozenset((token_second, token_end)))
2181
2214
                       frozenset((token_month, token_end)))
2182
2215
    token_week = Token(re.compile(r"(\d+)W"),
2183
2216
                       datetime.timedelta(weeks=1),
2184
 
                       frozenset((token_end,)))
 
2217
                       frozenset((token_end, )))
2185
2218
    token_duration = Token(re.compile(r"P"), None,
2186
2219
                           frozenset((token_year, token_month,
2187
2220
                                      token_day, token_time,
2189
2222
    # Define starting values
2190
2223
    value = datetime.timedelta() # Value so far
2191
2224
    found_token = None
2192
 
    followers = frozenset((token_duration,)) # Following valid tokens
 
2225
    followers = frozenset((token_duration, )) # Following valid tokens
2193
2226
    s = duration                # String left to parse
2194
2227
    # Loop until end token is found
2195
2228
    while found_token is not token_end:
2212
2245
                break
2213
2246
        else:
2214
2247
            # No currently valid tokens were found
2215
 
            raise ValueError("Invalid RFC 3339 duration")
 
2248
            raise ValueError("Invalid RFC 3339 duration: {!r}"
 
2249
                             .format(duration))
2216
2250
    # End token found
2217
2251
    return value
2218
2252
 
2255
2289
            elif suffix == "w":
2256
2290
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
2257
2291
            else:
2258
 
                raise ValueError("Unknown suffix {!r}"
2259
 
                                 .format(suffix))
 
2292
                raise ValueError("Unknown suffix {!r}".format(suffix))
2260
2293
        except IndexError as e:
2261
2294
            raise ValueError(*(e.args))
2262
2295
        timevalue += delta
2278
2311
        # Close all standard open file descriptors
2279
2312
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
2280
2313
        if not stat.S_ISCHR(os.fstat(null).st_mode):
2281
 
            raise OSError(errno.ENODEV, "{} not a character device"
 
2314
            raise OSError(errno.ENODEV,
 
2315
                          "{} not a character device"
2282
2316
                          .format(os.devnull))
2283
2317
        os.dup2(null, sys.stdin.fileno())
2284
2318
        os.dup2(null, sys.stdout.fileno())
2350
2384
                        "port": "",
2351
2385
                        "debug": "False",
2352
2386
                        "priority":
2353
 
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:+SIGN-RSA-SHA224:+SIGN-RSA-RMD160",
 
2387
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
 
2388
                        ":+SIGN-RSA-SHA224:+SIGN-RSA-RMD160",
2354
2389
                        "servicename": "Mandos",
2355
2390
                        "use_dbus": "True",
2356
2391
                        "use_ipv6": "True",
2360
2395
                        "statedir": "/var/lib/mandos",
2361
2396
                        "foreground": "False",
2362
2397
                        "zeroconf": "True",
2363
 
                        }
 
2398
                    }
2364
2399
    
2365
2400
    # Parse config file for server-global settings
2366
2401
    server_config = configparser.SafeConfigParser(server_defaults)
2367
2402
    del server_defaults
2368
 
    server_config.read(os.path.join(options.configdir,
2369
 
                                    "mandos.conf"))
 
2403
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
2370
2404
    # Convert the SafeConfigParser object to a dict
2371
2405
    server_settings = server_config.defaults()
2372
2406
    # Use the appropriate methods on the non-string config options
2390
2424
    # Override the settings from the config file with command line
2391
2425
    # options, if set.
2392
2426
    for option in ("interface", "address", "port", "debug",
2393
 
                   "priority", "servicename", "configdir",
2394
 
                   "use_dbus", "use_ipv6", "debuglevel", "restore",
2395
 
                   "statedir", "socket", "foreground", "zeroconf"):
 
2427
                   "priority", "servicename", "configdir", "use_dbus",
 
2428
                   "use_ipv6", "debuglevel", "restore", "statedir",
 
2429
                   "socket", "foreground", "zeroconf"):
2396
2430
        value = getattr(options, option)
2397
2431
        if value is not None:
2398
2432
            server_settings[option] = value
2413
2447
    
2414
2448
    ##################################################################
2415
2449
    
2416
 
    if (not server_settings["zeroconf"] and
2417
 
        not (server_settings["port"]
2418
 
             or server_settings["socket"] != "")):
2419
 
            parser.error("Needs port or socket to work without"
2420
 
                         " Zeroconf")
 
2450
    if (not server_settings["zeroconf"]
 
2451
        and not (server_settings["port"]
 
2452
                 or server_settings["socket"] != "")):
 
2453
        parser.error("Needs port or socket to work without Zeroconf")
2421
2454
    
2422
2455
    # For convenience
2423
2456
    debug = server_settings["debug"]
2439
2472
            initlogger(debug, level)
2440
2473
    
2441
2474
    if server_settings["servicename"] != "Mandos":
2442
 
        syslogger.setFormatter(logging.Formatter
2443
 
                               ('Mandos ({}) [%(process)d]:'
2444
 
                                ' %(levelname)s: %(message)s'
2445
 
                                .format(server_settings
2446
 
                                        ["servicename"])))
 
2475
        syslogger.setFormatter(
 
2476
            logging.Formatter('Mandos ({}) [%(process)d]:'
 
2477
                              ' %(levelname)s: %(message)s'.format(
 
2478
                                  server_settings["servicename"])))
2447
2479
    
2448
2480
    # Parse config file with clients
2449
2481
    client_config = configparser.SafeConfigParser(Client
2457
2489
    socketfd = None
2458
2490
    if server_settings["socket"] != "":
2459
2491
        socketfd = server_settings["socket"]
2460
 
    tcp_server = MandosServer((server_settings["address"],
2461
 
                               server_settings["port"]),
2462
 
                              ClientHandler,
2463
 
                              interface=(server_settings["interface"]
2464
 
                                         or None),
2465
 
                              use_ipv6=use_ipv6,
2466
 
                              gnutls_priority=
2467
 
                              server_settings["priority"],
2468
 
                              use_dbus=use_dbus,
2469
 
                              socketfd=socketfd)
 
2492
    tcp_server = MandosServer(
 
2493
        (server_settings["address"], server_settings["port"]),
 
2494
        ClientHandler,
 
2495
        interface=(server_settings["interface"] or None),
 
2496
        use_ipv6=use_ipv6,
 
2497
        gnutls_priority=server_settings["priority"],
 
2498
        use_dbus=use_dbus,
 
2499
        socketfd=socketfd)
2470
2500
    if not foreground:
2471
2501
        pidfilename = "/run/mandos.pid"
2472
2502
        if not os.path.isdir("/run/."):
2473
2503
            pidfilename = "/var/run/mandos.pid"
2474
2504
        pidfile = None
2475
2505
        try:
2476
 
            pidfile = open(pidfilename, "w")
 
2506
            pidfile = codecs.open(pidfilename, "w", encoding="utf-8")
2477
2507
        except IOError as e:
2478
2508
            logger.error("Could not open file %r", pidfilename,
2479
2509
                         exc_info=e)
2506
2536
        def debug_gnutls(level, string):
2507
2537
            logger.debug("GnuTLS: %s", string[:-1])
2508
2538
        
2509
 
        (gnutls.library.functions
2510
 
         .gnutls_global_set_log_function(debug_gnutls))
 
2539
        gnutls.library.functions.gnutls_global_set_log_function(
 
2540
            debug_gnutls)
2511
2541
        
2512
2542
        # Redirect stdin so all checkers get /dev/null
2513
2543
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
2533
2563
    if use_dbus:
2534
2564
        try:
2535
2565
            bus_name = dbus.service.BusName("se.recompile.Mandos",
2536
 
                                            bus, do_not_queue=True)
2537
 
            old_bus_name = (dbus.service.BusName
2538
 
                            ("se.bsnet.fukt.Mandos", bus,
2539
 
                             do_not_queue=True))
2540
 
        except dbus.exceptions.NameExistsException as e:
 
2566
                                            bus,
 
2567
                                            do_not_queue=True)
 
2568
            old_bus_name = dbus.service.BusName(
 
2569
                "se.bsnet.fukt.Mandos", bus,
 
2570
                do_not_queue=True)
 
2571
        except dbus.exceptions.DBusException as e:
2541
2572
            logger.error("Disabling D-Bus:", exc_info=e)
2542
2573
            use_dbus = False
2543
2574
            server_settings["use_dbus"] = False
2544
2575
            tcp_server.use_dbus = False
2545
2576
    if zeroconf:
2546
2577
        protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2547
 
        service = AvahiServiceToSyslog(name =
2548
 
                                       server_settings["servicename"],
2549
 
                                       servicetype = "_mandos._tcp",
2550
 
                                       protocol = protocol, bus = bus)
 
2578
        service = AvahiServiceToSyslog(
 
2579
            name = server_settings["servicename"],
 
2580
            servicetype = "_mandos._tcp",
 
2581
            protocol = protocol,
 
2582
            bus = bus)
2551
2583
        if server_settings["interface"]:
2552
 
            service.interface = (if_nametoindex
2553
 
                                 (server_settings["interface"]
2554
 
                                  .encode("utf-8")))
 
2584
            service.interface = if_nametoindex(
 
2585
                server_settings["interface"].encode("utf-8"))
2555
2586
    
2556
2587
    global multiprocessing_manager
2557
2588
    multiprocessing_manager = multiprocessing.Manager()
2576
2607
    if server_settings["restore"]:
2577
2608
        try:
2578
2609
            with open(stored_state_path, "rb") as stored_state:
2579
 
                clients_data, old_client_settings = (pickle.load
2580
 
                                                     (stored_state))
 
2610
                clients_data, old_client_settings = pickle.load(
 
2611
                    stored_state)
2581
2612
            os.remove(stored_state_path)
2582
2613
        except IOError as e:
2583
2614
            if e.errno == errno.ENOENT:
2584
 
                logger.warning("Could not load persistent state: {}"
2585
 
                                .format(os.strerror(e.errno)))
 
2615
                logger.warning("Could not load persistent state:"
 
2616
                               " {}".format(os.strerror(e.errno)))
2586
2617
            else:
2587
2618
                logger.critical("Could not load persistent state:",
2588
2619
                                exc_info=e)
2589
2620
                raise
2590
2621
        except EOFError as e:
2591
2622
            logger.warning("Could not load persistent state: "
2592
 
                           "EOFError:", exc_info=e)
 
2623
                           "EOFError:",
 
2624
                           exc_info=e)
2593
2625
    
2594
2626
    with PGPEngine() as pgp:
2595
2627
        for client_name, client in clients_data.items():
2607
2639
                    # For each value in new config, check if it
2608
2640
                    # differs from the old config value (Except for
2609
2641
                    # the "secret" attribute)
2610
 
                    if (name != "secret" and
2611
 
                        value != old_client_settings[client_name]
2612
 
                        [name]):
 
2642
                    if (name != "secret"
 
2643
                        and (value !=
 
2644
                             old_client_settings[client_name][name])):
2613
2645
                        client[name] = value
2614
2646
                except KeyError:
2615
2647
                    pass
2624
2656
                    if not client["last_checked_ok"]:
2625
2657
                        logger.warning(
2626
2658
                            "disabling client {} - Client never "
2627
 
                            "performed a successful checker"
2628
 
                            .format(client_name))
 
2659
                            "performed a successful checker".format(
 
2660
                                client_name))
2629
2661
                        client["enabled"] = False
2630
2662
                    elif client["last_checker_status"] != 0:
2631
2663
                        logger.warning(
2632
2664
                            "disabling client {} - Client last"
2633
 
                            " checker failed with error code {}"
2634
 
                            .format(client_name,
2635
 
                                    client["last_checker_status"]))
 
2665
                            " checker failed with error code"
 
2666
                            " {}".format(
 
2667
                                client_name,
 
2668
                                client["last_checker_status"]))
2636
2669
                        client["enabled"] = False
2637
2670
                    else:
2638
 
                        client["expires"] = (datetime.datetime
2639
 
                                             .utcnow()
2640
 
                                             + client["timeout"])
 
2671
                        client["expires"] = (
 
2672
                            datetime.datetime.utcnow()
 
2673
                            + client["timeout"])
2641
2674
                        logger.debug("Last checker succeeded,"
2642
 
                                     " keeping {} enabled"
2643
 
                                     .format(client_name))
 
2675
                                     " keeping {} enabled".format(
 
2676
                                         client_name))
2644
2677
            try:
2645
 
                client["secret"] = (
2646
 
                    pgp.decrypt(client["encrypted_secret"],
2647
 
                                client_settings[client_name]
2648
 
                                ["secret"]))
 
2678
                client["secret"] = pgp.decrypt(
 
2679
                    client["encrypted_secret"],
 
2680
                    client_settings[client_name]["secret"])
2649
2681
            except PGPError:
2650
2682
                # If decryption fails, we use secret from new settings
2651
 
                logger.debug("Failed to decrypt {} old secret"
2652
 
                             .format(client_name))
2653
 
                client["secret"] = (
2654
 
                    client_settings[client_name]["secret"])
 
2683
                logger.debug("Failed to decrypt {} old secret".format(
 
2684
                    client_name))
 
2685
                client["secret"] = (client_settings[client_name]
 
2686
                                    ["secret"])
2655
2687
    
2656
2688
    # Add/remove clients based on new changes made to config
2657
2689
    for client_name in (set(old_client_settings)
2664
2696
    # Create all client objects
2665
2697
    for client_name, client in clients_data.items():
2666
2698
        tcp_server.clients[client_name] = client_class(
2667
 
            name = client_name, settings = client,
 
2699
            name = client_name,
 
2700
            settings = client,
2668
2701
            server_settings = server_settings)
2669
2702
    
2670
2703
    if not tcp_server.clients:
2672
2705
    
2673
2706
    if not foreground:
2674
2707
        if pidfile is not None:
 
2708
            pid = os.getpid()
2675
2709
            try:
2676
2710
                with pidfile:
2677
 
                    pid = os.getpid()
2678
 
                    pidfile.write("{}\n".format(pid).encode("utf-8"))
 
2711
                    print(pid, file=pidfile)
2679
2712
            except IOError:
2680
2713
                logger.error("Could not write to file %r with PID %d",
2681
2714
                             pidfilename, pid)
2686
2719
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2687
2720
    
2688
2721
    if use_dbus:
2689
 
        @alternate_dbus_interfaces({"se.recompile.Mandos":
2690
 
                                        "se.bsnet.fukt.Mandos"})
 
2722
        
 
2723
        @alternate_dbus_interfaces(
 
2724
            { "se.recompile.Mandos": "se.bsnet.fukt.Mandos" })
2691
2725
        class MandosDBusService(DBusObjectWithProperties):
2692
2726
            """A D-Bus proxy object"""
 
2727
            
2693
2728
            def __init__(self):
2694
2729
                dbus.service.Object.__init__(self, bus, "/")
 
2730
            
2695
2731
            _interface = "se.recompile.Mandos"
2696
2732
            
2697
2733
            @dbus_interface_annotations(_interface)
2698
2734
            def _foo(self):
2699
 
                return { "org.freedesktop.DBus.Property"
2700
 
                         ".EmitsChangedSignal":
2701
 
                             "false"}
 
2735
                return {
 
2736
                    "org.freedesktop.DBus.Property.EmitsChangedSignal":
 
2737
                    "false" }
2702
2738
            
2703
2739
            @dbus.service.signal(_interface, signature="o")
2704
2740
            def ClientAdded(self, objpath):
2718
2754
            @dbus.service.method(_interface, out_signature="ao")
2719
2755
            def GetAllClients(self):
2720
2756
                "D-Bus method"
2721
 
                return dbus.Array(c.dbus_object_path
2722
 
                                  for c in
 
2757
                return dbus.Array(c.dbus_object_path for c in
2723
2758
                                  tcp_server.clients.itervalues())
2724
2759
            
2725
2760
            @dbus.service.method(_interface,
2774
2809
                # + secret.
2775
2810
                exclude = { "bus", "changedstate", "secret",
2776
2811
                            "checker", "server_settings" }
2777
 
                for name, typ in (inspect.getmembers
2778
 
                                  (dbus.service.Object)):
 
2812
                for name, typ in inspect.getmembers(dbus.service
 
2813
                                                    .Object):
2779
2814
                    exclude.add(name)
2780
2815
                
2781
2816
                client_dict["encrypted_secret"] = (client
2788
2823
                del client_settings[client.name]["secret"]
2789
2824
        
2790
2825
        try:
2791
 
            with (tempfile.NamedTemporaryFile
2792
 
                  (mode='wb', suffix=".pickle", prefix='clients-',
2793
 
                   dir=os.path.dirname(stored_state_path),
2794
 
                   delete=False)) as stored_state:
 
2826
            with tempfile.NamedTemporaryFile(
 
2827
                    mode='wb',
 
2828
                    suffix=".pickle",
 
2829
                    prefix='clients-',
 
2830
                    dir=os.path.dirname(stored_state_path),
 
2831
                    delete=False) as stored_state:
2795
2832
                pickle.dump((clients, client_settings), stored_state)
2796
 
                tempname=stored_state.name
 
2833
                tempname = stored_state.name
2797
2834
            os.rename(tempname, stored_state_path)
2798
2835
        except (IOError, OSError) as e:
2799
2836
            if not debug:
2818
2855
            client.disable(quiet=True)
2819
2856
            if use_dbus:
2820
2857
                # Emit D-Bus signal
2821
 
                mandos_dbus_service.ClientRemoved(client
2822
 
                                                  .dbus_object_path,
2823
 
                                                  client.name)
 
2858
                mandos_dbus_service.ClientRemoved(
 
2859
                    client.dbus_object_path, client.name)
2824
2860
        client_settings.clear()
2825
2861
    
2826
2862
    atexit.register(cleanup)
2879
2915
    # Must run before the D-Bus bus name gets deregistered
2880
2916
    cleanup()
2881
2917
 
 
2918
 
2882
2919
if __name__ == '__main__':
2883
2920
    main()