/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-06-28 18:52:00 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 350.
  • Revision ID: teddy@recompile.se-20160628185200-vv6fikpt18r977x3
mandos-ctl: Include "Expires" and "LastCheckerStatus" properties

* mandos-ctl (tablewords): Add "Expires" and "LastCheckerStatus".
  (main/keywords): - '' -

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.10"
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
2411
2453
        """This overrides the normal server_bind() function
2412
2454
        to bind to an interface if one was specified, and also NOT to
2413
2455
        bind to an address or port if they were not specified."""
 
2456
        global SO_BINDTODEVICE
2414
2457
        if self.interface is not None:
2415
2458
            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
 
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
2437
2480
        # Only bind(2) the socket if we really need to.
2438
2481
        if self.server_address[0] or self.server_address[1]:
2439
2482
            if not self.server_address[0]:
2462
2505
        gnutls_priority GnuTLS priority string
2463
2506
        use_dbus:       Boolean; to emit D-Bus signals or not
2464
2507
    
2465
 
    Assumes a gobject.MainLoop event loop.
 
2508
    Assumes a GLib.MainLoop event loop.
2466
2509
    """
2467
2510
    
2468
2511
    def __init__(self, server_address, RequestHandlerClass,
2493
2536
    
2494
2537
    def add_pipe(self, parent_pipe, proc):
2495
2538
        # Call "handle_ipc" for both data and EOF events
2496
 
        gobject.io_add_watch(
 
2539
        GLib.io_add_watch(
2497
2540
            parent_pipe.fileno(),
2498
 
            gobject.IO_IN | gobject.IO_HUP,
 
2541
            GLib.IO_IN | GLib.IO_HUP,
2499
2542
            functools.partial(self.handle_ipc,
2500
2543
                              parent_pipe = parent_pipe,
2501
2544
                              proc = proc))
2505
2548
                   proc = None,
2506
2549
                   client_object=None):
2507
2550
        # error, or the other end of multiprocessing.Pipe has closed
2508
 
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
 
2551
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
2509
2552
            # Wait for other process to exit
2510
2553
            proc.join()
2511
2554
            return False
2518
2561
            fpr = request[1]
2519
2562
            address = request[2]
2520
2563
            
2521
 
            for c in self.clients.itervalues():
 
2564
            for c in self.clients.values():
2522
2565
                if c.fingerprint == fpr:
2523
2566
                    client = c
2524
2567
                    break
2532
2575
                parent_pipe.send(False)
2533
2576
                return False
2534
2577
            
2535
 
            gobject.io_add_watch(
 
2578
            GLib.io_add_watch(
2536
2579
                parent_pipe.fileno(),
2537
 
                gobject.IO_IN | gobject.IO_HUP,
 
2580
                GLib.IO_IN | GLib.IO_HUP,
2538
2581
                functools.partial(self.handle_ipc,
2539
2582
                                  parent_pipe = parent_pipe,
2540
2583
                                  proc = proc,
2922
2965
            logger.error("Could not open file %r", pidfilename,
2923
2966
                         exc_info=e)
2924
2967
    
2925
 
    for name in ("_mandos", "mandos", "nobody"):
 
2968
    for name, group in (("_mandos", "_mandos"),
 
2969
                        ("mandos", "mandos"),
 
2970
                        ("nobody", "nogroup")):
2926
2971
        try:
2927
2972
            uid = pwd.getpwnam(name).pw_uid
2928
 
            gid = pwd.getpwnam(name).pw_gid
 
2973
            gid = pwd.getpwnam(group).pw_gid
2929
2974
            break
2930
2975
        except KeyError:
2931
2976
            continue
2935
2980
    try:
2936
2981
        os.setgid(gid)
2937
2982
        os.setuid(uid)
 
2983
        if debug:
 
2984
            logger.debug("Did setuid/setgid to {}:{}".format(uid,
 
2985
                                                             gid))
2938
2986
    except OSError as error:
 
2987
        logger.warning("Failed to setuid/setgid to {}:{}: {}"
 
2988
                       .format(uid, gid, os.strerror(error.errno)))
2939
2989
        if error.errno != errno.EPERM:
2940
2990
            raise
2941
2991
    
2963
3013
        # Close all input and output, do double fork, etc.
2964
3014
        daemon()
2965
3015
    
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()
 
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()
2969
3019
    
2970
3020
    global main_loop
2971
3021
    # From the Avahi example code
2972
3022
    DBusGMainLoop(set_as_default=True)
2973
 
    main_loop = gobject.MainLoop()
 
3023
    main_loop = GLib.MainLoop()
2974
3024
    bus = dbus.SystemBus()
2975
3025
    # End of Avahi example code
2976
3026
    if use_dbus:
3020
3070
    if server_settings["restore"]:
3021
3071
        try:
3022
3072
            with open(stored_state_path, "rb") as stored_state:
3023
 
                clients_data, old_client_settings = pickle.load(
3024
 
                    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
3118
            os.remove(stored_state_path)
3026
3119
        except IOError as e:
3027
3120
            if e.errno == errno.ENOENT:
3128
3221
        del pidfile
3129
3222
        del pidfilename
3130
3223
    
3131
 
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
3132
 
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
 
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
3227
    
3134
3228
    if use_dbus:
3135
3229
        
3166
3260
            def GetAllClients(self):
3167
3261
                "D-Bus method"
3168
3262
                return dbus.Array(c.dbus_object_path for c in
3169
 
                                  tcp_server.clients.itervalues())
 
3263
                                  tcp_server.clients.values())
3170
3264
            
3171
3265
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3172
3266
                               "true"})
3177
3271
                return dbus.Dictionary(
3178
3272
                    { c.dbus_object_path: c.GetAll(
3179
3273
                        "se.recompile.Mandos.Client")
3180
 
                      for c in tcp_server.clients.itervalues() },
 
3274
                      for c in tcp_server.clients.values() },
3181
3275
                    signature="oa{sv}")
3182
3276
            
3183
3277
            @dbus.service.method(_interface, in_signature="o")
3184
3278
            def RemoveClient(self, object_path):
3185
3279
                "D-Bus method"
3186
 
                for c in tcp_server.clients.itervalues():
 
3280
                for c in tcp_server.clients.values():
3187
3281
                    if c.dbus_object_path == object_path:
3188
3282
                        del tcp_server.clients[c.name]
3189
3283
                        c.remove_from_connection()
3234
3328
        
3235
3329
        mandos_dbus_service = MandosDBusService()
3236
3330
    
 
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
3237
3335
    def cleanup():
3238
3336
        "Cleanup function; run on exit"
3239
3337
        if zeroconf:
3240
3338
            service.cleanup()
3241
3339
        
3242
 
        multiprocessing.active_children()
3243
 
        wnull.close()
 
3340
        mp.active_children()
 
3341
        wn.close()
3244
3342
        if not (tcp_server.clients or client_settings):
3245
3343
            return
3246
3344
        
3249
3347
        # removed/edited, old secret will thus be unrecovable.
3250
3348
        clients = {}
3251
3349
        with PGPEngine() as pgp:
3252
 
            for client in tcp_server.clients.itervalues():
 
3350
            for client in tcp_server.clients.values():
3253
3351
                key = client_settings[client.name]["secret"]
3254
3352
                client.encrypted_secret = pgp.encrypt(client.secret,
3255
3353
                                                      key)
3279
3377
                    prefix='clients-',
3280
3378
                    dir=os.path.dirname(stored_state_path),
3281
3379
                    delete=False) as stored_state:
3282
 
                pickle.dump((clients, client_settings), stored_state)
 
3380
                pickle.dump((clients, client_settings), stored_state,
 
3381
                            protocol = 2)
3283
3382
                tempname = stored_state.name
3284
3383
            os.rename(tempname, stored_state_path)
3285
3384
        except (IOError, OSError) as e:
3310
3409
    
3311
3410
    atexit.register(cleanup)
3312
3411
    
3313
 
    for client in tcp_server.clients.itervalues():
 
3412
    for client in tcp_server.clients.values():
3314
3413
        if use_dbus:
3315
3414
            # Emit D-Bus signal for adding
3316
3415
            mandos_dbus_service.client_added_signal(client)
3345
3444
                sys.exit(1)
3346
3445
            # End of Avahi example code
3347
3446
        
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))
 
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))
3352
3451
        
3353
3452
        logger.debug("Starting main loop")
3354
3453
        main_loop.run()