/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2016-03-05 21:46:00 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 335.
  • Revision ID: teddy@recompile.se-20160305214600-2d7peg8qb5xhpi64
Fix incorrect documentation of minor limitation

* mandos-monitor.xml (BUGS): The name which is hard-coded is the D-Bus
                             *bus* name, not the service name.  Fix
                             the documentation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
from __future__ import (division, absolute_import, print_function,
35
35
                        unicode_literals)
36
36
 
37
 
try:
38
 
    from future_builtins import *
39
 
except ImportError:
40
 
    pass
 
37
from future_builtins import *
41
38
 
42
39
try:
43
40
    import SocketServer as socketserver
79
76
 
80
77
import dbus
81
78
import dbus.service
82
 
from gi.repository import GLib
 
79
try:
 
80
    from gi.repository import GObject
 
81
except ImportError:
 
82
    import gobject as GObject
 
83
import avahi
83
84
from dbus.mainloop.glib import DBusGMainLoop
84
85
import ctypes
85
86
import ctypes.util
86
87
import xml.dom.minidom
87
88
import inspect
88
89
 
89
 
# Try to find the value of SO_BINDTODEVICE:
90
90
try:
91
 
    # This is where SO_BINDTODEVICE is in Python 3.3 (or 3.4?) and
92
 
    # newer, and it is also the most natural place for it:
93
91
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
94
92
except AttributeError:
95
93
    try:
96
 
        # This is where SO_BINDTODEVICE was up to and including Python
97
 
        # 2.6, and also 3.2:
98
94
        from IN import SO_BINDTODEVICE
99
95
    except ImportError:
100
 
        # In Python 2.7 it seems to have been removed entirely.
101
 
        # Try running the C preprocessor:
102
 
        try:
103
 
            cc = subprocess.Popen(["cc", "--language=c", "-E",
104
 
                                   "/dev/stdin"],
105
 
                                  stdin=subprocess.PIPE,
106
 
                                  stdout=subprocess.PIPE)
107
 
            stdout = cc.communicate(
108
 
                "#include <sys/socket.h>\nSO_BINDTODEVICE\n")[0]
109
 
            SO_BINDTODEVICE = int(stdout.splitlines()[-1])
110
 
        except (OSError, ValueError, IndexError):
111
 
            # No value found
112
 
            SO_BINDTODEVICE = None
 
96
        SO_BINDTODEVICE = None
113
97
 
114
98
if sys.version_info.major == 2:
115
99
    str = unicode
116
100
 
117
 
version = "1.7.10"
 
101
version = "1.7.3"
118
102
stored_state_file = "clients.pickle"
119
103
 
120
104
logger = logging.getLogger()
135
119
        return interface_index
136
120
 
137
121
 
138
 
def copy_function(func):
139
 
    """Make a copy of a function"""
140
 
    if sys.version_info.major == 2:
141
 
        return types.FunctionType(func.func_code,
142
 
                                  func.func_globals,
143
 
                                  func.func_name,
144
 
                                  func.func_defaults,
145
 
                                  func.func_closure)
146
 
    else:
147
 
        return types.FunctionType(func.__code__,
148
 
                                  func.__globals__,
149
 
                                  func.__name__,
150
 
                                  func.__defaults__,
151
 
                                  func.__closure__)
152
 
 
153
 
 
154
122
def initlogger(debug, level=logging.WARNING):
155
123
    """init logger and add loglevel"""
156
124
    
187
155
        try:
188
156
            output = subprocess.check_output(["gpgconf"])
189
157
            for line in output.splitlines():
190
 
                name, text, path = line.split(b":")
 
158
                name, text, path = line.split(":")
191
159
                if name == "gpg":
192
160
                    self.gpg = path
193
161
                    break
197
165
        self.gnupgargs = ['--batch',
198
166
                          '--homedir', self.tempdir,
199
167
                          '--force-mdc',
200
 
                          '--quiet']
201
 
        # Only GPG version 1 has the --no-use-agent option.
