/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 03:01:43 UTC
  • mto: (237.7.594 trunk)
  • mto: This revision was merged to the branch mainline in revision 331.
  • Revision ID: teddy@recompile.se-20160228030143-i6w90r7wzkvlx9kq
Stop using python-gnutls.  Use GnuTLS 3.3 or later directly.

* INSTALL: Document dependency on GnuTLS 3.3 and remove dependency on
          Python-GnuTLS.

* debian/control (Source: mandos/Build-Depends): Add (>= 3.3.0) to
                                                 "libgnutls28-dev" and
                                                 "gnutls-dev".
  (Source: mandos/Build-Depends-Indep): Remove "python2.7-gnutls".
  (Package: mandos/Depends): Remove "python-gnutls" and
                             "python2.7-gnutls", add "libgnutls28-dev
                             (>= 3.3.0) | libgnutls30 (>= 3.3.0)"
* mandos: Remove imports of "gnutls" and all submodules.
  (GnuTLS, gnutls): New; simulate a "gnutls" module.  Change all
                    callers to match new shorter names.

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
97
98
if sys.version_info.major == 2:
98
99
    str = unicode
99
100
 
100
 
version = "1.7.6"
 
101
version = "1.7.1"
101
102
stored_state_file = "clients.pickle"
102
103
 
103
104
logger = logging.getLogger()
118
119
        return interface_index
119
120
 
120
121
 
121
 
def copy_function(func):
122
 
    """Make a copy of a function"""
123
 
    if sys.version_info.major == 2:
124
 
        return types.FunctionType(func.func_code,
125
 
                                  func.func_globals,
126
 
                                  func.func_name,
127
 
                                  func.func_defaults,
128
 
                                  func.func_closure)
129
 
    else:
130
 
        return types.FunctionType(func.__code__,
131
 
                                  func.__globals__,
132
 
                                  func.__name__,
133
 
                                  func.__defaults__,
134
 
                                  func.__closure__)
135
 
 
136
 
 
137
122
def initlogger(debug, level=logging.WARNING):
138
123
    """init logger and add loglevel"""
139
124
    
166
151
    
167
152
    def __init__(self):
168
153
        self.tempdir = tempfile.mkdtemp(prefix="mandos-")
169
 
        self.gpg = "gpg"
170
 
        try:
171
 
            output = subprocess.check_output(["gpgconf"])
172
 
            for line in output.splitlines():
173
 
                name, text, path = line.split(b":")
174
 
                if name == "gpg":
175
 
                    self.gpg = path
176
 
                    break
177
 
        except OSError as e:
178
 
            if e.errno != errno.ENOENT:
179
 
                raise
180
154
        self.gnupgargs = ['--batch',
181
 
                          '--homedir', self.tempdir,
 
155
                          '--home', self.tempdir,
182
156
                          '--force-mdc',
183
157
                          '--quiet',
184
158
                          '--no-use-agent']
223
197
                dir=self.tempdir) as passfile:
224
198
            passfile.write(passphrase)
225
199
            passfile.flush()
