/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-02-28 10:59:18 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 331.
  • Revision ID: teddy@recompile.se-20160228105918-tb8pt2p5j0tkcls3
Handle GnuTLS errors and partial sends in gnutls "module".

* mandos (GnuTLS.E_INTERRUPTED, GnuTLS.E_AGAIN): New.
  (GnuTLS.Error): Set error code as "code" attribute.
  (GnuTLS.ClientSession.send): Handle partial sends with a loop.
  (GnuTLS._retry_on_error): New function.
  (GnuTLS.record_send, GnuTLS.handshake, GnuTLS.bye): Set "errcheck"
                                                      attribute to
                                                    "_retry_on_error".
  (ClientHandler.handle): Remove loop for handling partial sends;
                          GnuTLS.ClientSession.send() will do that.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
 
1
#!/usr/bin/python2.7
2
2
# -*- mode: python; coding: utf-8 -*-
3
3
4
4
# Mandos server - give out binary blobs to connecting clients.
11
11
# "AvahiService" class, and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2008-2016 Teddy Hogeborn
15
 
# Copyright © 2008-2016 Björn Påhlsson
 
14
# Copyright © 2008-2015 Teddy Hogeborn
 
15
# Copyright © 2008-2015 Björn Påhlsson
16
16
17
17
# This program is free software: you can redistribute it and/or modify
18
18
# it under the terms of the GNU General Public License as published by
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
    import gobject
 
81
except ImportError:
 
82
    from gi.repository 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.8"
 
101
version = "1.7.1"
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
    
183
151
    
184
152
    def __init__(self):
185
153
        self.tempdir = tempfile.mkdtemp(prefix="mandos-")
186
 
        self.gpg = "gpg"
187
 
        try:
188
 
            output = subprocess.check_output(["gpgconf"])
189
 
            for line in output.splitlines():
190
 
                name, text, path = line.split(b":")
191
 
                if name == "gpg":
192
 
                    self.gpg = path
193
 
                    break
194
 
        except OSError as e:
195
 
            if e.errno != errno.ENOENT:
196
 
                raise
197
154
        self.gnupgargs = ['--batch',
198
 
                          '--homedir', self.tempdir,
 
155
                          '--home', self.tempdir,
199
156
                          '--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")
 
157
                          '--quiet',
 
158
                          '--no-use-agent']
204
159
    
205
160
    def __enter__(self):
206
161
        return self
242
197
                dir=self.tempdir) as passfile:
243
198
            passfile.write(passphrase)
244
199
            passfile.flush()