202
 
        if self.gpg == "gpg" or self.gpg.endswith("/gpg"):
203
 
            self.gnupgargs.append("--no-use-agent")
 
168
                          '--quiet',
 
169
                          '--no-use-agent']
204
170
    
205
171
    def __enter__(self):
206
172
        return self
272
238
            raise PGPError(err)
273
239
        return decrypted_plaintext
274
240
 
275
 
# Pretend that we have an Avahi module
276
 
class Avahi(object):
277
 
    """This isn't so much a class as it is a module-like namespace.
278
 
    It is instantiated once, and simulates having an Avahi module."""
279
 
    IF_UNSPEC = -1              # avahi-common/address.h
280
 
    PROTO_UNSPEC = -1           # avahi-common/address.h
281
 
    PROTO_INET = 0              # avahi-common/address.h
282
 
    PROTO_INET6 = 1             # avahi-common/address.h
283
 
    DBUS_NAME = "org.freedesktop.Avahi"
284
 
    DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
285
 
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
286
 
    DBUS_PATH_SERVER = "/"
287
 
    def string_array_to_txt_array(self, t):
288
 
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
289
 
                           for s in t), signature="ay")
290
 
    ENTRY_GROUP_ESTABLISHED = 2 # avahi-common/defs.h
291
 
    ENTRY_GROUP_COLLISION = 3   # avahi-common/defs.h
292
 
    ENTRY_GROUP_FAILURE = 4     # avahi-common/defs.h
293
 
    SERVER_INVALID = 0          # avahi-common/defs.h
294
 
    SERVER_REGISTERING = 1      # avahi-common/defs.h
295
 
    SERVER_RUNNING = 2          # avahi-common/defs.h
296
 
    SERVER_COLLISION = 3        # avahi-common/defs.h
297
 
    SERVER_FAILURE = 4          # avahi-common/defs.h
298
 
avahi = Avahi()
299
241
 
300
242
class AvahiError(Exception):
301
243
    def __init__(self, value, *args, **kwargs):
505
447
    
506
448
    _library = ctypes.cdll.LoadLibrary(
507
449
        ctypes.util.find_library("gnutls"))
508
 
    _need_version = b"3.3.0"
 
450
    _need_version = "3.3.0"
509
451
    def __init__(self):
510
452
        # Need to use class name "GnuTLS" here, since this method is
511
453
        # called before the assignment to the "gnutls" global variable
545
487
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
546
488
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
547
489
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
548
 
    credentials_type_t = ctypes.c_int
 
490
    credentials_type_t = ctypes.c_int # 
549
491
    transport_ptr_t = ctypes.c_void_p
550
492
    close_request_t = ctypes.c_int
551
493
    
773
715
    checker:    subprocess.Popen(); a running checker process used
774
716
                                    to see if the client lives.
775
717
                                    'None' if no process is running.
776
 
    checker_callback_tag: a GLib event source tag, or None
 
718
    checker_callback_tag: a GObject event source tag, or None
777
719
    checker_command: string; External command which is run to check
778
720
                     if client lives.  %() expansions are done at
779
721
                     runtime with vars(self) as dict, so that for
780
722
                     instance %(name)s can be used in the command.
781
 
    checker_initiator_tag: a GLib event source tag, or None
 
723
    checker_initiator_tag: a GObject event source tag, or None
782
724
    created:    datetime.datetime(); (UTC) object creation
783
725
    client_structure: Object describing what attributes a client has
784
726
                      and is used for storing the client at exit
785
727
    current_checker_command: string; current running checker_command
786
 
    disable_initiator_tag: a GLib event source tag, or None
 
728
    disable_initiator_tag: a GObject event source tag, or None
787
729
    enabled:    bool()
788
730
    fingerprint: string (40 or 32 hexadecimal digits); used to
789
731
                 uniquely identify the client
852
794
            client["fingerprint"] = (section["fingerprint"].upper()
853
795
                                     .replace(" ", ""))
854
796
            if "secret" in section:
855
 
                client["secret"] = codecs.decode(section["secret"]
856
 
                                                 .encode("utf-8"),
857
 
                                                 "base64")
 
797
                client["secret"] = section["secret"].decode("base64")
858
798
            elif "secfile" in section:
859
799
                with open(os.path.expanduser(os.path.expandvars
860
800
                                             (section["secfile"])),
913
853
        self.changedstate = multiprocessing_manager.Condition(
914
854
            multiprocessing_manager.Lock())
915
855
        self.client_structure = [attr
916
 
                                 for attr in self.__dict__.keys()
 
856
                                 for attr in self.__dict__.iterkeys()
917
857
                                 if not attr.startswith("_")]
918
858
        self.client_structure.append("client_structure")
919
859
        
945
885
        if not quiet:
946
886
            logger.info("Disabling client %s", self.name)
947
887
        if getattr(self, "disable_initiator_tag", None) is not None:
948
 
            GLib.source_remove(self.disable_initiator_tag)
 
888
            GObject.source_remove(self.disable_initiator_tag)
949
889
            self.disable_initiator_tag = None
950
890
        self.expires = None
951
891
        if getattr(self, "checker_initiator_tag", None) is not None:
952
 
            GLib.source_remove(self.checker_initiator_tag)
 
892
            GObject.source_remove(self.checker_initiator_tag)
953
893
            self.checker_initiator_tag = None
954
894
        self.stop_checker()
955
895
        self.enabled = False
956
896
        if not quiet:
957
897
            self.send_changedstate()
958
 
        # Do not run this again if called by a GLib.timeout_add
 
898
        # Do not run this again if called by a GObject.timeout_add
959
899
        return False
960
900
    
961
901
    def __del__(self):
965
905
        # Schedule a new checker to be started an 'interval' from now,
966
906
        # and every interval from then on.
967
907
        if self.checker_initiator_tag is not None:
968
 
            GLib.source_remove(self.checker_initiator_tag)
969
 
        self.checker_initiator_tag = GLib.timeout_add(
 
908
            GObject.source_remove(self.checker_initiator_tag)
 
909
        self.checker_initiator_tag = GObject.timeout_add(
970
910
            int(self.interval.total_seconds() * 1000),
971
911
            self.start_checker)
972
912
        # Schedule a disable() when 'timeout' has passed
973
913
        if self.disable_initiator_tag is not None:
974
 
            GLib.source_remove(self.disable_initiator_tag)
975
 
        self.disable_initiator_tag = GLib.timeout_add(
 
914
            GObject.source_remove(self.disable_initiator_tag)
 
915
        self.disable_initiator_tag = GObject.timeout_add(
976
916
            int(self.timeout.total_seconds() * 1000), self.disable)
977
917
        # Also start a new checker *right now*.
978
918
        self.start_checker()
1014
954
        if timeout is None:
1015
955
            timeout = self.timeout
1016
956
        if self.disable_initiator_tag is not None:
1017
 
            GLib.source_remove(self.disable_initiator_tag)
 
957
            GObject.source_remove(self.disable_initiator_tag)
1018
958
            self.disable_initiator_tag = None
1019
959
        if getattr(self, "enabled", False):
1020
 
            self.disable_initiator_tag = GLib.timeout_add(
 
960
            self.disable_initiator_tag = GObject.timeout_add(
1021
961
                int(timeout.total_seconds() * 1000), self.disable)
1022
962
            self.expires = datetime.datetime.utcnow() + timeout
1023
963
    
1078
1018
                args = (pipe[1], subprocess.call, command),
1079
1019
                kwargs = popen_args)
1080
1020
            self.checker.start()
1081
 
            self.checker_callback_tag = GLib.io_add_watch(
1082
 
                pipe[0].fileno(), GLib.IO_IN,
 
1021
            self.checker_callback_tag = GObject.io_add_watch(
 
1022
                pipe[0].fileno(), GObject.IO_IN,
1083
1023
                self.checker_callback, pipe[0], command)
1084
 
        # Re-run this periodically if run by GLib.timeout_add
 
1024
        # Re-run this periodically if run by GObject.timeout_add
1085
1025
        return True
1086
1026
    
1087
1027
    def stop_checker(self):
1088
1028
        """Force the checker process, if any, to stop."""
1089
1029
        if self.checker_callback_tag:
1090
 
            GLib.source_remove(self.checker_callback_tag)
 
1030
            GObject.source_remove(self.checker_callback_tag)
1091
1031
            self.checker_callback_tag = None
1092
1032
        if getattr(self, "checker", None) is None:
1093
1033
            return
1567
1507
                interface_names.add(alt_interface)
1568
1508
                # Is this a D-Bus signal?
1569
1509
                if getattr(attribute, "_dbus_is_signal", False):
1570
 
                    # Extract the original non-method undecorated
1571
 
                    # function by black magic
1572
1510
                    if sys.version_info.major == 2:
 
1511
                        # Extract the original non-method undecorated
 
1512
                        # function by black magic
1573
1513
                        nonmethod_func = (dict(
1574
1514
                            zip(attribute.func_code.co_freevars,
1575
1515
                                attribute.__closure__))
1576
1516
                                          ["func"].cell_contents)
1577
1517
                    else:
1578
 
                        nonmethod_func = (dict(
1579
 
                            zip(attribute.__code__.co_freevars,
1580
 
                                attribute.__closure__))
1581
 
                                          ["func"].cell_contents)
 
1518
                        nonmethod_func = attribute
1582
1519
                    # Create a new, but exactly alike, function
1583
1520
                    # object, and decorate it to be a new D-Bus signal
1584
1521
                    # with the alternate D-Bus interface name
1585
 
                    new_function = copy_function(nonmethod_func)
 
1522
                    if sys.version_info.major == 2:
 
1523
                        new_function = types.FunctionType(
 
1524
                            nonmethod_func.func_code,
 
1525
                            nonmethod_func.func_globals,
 
1526
                            nonmethod_func.func_name,
 
1527
                            nonmethod_func.func_defaults,
 
1528
                            nonmethod_func.func_closure)
 
1529
                    else:
 
1530
                        new_function = types.FunctionType(
 
1531
                            nonmethod_func.__code__,
 
1532
                            nonmethod_func.__globals__,
 
1533
                            nonmethod_func.__name__,
 
1534
                            nonmethod_func.__defaults__,
 
1535
                            nonmethod_func.__closure__)
1586
1536
                    new_function = (dbus.service.signal(
1587
1537
                        alt_interface,
1588
1538
                        attribute._dbus_signature)(new_function))
1627
1577
                            alt_interface,
1628
1578
                            attribute._dbus_in_signature,
1629
1579
                            attribute._dbus_out_signature)
1630
 
                        (copy_function(attribute)))
 
1580
                        (types.FunctionType(attribute.func_code,
 
1581
                                            attribute.func_globals,
 
1582
                                            attribute.func_name,
 
1583
                                            attribute.func_defaults,
 
1584
                                            attribute.func_closure)))
1631
1585
                    # Copy annotations, if any
1632
1586
                    try:
1633
1587
                        attr[attrname]._dbus_annotations = dict(
1645
1599
                        attribute._dbus_access,
1646
1600
                        attribute._dbus_get_args_options
1647
1601
                        ["byte_arrays"])
1648
 
                                      (copy_function(attribute)))
 
