/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: 2016-06-21 19:47:08 UTC
  • Revision ID: teddy@recompile.se-20160621194708-bmsd15fu32llgb4s
Fix typo in README.Debian for mandos-client package.

* debian/mandos.README.Debian: Replace "it it" with "it is", and make
  lintian warning tag "spelling-error-in-readme-debian" go away.

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