226
 
            proc = subprocess.Popen([self.gpg, '--symmetric',
 
200
            proc = subprocess.Popen(['gpg', '--symmetric',
227
201
                                     '--passphrase-file',
228
202
                                     passfile.name]
229
203
                                    + self.gnupgargs,
241
215
                dir = self.tempdir) as passfile:
242
216
            passfile.write(passphrase)
243
217
            passfile.flush()
244
 
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
218
            proc = subprocess.Popen(['gpg', '--decrypt',
245
219
                                     '--passphrase-file',
246
220
                                     passfile.name]
247
221
                                    + self.gnupgargs,
253
227
            raise PGPError(err)
254
228
        return decrypted_plaintext
255
229
 
256
 
# Pretend that we have an Avahi module
257
 
class Avahi(object):
258
 
    """This isn't so much a class as it is a module-like namespace.
259
 
    It is instantiated once, and simulates having an Avahi module."""
260
 
    IF_UNSPEC = -1              # avahi-common/address.h
261
 
    PROTO_UNSPEC = -1           # avahi-common/address.h
262
 
    PROTO_INET = 0              # avahi-common/address.h
263
 
    PROTO_INET6 = 1             # avahi-common/address.h
264
 
    DBUS_NAME = "org.freedesktop.Avahi"
265
 
    DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
266
 
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
267
 
    DBUS_PATH_SERVER = "/"
268
 
    def string_array_to_txt_array(self, t):
269
 
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
270
 
                           for s in t), signature="ay")
271
 
    ENTRY_GROUP_ESTABLISHED = 2 # avahi-common/defs.h
272
 
    ENTRY_GROUP_COLLISION = 3   # avahi-common/defs.h
273
 
    ENTRY_GROUP_FAILURE = 4     # avahi-common/defs.h
274
 
    SERVER_INVALID = 0          # avahi-common/defs.h
275
 
    SERVER_REGISTERING = 1      # avahi-common/defs.h
276
 
    SERVER_RUNNING = 2          # avahi-common/defs.h
277
 
    SERVER_COLLISION = 3        # avahi-common/defs.h
278
 
    SERVER_FAILURE = 4          # avahi-common/defs.h
279
 
avahi = Avahi()
280
230
 
281
231
class AvahiError(Exception):
282
232
    def __init__(self, value, *args, **kwargs):
486
436
    
487
437
    _library = ctypes.cdll.LoadLibrary(
488
438
        ctypes.util.find_library("gnutls"))
489
 
    _need_version = b"3.3.0"
 
439
    _need_version = "3.3.0"
490
440
    def __init__(self):
491
441
        # Need to use class name "GnuTLS" here, since this method is
492
442
        # called before the assignment to the "gnutls" global variable
500
450
    
501
451
    # Constants
502
452
    E_SUCCESS = 0
503
 
    E_INTERRUPTED = -52
504
 
    E_AGAIN = -28
505
453
    CRT_OPENPGP = 2
506
454
    CLIENT = 2
507
455
    SHUT_RDWR = 0
526
474
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
527
475
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
528
476
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
529
 
    credentials_type_t = ctypes.c_int
 
477
    credentials_type_t = ctypes.c_int # 
530
478
    transport_ptr_t = ctypes.c_void_p
531
479
    close_request_t = ctypes.c_int
532
480
    
535
483
        # We need to use the class name "GnuTLS" here, since this
536
484
        # exception might be raised from within GnuTLS.__init__,
537
485
        # which is called before the assignment to the "gnutls"
538
 
        # global variable has happened.
 
486
        # global variable happens.
539
487
        def __init__(self, message = None, code = None, args=()):
540
488
            # Default usage is by a message string, but if a return
541
489
            # code is passed, convert it to a string with
542
490
            # gnutls.strerror()
543
 
            self.code = code
544
491
            if message is None and code is not None:
545
492
                message = GnuTLS.strerror(code)
546
493
            return super(GnuTLS.Error, self).__init__(
584
531
        
585
532
        def send(self, data):
586
533
            data = bytes(data)
587
 
            data_len = len(data)
588
 
            while data_len > 0:
589
 
                data_len -= gnutls.record_send(self._c_object,
590
 
                                               data[-data_len:],
591
 
                                               data_len)
 
534
            if not data:
 
535
                return 0
 
536
            return gnutls.record_send(self._c_object, data, len(data))
592
537
        
593
538
        def bye(self):
594
539
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
595
540
    
596
 
    # Error handling functions
 
541
    # Error handling function
597
542
    def _error_code(result):
598
543
        """A function to raise exceptions on errors, suitable
599
544
        for the 'restype' attribute on ctypes functions"""
603
548
            raise gnutls.CertificateSecurityError(code = result)
604
549
        raise gnutls.Error(code = result)
605
550
    
606
 
    def _retry_on_error(result, func, arguments):
607
 
        """A function to retry on some errors, suitable
608
 
        for the 'errcheck' attribute on ctypes functions"""
609
 
        while result < 0:
610
 
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
611
 
                return _error_code(result)
612
 
            result = func(*arguments)
613
 
        return result
614
 
    
615
551
    # Unless otherwise indicated, the function declarations below are
616
552
    # all from the gnutls/gnutls.h C header file.
617
553
    
633
569
    record_send.argtypes = [session_t, ctypes.c_void_p,
634
570
                            ctypes.c_size_t]
635
571
    record_send.restype = ctypes.c_ssize_t
636
 
    record_send.errcheck = _retry_on_error
637
572
    
638
573
    certificate_allocate_credentials = (
639
574
        _library.gnutls_certificate_allocate_credentials)
685
620
    handshake = _library.gnutls_handshake
686
621
    handshake.argtypes = [session_t]
687
622
    handshake.restype = _error_code
688
 
    handshake.errcheck = _retry_on_error
689
623
    
690
624
    transport_set_ptr = _library.gnutls_transport_set_ptr
691
625
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
694
628
    bye = _library.gnutls_bye
695
629
    bye.argtypes = [session_t, close_request_t]
696
630
    bye.restype = _error_code
697
 
    bye.errcheck = _retry_on_error
698
631
    
699
632
    check_version = _library.gnutls_check_version
700
633
    check_version.argtypes = [ctypes.c_char_p]
729
662
                                                ctypes.c_size_t)]