1602
                                      (types.FunctionType(
 
1603
                                          attribute.func_code,
 
1604
                                          attribute.func_globals,
 
1605
                                          attribute.func_name,
 
1606
                                          attribute.func_defaults,
 
1607
                                          attribute.func_closure)))
1649
1608
                    # Copy annotations, if any
1650
1609
                    try:
1651
1610
                        attr[attrname]._dbus_annotations = dict(
1660
1619
                    # to the class.
1661
1620
                    attr[attrname] = (
1662
1621
                        dbus_interface_annotations(alt_interface)
1663
 
                        (copy_function(attribute)))
 
1622
                        (types.FunctionType(attribute.func_code,
 
1623
                                            attribute.func_globals,
 
1624
                                            attribute.func_name,
 
1625
                                            attribute.func_defaults,
 
1626
                                            attribute.func_closure)))
1664
1627
            if deprecate:
1665
1628
                # Deprecate all alternate interfaces
1666
1629
                iname="_AlternateDBusNames_interface_annotation{}"
1679
1642
            if interface_names:
1680
1643
                # Replace the class with a new subclass of it with
1681
1644
                # methods, signals, etc. as created above.
1682
 
                if sys.version_info.major == 2:
1683
 
                    cls = type(b"{}Alternate".format(cls.__name__),
1684
 
                               (cls, ), attr)