245
 
            proc = subprocess.Popen([self.gpg, '--symmetric',
 
200
            proc = subprocess.Popen(['gpg', '--symmetric',
246
201
                                     '--passphrase-file',
247
202
                                     passfile.name]
248
203
                                    + self.gnupgargs,
260
215
                dir = self.tempdir) as passfile:
261
216
            passfile.write(passphrase)
262
217
            passfile.flush()
263
 
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
218
            proc = subprocess.Popen(['gpg', '--decrypt',
264
219
                                     '--passphrase-file',
265
220
                                     passfile.name]
266
221
                                    + self.gnupgargs,
272
227
            raise PGPError(err)
273
228
        return decrypted_plaintext
274
229
 
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
230
 
300
231
class AvahiError(Exception):
301
232
    def __init__(self, value, *args, **kwargs):
505
436
    
506
437
    _library = ctypes.cdll.LoadLibrary(
507
438
        ctypes.util.find_library("gnutls"))
508
 
    _need_version = b"3.3.0"
 
439
    _need_version = "3.3.0"
509
440
    def __init__(self):
510
441
        # Need to use class name "GnuTLS" here, since this method is
511
442
        # called before the assignment to the "gnutls" global variable
545
476
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
546
477
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
547
478
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
548
 
    credentials_type_t = ctypes.c_int
 
479
    credentials_type_t = ctypes.c_int # 
549
480
    transport_ptr_t = ctypes.c_void_p
550
481
    close_request_t = ctypes.c_int
551
482
    
773
704
    checker:    subprocess.Popen(); a running checker process used
774
705
                                    to see if the client lives.
775
706
                                    'None' if no process is running.
776
 
    checker_callback_tag: a GLib event source tag, or None
 
707
    checker_callback_tag: a gobject event source tag, or None
777
708
    checker_command: string; External command which is run to check
778
709
                     if client lives.  %() expansions are done at
779
710
                     runtime with vars(self) as dict, so that for
780
711
                     instance %(name)s can be used in the command.
781
 
    checker_initiator_tag: a GLib event source tag, or None
 
712
    checker_initiator_tag: a gobject event source tag, or None
782
713
    created:    datetime.datetime(); (UTC) object creation
783
714
    client_structure: Object describing what attributes a client has
784
715
                      and is used for storing the client at exit
785
716
    current_checker_command: string; current running checker_command
786
 
    disable_initiator_tag: a GLib event source tag, or None
 
717
    disable_initiator_tag: a gobject event source tag, or None
787
718
    enabled:    bool()
788
719
    fingerprint: string (40 or 32 hexadecimal digits); used to
789
720
                 uniquely identify the client
852
783
            client["fingerprint"] = (section["fingerprint"].upper()
853
784
                                     .replace(" ", ""))
854
785
            if "secret" in section:
855
 
                client["secret"] = codecs.decode(section["secret"]
856
 
                                                 .encode("utf-8"),
857
 
                                                 "base64")
 
786
                client["secret"] = section["secret"].decode("base64")
858
787
            elif "secfile" in section:
859
788
                with open(os.path.expanduser(os.path.expandvars
860
789
                                             (section["secfile"])),
913
842
        self.changedstate = multiprocessing_manager.Condition(
914
843
            multiprocessing_manager.Lock())
915
844
        self.client_structure = [attr
916
 
                                 for attr in self.__dict__.keys()
 
845
                                 for attr in self.__dict__.iterkeys()
917
846
                                 if not attr.startswith("_")]
918
847
        self.client_structure.append("client_structure")
919
848
        
945
874
        if not quiet:
946
875
            logger.info("Disabling client %s", self.name)
947
876
        if getattr(self, "disable_initiator_tag", None) is not None:
948
 
            GLib.source_remove(self.disable_initiator_tag)
 
877
            gobject.source_remove(self.disable_initiator_tag)
949
878
            self.disable_initiator_tag = None
950
879
        self.expires = None
951
880
        if getattr(self, "checker_initiator_tag", None) is not None:
952
 
            GLib.source_remove(self.checker_initiator_tag)
 
881
            gobject.source_remove(self.checker_initiator_tag)
953
882
            self.checker_initiator_tag = None
954
883
        self.stop_checker()
955
884
        self.enabled = False
956
885
        if not quiet:
957
886
            self.send_changedstate()
958
 
        # Do not run this again if called by a GLib.timeout_add
 
887
        # Do not run this again if called by a gobject.timeout_add
959
888
        return False
960
889
    
961
890
    def __del__(self):
965
894
        # Schedule a new checker to be started an 'interval' from now,
966
895
        # and every interval from then on.
967
896
        if self.checker_initiator_tag is not None:
968
 
            GLib.source_remove(self.checker_initiator_tag)
969
 
        self.checker_initiator_tag = GLib.timeout_add(
 
897
            gobject.source_remove(self.checker_initiator_tag)
 
898
        self.checker_initiator_tag = gobject.timeout_add(
970
899
            int(self.interval.total_seconds() * 1000),
971
900
            self.start_checker)
972
901
        # Schedule a disable() when 'timeout' has passed
973
902
        if self.disable_initiator_tag is not None:
974
 
            GLib.source_remove(self.disable_initiator_tag)
975
 
        self.disable_initiator_tag = GLib.timeout_add(
 
903
            gobject.source_remove(self.disable_initiator_tag)
 
904
        self.disable_initiator_tag = gobject.timeout_add(
976
905
            int(self.timeout.total_seconds() * 1000), self.disable)
977
906
        # Also start a new checker *right now*.
978
907
        self.start_checker()
1014
943
        if timeout is None:
1015
944
            timeout = self.timeout
1016
945
        if self.disable_initiator_tag is not None:
1017
 
            GLib.source_remove(self.disable_initiator_tag)
 
946
            gobject.source_remove(self.disable_initiator_tag)
1018
947
            self.disable_initiator_tag = None
1019
948
        if getattr(self, "enabled", False):
1020
 
            self.disable_initiator_tag = GLib.timeout_add(
 
949
            self.disable_initiator_tag = gobject.timeout_add(
1021
950
                int(timeout.total_seconds() * 1000), self.disable)
1022
951
            self.expires = datetime.datetime.utcnow() + timeout
1023
952
    
1078
1007
                args = (pipe[1], subprocess.call, command),
1079
1008
                kwargs = popen_args)
1080
1009
            self.checker.start()
1081
 
            self.checker_callback_tag = GLib.io_add_watch(
1082
 
                pipe[0].fileno(), GLib.IO_IN,
 
1010
            self.checker_callback_tag = gobject.io_add_watch(
 
1011
                pipe[0].fileno(), gobject.IO_IN,
1083
1012
                self.checker_callback, pipe[0], command)
1084
 
        # Re-run this periodically if run by GLib.timeout_add
 
1013
        # Re-run this periodically if run by gobject.timeout_add
1085
1014
        return True
1086
1015
    
1087
1016
    def stop_checker(self):
1088
1017
        """Force the checker process, if any, to stop."""
1089
1018
        if self.checker_callback_tag:
1090
 
            GLib.source_remove(self.checker_callback_tag)
 
1019
            gobject.source_remove(self.checker_callback_tag)
1091
1020
            self.checker_callback_tag = None
1092
1021
        if getattr(self, "checker", None) is None:
1093
1022
            return
1567
1496
                interface_names.add(alt_interface)
1568
1497
                # Is this a D-Bus signal?
1569
1498
                if getattr(attribute, "_dbus_is_signal", False):
1570
 
                    # Extract the original non-method undecorated
1571
 
                    # function by black magic
1572
1499
                    if sys.version_info.major == 2:
 
1500
                        # Extract the original non-method undecorated
 
1501
                        # function by black magic
1573
1502
                        nonmethod_func = (dict(
1574
1503
                            zip(attribute.func_code.co_freevars,
1575
1504
                                attribute.__closure__))
1576
1505
                                          ["func"].cell_contents)
1577
1506
                    else:
1578
 
                        nonmethod_func = (dict(
1579
 
                            zip(attribute.__code__.co_freevars,
1580
 
                                attribute.__closure__))
1581
 
                                          ["func"].cell_contents)
 
1507
                        nonmethod_func = attribute
1582
1508
                    # Create a new, but exactly alike, function
1583
1509
                    # object, and decorate it to be a new D-Bus signal
1584
1510
                    # with the alternate D-Bus interface name
1585
 
                    new_function = copy_function(nonmethod_func)
 
1511
                    if sys.version_info.major == 2:
 
1512
                        new_function = types.FunctionType(
 
1513
                            nonmethod_func.func_code,
 
1514
                            nonmethod_func.func_globals,
 
1515
                            nonmethod_func.func_name,
 
1516
                            nonmethod_func.func_defaults,
 
1517
                            nonmethod_func.func_closure)
 
1518
                    else:
 
1519
                        new_function = types.FunctionType(
 
1520
                            nonmethod_func.__code__,
 
1521
                            nonmethod_func.__globals__,
 
1522
                            nonmethod_func.__name__,
 
1523
                            nonmethod_func.__defaults__,
 
1524
                            nonmethod_func.__closure__)
1586
1525
                    new_function = (dbus.service.signal(
1587
1526
                        alt_interface,
1588
1527
                        attribute._dbus_signature)(new_function))
1627
1566
                            alt_interface,
1628
1567
                            attribute._dbus_in_signature,
1629
1568
                            attribute._dbus_out_signature)
1630
 
                        (copy_function(attribute)))
 
1569
                        (types.FunctionType(attribute.func_code,
 
1570
                                            attribute.func_globals,
 
1571
                                            attribute.func_name,
 
1572
                                            attribute.func_defaults,
 
1573
                                            attribute.func_closure)))
1631
1574
                    # Copy annotations, if any
1632
1575
                    try:
1633
1576
                        attr[attrname]._dbus_annotations = dict(
1645
1588
                        attribute._dbus_access,
1646
1589
                        attribute._dbus_get_args_options
1647
1590
                        ["byte_arrays"])
1648
 
                                      (copy_function(attribute)))
 
1591
                                      (types.FunctionType(
 
1592
                                          attribute.func_code,
 
1593
                                          attribute.func_globals,
 
1594
                                          attribute.func_name,
 
1595
                                          attribute.func_defaults,
 
1596
                                          attribute.func_closure)))
1649
1597
                    # Copy annotations, if any
1650
1598
                    try:
1651
1599
                        attr[attrname]._dbus_annotations = dict(
1660
1608
                    # to the class.
1661
1609
                    attr[attrname] = (
1662
1610
                        dbus_interface_annotations(alt_interface)
1663
 
                        (copy_function(attribute)))
 
1611
                        (types.FunctionType(attribute.func_code,
 
1612
                                            attribute.func_globals,
 
1613
                                            attribute.func_name,
 
1614
                                            attribute.func_defaults,
 
1615
                                            attribute.func_closure)))
1664
1616
            if deprecate:
1665
1617
                # Deprecate all alternate interfaces
1666
1618
                iname="_AlternateDBusNames_interface_annotation{}"
1679
1631
            if interface_names:
1680
1632
                # Replace the class with a new subclass of it with
1681
1633
                # 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)
 
1634
                cls = type(b"{}Alternate".format(cls.__name__),
 
1635
                           (cls, ), attr)
1688
1636
        return cls
1689
1637
    
1690
1638
    return wrapper
1848
1796
    
1849
1797
    def approve(self, value=True):
1850
1798
        self.approved = value
1851
 
        GLib.timeout_add(int(self.approval_duration.total_seconds()
1852
 
                             * 1000), self._reset_approved)
 
1799
        gobject.timeout_add(int(self.approval_duration.total_seconds()
 
1800
                                * 1000), self._reset_approved)
1853
1801
        self.send_changedstate()
1854
1802
    
1855
1803
    ## D-Bus methods, signals & properties
2065
2013
                if (getattr(self, "disable_initiator_tag", None)
2066
2014
                    is None):
2067
2015
                    return
2068
 
                GLib.source_remove(self.disable_initiator_tag)
2069
 
                self.disable_initiator_tag = GLib.timeout_add(
 
2016
                gobject.source_remove(self.disable_initiator_tag)
 
2017
                self.disable_initiator_tag = gobject.timeout_add(
2070
2018
                    int((self.expires - now).total_seconds() * 1000),
2071
2019
                    self.disable)
2072
2020
    
2092
2040
            return
2093
2041
        if self.enabled:
2094
2042
            # Reschedule checker run
2095
 
            GLib.source_remove(self.checker_initiator_tag)
2096
 
            self.checker_initiator_tag = GLib.timeout_add(
 
2043
            gobject.source_remove(self.checker_initiator_tag)
 
2044
            self.checker_initiator_tag = gobject.timeout_add(
2097
2045
                value, self.start_checker)
2098
2046
            self.start_checker() # Start one now, too
2099
2047
    
2191
2139
            priority = self.server.gnutls_priority
2192
2140
            if priority is None:
2193
2141
                priority = "NORMAL"
2194
 
            gnutls.priority_set_direct(session._c_object,
2195
 
                                       priority.encode("utf-8"),
 
2142
            gnutls.priority_set_direct(session._c_object, priority,
2196
2143
                                       None)
2197
2144
            
2198
2145
            # Start communication using the Mandos protocol
2455
2402
        bind to an address or port if they were not specified."""
2456
2403
        if self.interface is not None:
2457
2404
            if SO_BINDTODEVICE is None:
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
 
2405
                logger.error("SO_BINDTODEVICE does not exist;"
 
2406
                             " cannot bind to interface %s",
 
2407
                             self.interface)
 
2408
            else:
 
2409
                try:
 
2410
                    self.socket.setsockopt(
 
2411
                        socket.SOL_SOCKET, SO_BINDTODEVICE,
 
2412
                        (self.interface + "\0").encode("utf-8"))
 
2413
                except socket.error as error:
 
2414
                    if error.errno == errno.EPERM:
 
2415
                        logger.error("No permission to bind to"
 
2416
                                     " interface %s", self.interface)
 
2417
                    elif error.errno == errno.ENOPROTOOPT:
 
2418
                        logger.error("SO_BINDTODEVICE not available;"
 
2419
                                     " cannot bind to interface %s",
 
2420
                                     self.interface)
 
2421
                    elif error.errno == errno.ENODEV:
 
2422
                        logger.error("Interface %s does not exist,"
 
2423
                                     " cannot bind", self.interface)
 
2424
                    else:
 
2425
                        raise
2479
2426
        # Only bind(2) the socket if we really need to.
2480
2427
        if self.server_address[0] or self.server_address[1]:
2481
2428
            if not self.server_address[0]:
2504
2451
        gnutls_priority GnuTLS priority string
2505
2452
        use_dbus:       Boolean; to emit D-Bus signals or not
2506
2453
    
2507
 
    Assumes a GLib.MainLoop event loop.
 
2454
    Assumes a gobject.MainLoop event loop.
2508
2455
    """
2509
2456
    
2510
2457
    def __init__(self, server_address, RequestHandlerClass,
2535
2482
    
2536
2483
    def add_pipe(self, parent_pipe, proc):
2537
2484
        # Call "handle_ipc" for both data and EOF events
2538
 
        GLib.io_add_watch(
 
2485
        gobject.io_add_watch(
2539
2486
            parent_pipe.fileno(),
2540
 
            GLib.IO_IN | GLib.IO_HUP,
 
2487
            gobject.IO_IN | gobject.IO_HUP,
2541
2488
            functools.partial(self.handle_ipc,
2542
2489
                              parent_pipe = parent_pipe,
2543
2490
                              proc = proc))
2547
2494
                   proc = None,
2548
2495
                   client_object=None):
2549
2496
        # error, or the other end of multiprocessing.Pipe has closed
2550
 
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2497
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
2551
2498
            # Wait for other process to exit
2552
2499
            proc.join()
2553
2500
            return False
2560
2507
            fpr = request[1]
2561
2508
            address = request[2]
2562
2509
            
2563
 
            for c in self.clients.values():
 
2510
            for c in self.clients.itervalues():
2564
2511
                if c.fingerprint == fpr:
2565
2512
                    client = c
2566
2513
                    break
2574
2521
                parent_pipe.send(False)
2575
2522
                return False
2576
2523
            
2577
 
            GLib.io_add_watch(
 
2524
            gobject.io_add_watch(
2578
2525
                parent_pipe.fileno(),
2579
 
                GLib.IO_IN | GLib.IO_HUP,
 
2526
                gobject.IO_IN | gobject.IO_HUP,
2580
2527
                functools.partial(self.handle_ipc,
2581
2528
                                  parent_pipe = parent_pipe,
2582
2529
                                  proc = proc,
2964
2911
            logger.error("Could not open file %r", pidfilename,
2965
2912
                         exc_info=e)
2966
2913
    
2967
 
    for name, group in (("_mandos", "_mandos"),
2968
 
                        ("mandos", "mandos"),
2969
 
                        ("nobody", "nogroup")):
 
2914
    for name in ("_mandos", "mandos", "nobody"):
2970
2915
        try:
2971
2916
            uid = pwd.getpwnam(name).pw_uid
2972
 
            gid = pwd.getpwnam(group).pw_gid
 
2917
            gid = pwd.getpwnam(name).pw_gid
2973
2918
            break
2974
2919
        except KeyError:
2975
2920
            continue
2979
2924
    try:
2980
2925
        os.setgid(gid)
2981
2926
        os.setuid(uid)
2982
 
        if debug:
2983
 
            logger.debug("Did setuid/setgid to {}:{}".format(uid,
2984
 
                                                             gid))
2985
2927
    except OSError as error:
2986
 
        logger.warning("Failed to setuid/setgid to {}:{}: {}"
2987
 
                       .format(uid, gid, os.strerror(error.errno)))
2988
2928
        if error.errno != errno.EPERM:
2989
2929
            raise
2990
2930
    
3012
2952
        # Close all input and output, do double fork, etc.
3013
2953
        daemon()
3014
2954
    
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()
 
2955
    # multiprocessing will use threads, so before we use gobject we
 
2956
    # need to inform gobject that threads will be used.
 
2957
    gobject.threads_init()
3018
2958
    
3019
2959
    global main_loop
3020
2960
    # From the Avahi example code
3021
2961
    DBusGMainLoop(set_as_default=True)
3022
 
    main_loop = GLib.MainLoop()
 
2962
    main_loop = gobject.MainLoop()
3023
2963
    bus = dbus.SystemBus()
3024
2964
    # End of Avahi example code
3025
2965
    if use_dbus:
3069
3009
    if server_settings["restore"]:
3070
3010
        try:
3071
3011
            with open(stored_state_path, "rb") as 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"))
 
3012
                clients_data, old_client_settings = pickle.load(
 
3013
                    stored_state)
3117
3014
            os.remove(stored_state_path)
3118
3015
        except IOError as e:
3119
3016
            if e.errno == errno.ENOENT:
3220
3117
        del pidfile
3221
3118
        del pidfilename
3222
3119
    
3223
 
    for termsig in (signal.SIGHUP, signal.SIGTERM):
3224
 
        GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig,
3225
 
                             lambda: main_loop.quit() and False)
 
3120
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
 
3121
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
3226
3122
    
3227
3123
    if use_dbus:
3228
3124
        
3259
3155
            def GetAllClients(self):
3260
3156
                "D-Bus method"
3261
3157
                return dbus.Array(c.dbus_object_path for c in
3262
 
                                  tcp_server.clients.values())
 
3158
                                  tcp_server.clients.itervalues())
3263
3159
            
3264
3160
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3265
3161
                               "true"})
3270
3166
                return dbus.Dictionary(
3271
3167
                    { c.dbus_object_path: c.GetAll(
3272
3168
                        "se.recompile.Mandos.Client")
3273
 
                      for c in tcp_server.clients.values() },
 
3169
                      for c in tcp_server.clients.itervalues() },
3274
3170
                    signature="oa{sv}")
3275
3171
            
3276
3172
            @dbus.service.method(_interface, in_signature="o")
3277
3173
            def RemoveClient(self, object_path):
3278
3174
                "D-Bus method"
3279
 
                for c in tcp_server.clients.values():
 
3175
                for c in tcp_server.clients.itervalues():
3280
3176
                    if c.dbus_object_path == object_path:
3281
3177
                        del tcp_server.clients[c.name]
3282
3178
                        c.remove_from_connection()
3327
3223
        
3328
3224
        mandos_dbus_service = MandosDBusService()
3329
3225
    
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
3334
3226
    def cleanup():
3335
3227
        "Cleanup function; run on exit"
3336
3228
        if zeroconf:
3337
3229
            service.cleanup()
3338
3230
        
3339
 
        mp.active_children()
3340
 
        wn.close()
 
3231
        multiprocessing.active_children()
 
3232
        wnull.close()
3341
3233
        if not (tcp_server.clients or client_settings):
3342
3234
            return
3343
3235
        
3346
3238
        # removed/edited, old secret will thus be unrecovable.
3347
3239
        clients = {}
3348
3240
        with PGPEngine() as pgp:
3349
 
            for client in tcp_server.clients.values():
 
3241
            for client in tcp_server.clients.itervalues():
3350
3242
                key = client_settings[client.name]["secret"]
3351
3243
                client.encrypted_secret = pgp.encrypt(client.secret,
3352
3244
                                                      key)
3376
3268
                    prefix='clients-',
3377
3269
                    dir=os.path.dirname(stored_state_path),
3378
3270
                    delete=False) as stored_state:
3379
 
                pickle.dump((clients, client_settings), stored_state,
3380
 
                            protocol = 2)
 
3271
                pickle.dump((clients, client_settings), stored_state)
3381
3272
                tempname = stored_state.name
3382
3273
            os.rename(tempname, stored_state_path)
3383
3274
        except (IOError, OSError) as e:
3408
3299
    
3409
3300
    atexit.register(cleanup)
3410
3301
    
3411
 
    for client in tcp_server.clients.values():
 
3302
    for client in tcp_server.clients.itervalues():
3412
3303
        if use_dbus:
3413
3304
            # Emit D-Bus signal for adding
3414
3305
            mandos_dbus_service.client_added_signal(client)
3443
3334
                sys.exit(1)
3444
3335
            # End of Avahi example code
3445
3336
        
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))
 
3337
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
 
3338
                             lambda *args, **kwargs:
 
3339
                             (tcp_server.handle_request
 
3340
                              (*args[2:], **kwargs) or True))
3450
3341
        
3451
3342
        logger.debug("Starting main loop")
3452
3343
        main_loop.run()