730
663
    openpgp_crt_get_fingerprint.restype = _error_code
731
664
    
732
 
    # Remove non-public functions
733
 
    del _error_code, _retry_on_error
 
665
    # Remove non-public function
 
666
    del _error_code
734
667
# Create the global "gnutls" object, simulating a module
735
668
gnutls = GnuTLS()
736
669
 
754
687
    checker:    subprocess.Popen(); a running checker process used
755
688
                                    to see if the client lives.
756
689
                                    'None' if no process is running.
757
 
    checker_callback_tag: a GLib event source tag, or None
 
690
    checker_callback_tag: a gobject event source tag, or None
758
691
    checker_command: string; External command which is run to check
759
692
                     if client lives.  %() expansions are done at
760
693
                     runtime with vars(self) as dict, so that for
761
694
                     instance %(name)s can be used in the command.
762
 
    checker_initiator_tag: a GLib event source tag, or None
 
695
    checker_initiator_tag: a gobject event source tag, or None
763
696
    created:    datetime.datetime(); (UTC) object creation
764
697
    client_structure: Object describing what attributes a client has
765
698
                      and is used for storing the client at exit
766
699
    current_checker_command: string; current running checker_command
767
 
    disable_initiator_tag: a GLib event source tag, or None
 
700
    disable_initiator_tag: a gobject event source tag, or None
768
701
    enabled:    bool()
769
702
    fingerprint: string (40 or 32 hexadecimal digits); used to
770
703
                 uniquely identify the client
833
766
            client["fingerprint"] = (section["fingerprint"].upper()
834
767
                                     .replace(" ", ""))
835
768
            if "secret" in section:
836
 
                client["secret"] = codecs.decode(section["secret"]
837
 
                                                 .encode("utf-8"),
838
 
                                                 "base64")
 
769
                client["secret"] = section["secret"].decode("base64")
839
770
            elif "secfile" in section:
840
771
                with open(os.path.expanduser(os.path.expandvars
841
772
                                             (section["secfile"])),
894
825
        self.changedstate = multiprocessing_manager.Condition(
895
826
            multiprocessing_manager.Lock())
896
827
        self.client_structure = [attr
897
 
                                 for attr in self.__dict__.keys()
 
828
                                 for attr in self.__dict__.iterkeys()
898
829
                                 if not attr.startswith("_")]
899
830
        self.client_structure.append("client_structure")
900
831
        
926
857
        if not quiet:
927
858
            logger.info("Disabling client %s", self.name)
928
859
        if getattr(self, "disable_initiator_tag", None) is not None:
929
 
            GLib.source_remove(self.disable_initiator_tag)
 
860
            gobject.source_remove(self.disable_initiator_tag)
930
861
            self.disable_initiator_tag = None
931
862
        self.expires = None
932
863
        if getattr(self, "checker_initiator_tag", None) is not None:
933
 
            GLib.source_remove(self.checker_initiator_tag)
 
864
            gobject.source_remove(self.checker_initiator_tag)
934
865
            self.checker_initiator_tag = None
935
866
        self.stop_checker()
936
867
        self.enabled = False
937
868
        if not quiet:
938
869
            self.send_changedstate()
939
 
        # Do not run this again if called by a GLib.timeout_add
 
870
        # Do not run this again if called by a gobject.timeout_add
940
871
        return False
941
872
    
942
873
    def __del__(self):
946
877
        # Schedule a new checker to be started an 'interval' from now,
947
878
        # and every interval from then on.
948
879
        if self.checker_initiator_tag is not None:
949
 
            GLib.source_remove(self.checker_initiator_tag)
950
 
        self.checker_initiator_tag = GLib.timeout_add(
 
880
            gobject.source_remove(self.checker_initiator_tag)
 
881
        self.checker_initiator_tag = gobject.timeout_add(
951
882
            int(self.interval.total_seconds() * 1000),
952
883
            self.start_checker)
953
884
        # Schedule a disable() when 'timeout' has passed
954
885
        if self.disable_initiator_tag is not None:
955
 
            GLib.source_remove(self.disable_initiator_tag)
956
 
        self.disable_initiator_tag = GLib.timeout_add(
 
886
            gobject.source_remove(self.disable_initiator_tag)
 
887
        self.disable_initiator_tag = gobject.timeout_add(
957
888
            int(self.timeout.total_seconds() * 1000), self.disable)
958
889
        # Also start a new checker *right now*.
959
890
        self.start_checker()
995
926
        if timeout is None:
996
927
            timeout = self.timeout
997
928
        if self.disable_initiator_tag is not None:
998
 
            GLib.source_remove(self.disable_initiator_tag)
 
929
            gobject.source_remove(self.disable_initiator_tag)
999
930
            self.disable_initiator_tag = None
1000
931
        if getattr(self, "enabled", False):
1001
 
            self.disable_initiator_tag = GLib.timeout_add(
 
932
            self.disable_initiator_tag = gobject.timeout_add(
1002
933
                int(timeout.total_seconds() * 1000), self.disable)
1003
934
            self.expires = datetime.datetime.utcnow() + timeout
1004
935
    
1059
990
                args = (pipe[1], subprocess.call, command),
1060
991
                kwargs = popen_args)
1061
992
            self.checker.start()
1062
 
            self.checker_callback_tag = GLib.io_add_watch(
1063
 
                pipe[0].fileno(), GLib.IO_IN,
 
993
            self.checker_callback_tag = gobject.io_add_watch(
 
994
                pipe[0].fileno(), gobject.IO_IN,
1064
995
                self.checker_callback, pipe[0], command)
1065
 
        # Re-run this periodically if run by GLib.timeout_add
 
996
        # Re-run this periodically if run by gobject.timeout_add
1066
997
        return True
1067
998
    
1068
999
    def stop_checker(self):
1069
1000
        """Force the checker process, if any, to stop."""
1070
1001
        if self.checker_callback_tag:
1071
 
            GLib.source_remove(self.checker_callback_tag)
 
1002
            gobject.source_remove(self.checker_callback_tag)
1072
1003
            self.checker_callback_tag = None
1073
1004
        if getattr(self, "checker", None) is None:
1074
1005
            return
1548
1479
                interface_names.add(alt_interface)
1549
1480
                # Is this a D-Bus signal?
1550
1481
                if getattr(attribute, "_dbus_is_signal", False):
1551
 
                    # Extract the original non-method undecorated
1552
 
                    # function by black magic
1553
1482
                    if sys.version_info.major == 2:
 
1483
                        # Extract the original non-method undecorated
 
1484
                        # function by black magic
1554
1485
                        nonmethod_func = (dict(
1555
1486
                            zip(attribute.func_code.co_freevars,
1556
1487
                                attribute.__closure__))
1557
1488
                                          ["func"].cell_contents)
1558
1489
                    else:
1559
 
                        nonmethod_func = (dict(
1560
 
                            zip(attribute.__code__.co_freevars,
1561
 
                                attribute.__closure__))
1562
 
                                          ["func"].cell_contents)
 
1490
                        nonmethod_func = attribute
1563
1491
                    # Create a new, but exactly alike, function
1564
1492
                    # object, and decorate it to be a new D-Bus signal
1565
1493
                    # with the alternate D-Bus interface name
1566
 
                    new_function = copy_function(nonmethod_func)
 
1494
                    if sys.version_info.major == 2:
 
1495
                        new_function = types.FunctionType(
 
1496
                            nonmethod_func.func_code,
 
1497
                            nonmethod_func.func_globals,
 
1498
                            nonmethod_func.func_name,
 
1499
                            nonmethod_func.func_defaults,
 
1500
                            nonmethod_func.func_closure)
 
1501
                    else:
 
1502
                        new_function = types.FunctionType(
 
1503
                            nonmethod_func.__code__,
 
1504
                            nonmethod_func.__globals__,
 
1505
                            nonmethod_func.__name__,
 
1506
                            nonmethod_func.__defaults__,
 
1507
                            nonmethod_func.__closure__)
1567
1508
                    new_function = (dbus.service.signal(
1568
1509
                        alt_interface,
1569
1510
                        attribute._dbus_signature)(new_function))
1608
1549
                            alt_interface,
1609
1550
                            attribute._dbus_in_signature,
1610
1551
                            attribute._dbus_out_signature)
1611
 
                        (copy_function(attribute)))
 
1552
                        (types.FunctionType(attribute.func_code,
 
1553
                                            attribute.func_globals,
 
1554
                                            attribute.func_name,
 
1555
                                            attribute.func_defaults,
 
1556
                                            attribute.func_closure)))
1612
1557
                    # Copy annotations, if any
1613
1558
                    try:
1614
1559
                        attr[attrname]._dbus_annotations = dict(
1626
1571
                        attribute._dbus_access,
1627
1572
                        attribute._dbus_get_args_options
1628
1573
                        ["byte_arrays"])
1629
 
                                      (copy_function(attribute)))
 
