/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2016-03-12 20:23:15 UTC
  • Revision ID: teddy@recompile.se-20160312202315-hu7b87ivetlxqbw3
Server: Fix minor thing with Python 3 compatibility

Fix another small thing with unpickling string values.

* mandos (main): When restoring pickled client data, only decode byte
                 string for "host" key if it really is a byte string.

Show diffs side-by-side

added added

removed removed

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