1685
 
                else:
1686
 
                    cls = type("{}Alternate".format(cls.__name__),
1687
 
                               (cls, ), attr)
 
1645
                cls = type(b"{}Alternate".format(cls.__name__),
 
1646
                           (cls, ), attr)
1688
1647
        return cls
1689
1648
    
1690
1649
    return wrapper
1848
1807
    
1849
1808
    def approve(self, value=True):
1850
1809
        self.approved = value
1851
 
        GLib.timeout_add(int(self.approval_duration.total_seconds()
1852
 
                             * 1000), self._reset_approved)
 
1810
        GObject.timeout_add(int(self.approval_duration.total_seconds()
 
1811
                                * 1000), self._reset_approved)
1853
1812
        self.send_changedstate()
1854
1813
    
1855
1814
    ## D-Bus methods, signals & properties
2065
2024
                if (getattr(self, "disable_initiator_tag", None)
2066
2025
                    is None):
2067
2026
                    return
2068
 
                GLib.source_remove(self.disable_initiator_tag)
2069
 
                self.disable_initiator_tag = GLib.timeout_add(
 
2027
                GObject.source_remove(self.disable_initiator_tag)
 
2028
                self.disable_initiator_tag = GObject.timeout_add(
2070
2029
                    int((self.expires - now).total_seconds() * 1000),
2071
2030
                    self.disable)
2072
2031
    
2092
2051
            return
2093
2052
        if self.enabled:
2094
2053
            # Reschedule checker run
2095
 
            GLib.source_remove(self.checker_initiator_tag)
2096
 
            self.checker_initiator_tag = GLib.timeout_add(
 
2054
            GObject.source_remove(self.checker_initiator_tag)
 
2055
            self.checker_initiator_tag = GObject.timeout_add(
2097
2056
                value, self.start_checker)
2098
2057
            self.start_checker() # Start one now, too
2099
2058
    
2191
2150
            priority = self.server.gnutls_priority
2192
2151
            if priority is None:
2193
2152
                priority = "NORMAL"
2194
 
            gnutls.priority_set_direct(session._c_object,
2195
 
                                       priority.encode("utf-8"),
 
2153
            gnutls.priority_set_direct(session._c_object, priority,
2196
2154
                                       None)
2197
2155
            
2198
2156
            # Start communication using the Mandos protocol
2453
2411
        """This overrides the normal server_bind() function
2454
2412
        to bind to an interface if one was specified, and also NOT to
2455
2413
        bind to an address or port if they were not specified."""
2456
 
        global SO_BINDTODEVICE
2457
2414
        if self.interface is not None:
2458
2415
            if SO_BINDTODEVICE is None:
2459
 
                # Fall back to a hard-coded value which seems to be
2460
 
                # common enough.
2461
 
                logger.warning("SO_BINDTODEVICE not found, trying 25")
2462
 
                SO_BINDTODEVICE = 25
2463
 
            try:
2464
 
                self.socket.setsockopt(
2465
 
                    socket.SOL_SOCKET, SO_BINDTODEVICE,
2466
 
                    (self.interface + "\0").encode("utf-8"))
2467
 
            except socket.error as error:
2468
 
                if error.errno == errno.EPERM:
2469
 
                    logger.error("No permission to bind to"
2470
 
                                 " interface %s", self.interface)
2471
 
                elif error.errno == errno.ENOPROTOOPT:
2472
 
                    logger.error("SO_BINDTODEVICE not available;"
2473
 
                                 " cannot bind to interface %s",
2474
 
                                 self.interface)
2475
 
                elif error.errno == errno.ENODEV:
2476
 
                    logger.error("Interface %s does not exist,"
2477
 
                                 " cannot bind", self.interface)
2478
 
                else:
2479
 
                    raise
 
2416
                logger.error("SO_BINDTODEVICE does not exist;"
 
2417
                             " cannot bind to interface %s",
 
2418
                             self.interface)
 
2419
            else:
 
2420
                try:
 
2421
                    self.socket.setsockopt(
 
2422
                        socket.SOL_SOCKET, SO_BINDTODEVICE,
 
2423
                        (self.interface + "\0").encode("utf-8"))
 
2424
                except socket.error as error:
 
2425
                    if error.errno == errno.EPERM:
 
2426
                        logger.error("No permission to bind to"
 
2427
                                     " interface %s", self.interface)
 
2428
                    elif error.errno == errno.ENOPROTOOPT:
 
2429
                        logger.error("SO_BINDTODEVICE not available;"
 
2430
                                     " cannot bind to interface %s",
 
2431
                                     self.interface)
 
2432
                    elif error.errno == errno.ENODEV:
 
2433
                        logger.error("Interface %s does not exist,"
 
2434
                                     " cannot bind", self.interface)
 
2435
                    else:
 
2436
                        raise
2480
2437
        # Only bind(2) the socket if we really need to.
2481
2438
        if self.server_address[0] or self.server_address[1]:
2482
2439
            if not self.server_address[0]:
2505
2462
        gnutls_priority GnuTLS priority string
2506
2463
        use_dbus:       Boolean; to emit D-Bus signals or not
2507
2464
    
2508
 
    Assumes a GLib.MainLoop event loop.
 
2465
    Assumes a GObject.MainLoop event loop.
2509
2466
    """
2510
2467
    
2511
2468
    def __init__(self, server_address, RequestHandlerClass,
2536
2493
    
2537
2494
    def add_pipe(self, parent_pipe, proc):
2538
2495
        # Call "handle_ipc" for both data and EOF events
2539
 
        GLib.io_add_watch(
 
2496
        GObject.io_add_watch(
2540
2497
            parent_pipe.fileno(),
2541
 
            GLib.IO_IN | GLib.IO_HUP,
 
2498
            GObject.IO_IN | GObject.IO_HUP,
2542
2499
            functools.partial(self.handle_ipc,
2543
2500
                              parent_pipe = parent_pipe,
2544
2501
                              proc = proc))
2548
2505
                   proc = None,
2549
2506
                   client_object=None):
2550
2507
        # error, or the other end of multiprocessing.Pipe has closed
2551
 
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2508
        if condition & (GObject.IO_ERR | GObject.IO_HUP):
2552
2509
            # Wait for other process to exit
2553
2510
            proc.join()
2554
2511
            return False
2561
2518
            fpr = request[1]
2562
2519
            address = request[2]
2563
2520
            
2564
 
            for c in self.clients.values():
 
2521
            for c in self.clients.itervalues():
2565
2522
                if c.fingerprint == fpr:
2566
2523
                    client = c
2567
2524
                    break
2575
2532
                parent_pipe.send(False)
2576
2533
                return False
2577
2534
            
2578
 
            GLib.io_add_watch(
 
2535
            GObject.io_add_watch(
2579
2536
                parent_pipe.fileno(),
2580
 
                GLib.IO_IN | GLib.IO_HUP,
 
2537
                GObject.IO_IN | GObject.IO_HUP,
2581
2538
                functools.partial(self.handle_ipc,
2582
2539
                                  parent_pipe = parent_pipe,
2583
2540
                                  proc = proc,
2980
2937
    try:
2981
2938
        os.setgid(gid)
2982
2939
        os.setuid(uid)
2983
 
        if debug:
2984
 
            logger.debug("Did setuid/setgid to {}:{}".format(uid,
2985
 
                                                             gid))
2986
2940
    except OSError as error:
2987
 
        logger.warning("Failed to setuid/setgid to {}:{}: {}"
2988
 
                       .format(uid, gid, os.strerror(error.errno)))
2989
2941
        if error.errno != errno.EPERM:
2990
2942
            raise
2991
2943
    
3013
2965
        # Close all input and output, do double fork, etc.
3014
2966
        daemon()
3015
2967
    
3016
 
    # multiprocessing will use threads, so before we use GLib we need
3017
 
    # to inform GLib that threads will be used.
3018
 
    GLib.threads_init()
 
2968
    # multiprocessing will use threads, so before we use GObject we
 
2969
    # need to inform GObject that threads will be used.
 
2970
    GObject.threads_init()
3019
2971
    
3020
2972
    global main_loop
3021
2973
    # From the Avahi example code
3022
2974
    DBusGMainLoop(set_as_default=True)
3023
 
    main_loop = GLib.MainLoop()
 
2975
    main_loop = GObject.MainLoop()
3024
2976
    bus = dbus.SystemBus()
3025
2977
    # End of Avahi example code
3026
2978
    if use_dbus:
3070
3022
    if server_settings["restore"]:
3071
3023
        try:
3072
3024
            with open(stored_state_path, "rb") as stored_state:
3073
 
                if sys.version_info.major == 2:                
3074
 
                    clients_data, old_client_settings = pickle.load(
3075
 
                        stored_state)
3076
 
                else:
3077
 
                    bytes_clients_data, bytes_old_client_settings = (
3078
 
                        pickle.load(stored_state, encoding = "bytes"))
3079
 
                    ### Fix bytes to strings
3080
 
                    ## clients_data
3081
 
                    # .keys()
3082
 
                    clients_data = { (key.decode("utf-8")
3083
 
                                      if isinstance(key, bytes)
3084
 
                                      else key): value
3085
 
                                     for key, value in
3086
 
                                     bytes_clients_data.items() }
3087
 
                    del bytes_clients_data
3088
 
                    for key in clients_data:
3089
 
                        value = { (k.decode("utf-8")
3090
 
                                   if isinstance(k, bytes) else k): v
3091
 
                                  for k, v in
3092
 
                                  clients_data[key].items() }
3093
 
                        clients_data[key] = value
3094
 
                        # .client_structure
3095
 
                        value["client_structure"] = [
3096
 
                            (s.decode("utf-8")
3097
 
                             if isinstance(s, bytes)
3098
 
                             else s) for s in
3099
 
                            value["client_structure"] ]
3100
 
                        # .name & .host
3101
 
                        for k in ("name", "host"):
3102
 
                            if isinstance(value[k], bytes):
3103
 
                                value[k] = value[k].decode("utf-8")
3104
 
                    ## old_client_settings
3105
 
                    # .keys()
3106
 
                    old_client_settings = {
3107
 
                        (key.decode("utf-8")
3108
 
                         if isinstance(key, bytes)
3109
 
                         else key): value
3110
 
                        for key, value in
3111
 
                        bytes_old_client_settings.items() }
3112
 
                    del bytes_old_client_settings
3113
 
                    # .host
3114
 
                    for value in old_client_settings.values():
3115
 
                        if isinstance(value["host"], bytes):
3116
 
                            value["host"] = (value["host"]
3117
 
                                             .decode("utf-8"))
 
3025
                clients_data, old_client_settings = pickle.load(
 
3026
                    stored_state)
3118
3027
            os.remove(stored_state_path)
3119
3028
        except IOError as e:
3120
3029
            if e.errno == errno.ENOENT:
3221
3130
        del pidfile
3222
3131
        del pidfilename
3223
3132
    
3224
 
    for termsig in (signal.SIGHUP, signal.SIGTERM):
3225
 
        GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig,
3226
 
                             lambda: main_loop.quit() and False)
 
3133
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
 
3134
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
3227
3135
    
3228
3136
    if use_dbus:
3229
3137
        
3260
3168
            def GetAllClients(self):
3261
3169
                "D-Bus method"
3262
3170
                return dbus.Array(c.dbus_object_path for c in
3263
 
                                  tcp_server.clients.values())
 
3171
                                  tcp_server.clients.itervalues())
3264
3172
            
3265
3173
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3266
3174
                               "true"})
3271
3179
                return dbus.Dictionary(
3272
3180
                    { c.dbus_object_path: c.GetAll(
3273
3181
                        "se.recompile.Mandos.Client")
3274
 
                      for c in tcp_server.clients.values() },
 
3182
                      for c in tcp_server.clients.itervalues() },
3275
3183
                    signature="oa{sv}")
3276
3184
            
3277
3185
            @dbus.service.method(_interface, in_signature="o")
3278
3186
            def RemoveClient(self, object_path):
3279
3187
                "D-Bus method"
3280
 
                for c in tcp_server.clients.values():
 
3188
                for c in tcp_server.clients.itervalues():
3281
3189
                    if c.dbus_object_path == object_path:
3282
3190
                        del tcp_server.clients[c.name]
3283
3191
                        c.remove_from_connection()
3328
3236
        
3329
3237
        mandos_dbus_service = MandosDBusService()
3330
3238
    
3331
 
    # Save modules to variables to exempt the modules from being
3332
 
    # unloaded before the function registered with atexit() is run.
3333
 
    mp = multiprocessing
3334
 
    wn = wnull
3335
3239
    def cleanup():
3336
3240
        "Cleanup function; run on exit"
3337
3241
        if zeroconf:
3338
3242
            service.cleanup()
3339
3243
        
3340
 
        mp.active_children()
3341
 
        wn.close()
 
3244
        multiprocessing.active_children()
 
3245
        wnull.close()
3342
3246
        if not (tcp_server.clients or client_settings):
3343
3247
            return
3344
3248
        
3347
3251
        # removed/edited, old secret will thus be unrecovable.
3348
3252
        clients = {}
3349
3253
        with PGPEngine() as pgp:
3350
 
            for client in tcp_server.clients.values():
 
3254
            for client in tcp_server.clients.itervalues():
3351
3255
                key = client_settings[client.name]["secret"]
3352
3256
                client.encrypted_secret = pgp.encrypt(client.secret,
3353
3257
                                                      key)
3377
3281
                    prefix='clients-',
3378
3282
                    dir=os.path.dirname(stored_state_path),
3379
3283
                    delete=False) as stored_state:
3380
 
                pickle.dump((clients, client_settings), stored_state,
3381
 
                            protocol = 2)
 
3284
                pickle.dump((clients, client_settings), stored_state)
3382
3285
                tempname = stored_state.name
3383
3286
            os.rename(tempname, stored_state_path)
3384
3287
        except (IOError, OSError) as e:
3409
3312
    
3410
3313
    atexit.register(cleanup)
3411
3314
    
3412
 
    for client in tcp_server.clients.values():
 
3315
    for client in tcp_server.clients.itervalues():
3413
3316
        if use_dbus:
3414
3317
            # Emit D-Bus signal for adding
3415
3318
            mandos_dbus_service.client_added_signal(client)
3444
3347
                sys.exit(1)
3445
3348
            # End of Avahi example code
3446
3349
        
3447
 
        GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN,
3448
 
                          lambda *args, **kwargs:
3449
 
                          (tcp_server.handle_request
3450
 
                           (*args[2:], **kwargs) or True))
 
3350
        GObject.io_add_watch(tcp_server.fileno(), GObject.IO_IN,
 
3351
                             lambda *args, **kwargs:
 
3352
                             (tcp_server.handle_request
 
3353
                              (*args[2:], **kwargs) or True))
3451
3354
        
3452
3355
        logger.debug("Starting main loop")
3453
3356
        main_loop.run()