1574
                                      (types.FunctionType(
 
1575
                                          attribute.func_code,
 
1576
                                          attribute.func_globals,
 
1577
                                          attribute.func_name,
 
1578
                                          attribute.func_defaults,
 
1579
                                          attribute.func_closure)))
1630
1580
                    # Copy annotations, if any
1631
1581
                    try:
1632
1582
                        attr[attrname]._dbus_annotations = dict(
1641
1591
                    # to the class.
1642
1592
                    attr[attrname] = (
1643
1593
                        dbus_interface_annotations(alt_interface)
1644
 
                        (copy_function(attribute)))
 
1594
                        (types.FunctionType(attribute.func_code,
 
1595
                                            attribute.func_globals,
 
1596
                                            attribute.func_name,
 
1597
                                            attribute.func_defaults,
 
1598
                                            attribute.func_closure)))
1645
1599
            if deprecate:
1646
1600
                # Deprecate all alternate interfaces
1647
1601
                iname="_AlternateDBusNames_interface_annotation{}"
1660
1614
            if interface_names:
1661
1615
                # Replace the class with a new subclass of it with
1662
1616
                # methods, signals, etc. as created above.
1663
 
                if sys.version_info.major == 2:
1664
 
                    cls = type(b"{}Alternate".format(cls.__name__),
1665
 
                               (cls, ), attr)
1666
 
                else:
1667
 
                    cls = type("{}Alternate".format(cls.__name__),
1668
 
                               (cls, ), attr)
 
1617
                cls = type(b"{}Alternate".format(cls.__name__),
 
1618
                           (cls, ), attr)
1669
1619
        return cls
1670
1620
    
1671
1621
    return wrapper
1829
1779
    
1830
1780
    def approve(self, value=True):
1831
1781
        self.approved = value
1832
 
        GLib.timeout_add(int(self.approval_duration.total_seconds()
1833
 
                             * 1000), self._reset_approved)
 
1782
        gobject.timeout_add(int(self.approval_duration.total_seconds()
 
1783
                                * 1000), self._reset_approved)
1834
1784
        self.send_changedstate()
1835
1785
    
1836
1786
    ## D-Bus methods, signals & properties
2046
1996
                if (getattr(self, "disable_initiator_tag", None)
2047
1997
                    is None):
2048
1998
                    return
2049
 
                GLib.source_remove(self.disable_initiator_tag)
2050
 
                self.disable_initiator_tag = GLib.timeout_add(
 
1999
                gobject.source_remove(self.disable_initiator_tag)
 
2000
                self.disable_initiator_tag = gobject.timeout_add(
2051
2001
                    int((self.expires - now).total_seconds() * 1000),
2052
2002
                    self.disable)
2053
2003
    
2073
2023
            return
2074
2024
        if self.enabled:
2075
2025
            # Reschedule checker run
2076
 
            GLib.source_remove(self.checker_initiator_tag)
2077
 
            self.checker_initiator_tag = GLib.timeout_add(
 
2026
            gobject.source_remove(self.checker_initiator_tag)
 
2027
            self.checker_initiator_tag = gobject.timeout_add(
2078
2028
                value, self.start_checker)
2079
2029
            self.start_checker() # Start one now, too
2080
2030
    
2265
2215
                    else:
2266
2216
                        delay -= time2 - time
2267
2217
                
2268
 
                try:
2269
 
                    session.send(client.secret)
2270
 
                except gnutls.Error as error:
2271
 
                    logger.warning("gnutls send failed",
2272
 
                                   exc_info = error)
2273
 
                    return
 
2218
                sent_size = 0
 
2219
                while sent_size < len(client.secret):
 
2220
                    try:
 
2221
                        sent = session.send(client.secret[sent_size:])
 
2222
                    except gnutls.Error as error:
 
2223
                        logger.warning("gnutls send failed",
 
2224
                                       exc_info=error)
 
2225
                        return
 
2226
                    logger.debug("Sent: %d, remaining: %d", sent,
 
2227
                                 len(client.secret) - (sent_size
 
2228
                                                       + sent))
 
2229
                    sent_size += sent
2274
2230
                
2275
2231
                logger.info("Sending secret to %s", client.name)
2276
2232
                # bump the timeout using extended_timeout
2484
2440
        gnutls_priority GnuTLS priority string
2485
2441
        use_dbus:       Boolean; to emit D-Bus signals or not
2486
2442
    
2487
 
    Assumes a GLib.MainLoop event loop.
 
2443
    Assumes a gobject.MainLoop event loop.
2488
2444
    """
2489
2445
    
2490
2446
    def __init__(self, server_address, RequestHandlerClass,
2515
2471
    
2516
2472
    def add_pipe(self, parent_pipe, proc):
2517
2473
        # Call "handle_ipc" for both data and EOF events
2518
 
        GLib.io_add_watch(
 
2474
        gobject.io_add_watch(
2519
2475
            parent_pipe.fileno(),
2520
 
            GLib.IO_IN | GLib.IO_HUP,
 
2476
            gobject.IO_IN | gobject.IO_HUP,
2521
2477
            functools.partial(self.handle_ipc,
2522
2478
                              parent_pipe = parent_pipe,
2523
2479
                              proc = proc))
2527
2483
                   proc = None,
2528
2484
                   client_object=None):
2529
2485
        # error, or the other end of multiprocessing.Pipe has closed
2530
 
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2486
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
2531
2487
            # Wait for other process to exit
2532
2488
            proc.join()
2533
2489
            return False
2540
2496
            fpr = request[1]
2541
2497
            address = request[2]
2542
2498
            
2543
 
            for c in self.clients.values():
 
2499
            for c in self.clients.itervalues():
2544
2500
                if c.fingerprint == fpr:
2545
2501
                    client = c
2546
2502
                    break
2554
2510
                parent_pipe.send(False)
2555
2511
                return False
2556
2512
            
2557
 
            GLib.io_add_watch(
 
2513
            gobject.io_add_watch(
2558
2514
                parent_pipe.fileno(),
2559
 
                GLib.IO_IN | GLib.IO_HUP,
 
2515
                gobject.IO_IN | gobject.IO_HUP,
2560
2516
                functools.partial(self.handle_ipc,
2561
2517
                                  parent_pipe = parent_pipe,
2562
2518
                                  proc = proc,
2944
2900
            logger.error("Could not open file %r", pidfilename,
2945
2901
                         exc_info=e)
2946
2902
    
2947
 
    for name, group in (("_mandos", "_mandos"),
2948
 
                        ("mandos", "mandos"),
2949
 
                        ("nobody", "nogroup")):
 
2903
    for name in ("_mandos", "mandos", "nobody"):
2950
2904
        try:
2951
2905
            uid = pwd.getpwnam(name).pw_uid
2952
 
            gid = pwd.getpwnam(group).pw_gid
 
2906
            gid = pwd.getpwnam(name).pw_gid
2953
2907
            break
2954
2908
        except KeyError:
2955
2909
            continue
2959
2913
    try:
2960
2914
        os.setgid(gid)
2961
2915
        os.setuid(uid)
2962
 
        if debug:
2963
 
            logger.debug("Did setuid/setgid to {}:{}".format(uid,
2964
 
                                                             gid))
2965
2916
    except OSError as error:
2966
 
        logger.warning("Failed to setuid/setgid to {}:{}: {}"
2967
 
                       .format(uid, gid, os.strerror(error.errno)))
2968
2917
        if error.errno != errno.EPERM:
2969
2918
            raise
2970
2919
    
2992
2941
        # Close all input and output, do double fork, etc.
2993
2942
        daemon()
2994
2943
    
2995
 
    # multiprocessing will use threads, so before we use GLib we need
2996
 
    # to inform GLib that threads will be used.
2997
 
    GLib.threads_init()
 
2944
    # multiprocessing will use threads, so before we use gobject we
 
2945
    # need to inform gobject that threads will be used.
 
2946
    gobject.threads_init()
2998
2947
    
2999
2948
    global main_loop
3000
2949
    # From the Avahi example code
3001
2950
    DBusGMainLoop(set_as_default=True)
3002
 
    main_loop = GLib.MainLoop()
 
2951
    main_loop = gobject.MainLoop()
3003
2952
    bus = dbus.SystemBus()
3004
2953
    # End of Avahi example code
3005
2954
    if use_dbus:
3049
2998
    if server_settings["restore"]:
3050
2999
        try:
3051
3000
            with open(stored_state_path, "rb") as stored_state:
3052
 
                if sys.version_info.major == 2:                
3053
 
                    clients_data, old_client_settings = pickle.load(
3054
 
                        stored_state)
3055
 
                else:
3056
 
                    bytes_clients_data, bytes_old_client_settings = (
3057
 
                        pickle.load(stored_state, encoding = "bytes"))
3058
 
                    ### Fix bytes to strings
3059
 
                    ## clients_data
3060
 
                    # .keys()
3061
 
                    clients_data = { (key.decode("utf-8")
3062
 
                                      if isinstance(key, bytes)
3063
 
                                      else key): value
3064
 
                                     for key, value in
3065
 
                                     bytes_clients_data.items() }
3066
 
                    del bytes_clients_data
3067
 
                    for key in clients_data:
3068
 
                        value = { (k.decode("utf-8")
3069
 
                                   if isinstance(k, bytes) else k): v
3070
 
                                  for k, v in
3071
 
                                  clients_data[key].items() }
3072
 
                        clients_data[key] = value
3073
 
                        # .client_structure
3074
 
                        value["client_structure"] = [
3075
 
                            (s.decode("utf-8")
3076
 
                             if isinstance(s, bytes)
3077
 
                             else s) for s in
3078
 
                            value["client_structure"] ]
3079
 
                        # .name & .host
3080
 
                        for k in ("name", "host"):
3081
 
                            if isinstance(value[k], bytes):
3082
 
                                value[k] = value[k].decode("utf-8")
3083
 
                    ## old_client_settings
3084
 
                    # .keys()
3085
 
                    old_client_settings = {
3086
 
                        (key.decode("utf-8")
3087
 
                         if isinstance(key, bytes)
3088
 
                         else key): value
3089
 
                        for key, value in
3090
 
                        bytes_old_client_settings.items() }
3091
 
                    del bytes_old_client_settings
3092
 
                    # .host
3093
 
                    for value in old_client_settings.values():
3094
 
                        if isinstance(value["host"], bytes):
3095
 
                            value["host"] = (value["host"]
3096
 
                                             .decode("utf-8"))
 
3001
                clients_data, old_client_settings = pickle.load(
 
3002
                    stored_state)
3097
3003
            os.remove(stored_state_path)
3098
3004
        except IOError as e:
3099
3005
            if e.errno == errno.ENOENT:
3200
3106
        del pidfile
3201
3107
        del pidfilename
3202
3108
    
3203
 
    for termsig in (signal.SIGHUP, signal.SIGTERM):
3204
 
        GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig,
3205
 
                             lambda: main_loop.quit() and False)
 
3109
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
 
3110
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
3206
3111
    
3207
3112
    if use_dbus:
3208
3113
        
3239
3144
            def GetAllClients(self):
3240
3145
                "D-Bus method"
3241
3146
                return dbus.Array(c.dbus_object_path for c in
3242
 
                                  tcp_server.clients.values())
 
3147
                                  tcp_server.clients.itervalues())
3243
3148
            
3244
3149
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3245
3150
                               "true"})
3250
3155
                return dbus.Dictionary(
3251
3156
                    { c.dbus_object_path: c.GetAll(
3252
3157
                        "se.recompile.Mandos.Client")
3253
 
                      for c in tcp_server.clients.values() },
 
3158
                      for c in tcp_server.clients.itervalues() },
3254
3159
                    signature="oa{sv}")
3255
3160
            
3256
3161
            @dbus.service.method(_interface, in_signature="o")
3257
3162
            def RemoveClient(self, object_path):
3258
3163
                "D-Bus method"
3259
 
                for c in tcp_server.clients.values():
 
3164
                for c in tcp_server.clients.itervalues():
3260
3165
                    if c.dbus_object_path == object_path:
3261
3166
                        del tcp_server.clients[c.name]
3262
3167
                        c.remove_from_connection()
3322
3227
        # removed/edited, old secret will thus be unrecovable.
3323
3228
        clients = {}
3324
3229
        with PGPEngine() as pgp:
3325
 
            for client in tcp_server.clients.values():
 
3230
            for client in tcp_server.clients.itervalues():
3326
3231
                key = client_settings[client.name]["secret"]
3327
3232
                client.encrypted_secret = pgp.encrypt(client.secret,
3328
3233
                                                      key)
3352
3257
                    prefix='clients-',
3353
3258
                    dir=os.path.dirname(stored_state_path),
3354
3259
                    delete=False) as stored_state:
3355
 
                pickle.dump((clients, client_settings), stored_state,
3356
 
                            protocol = 2)
 
3260
                pickle.dump((clients, client_settings), stored_state)
3357
3261
                tempname = stored_state.name
3358
3262
            os.rename(tempname, stored_state_path)
3359
3263
        except (IOError, OSError) as e:
3384
3288
    
3385
3289
    atexit.register(cleanup)
3386
3290
    
3387
 
    for client in tcp_server.clients.values():
 
3291
    for client in tcp_server.clients.itervalues():
3388
3292
        if use_dbus:
3389
3293
            # Emit D-Bus signal for adding
3390
3294
            mandos_dbus_service.client_added_signal(client)
3419
3323
                sys.exit(1)
3420
3324
            # End of Avahi example code
3421
3325
        
3422
 
        GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN,
3423
 
                          lambda *args, **kwargs:
3424
 
                          (tcp_server.handle_request
3425
 
                           (*args[2:], **kwargs) or True))
 
3326
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
 
3327
                             lambda *args, **kwargs:
 
3328
                             (tcp_server.handle_request
 
3329
                              (*args[2:], **kwargs) or True))
3426
3330
        
3427
3331
        logger.debug("Starting main loop")
3428
3332
        main_loop.run()