/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
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
519
450
    
520
451
    # Constants
521
452
    E_SUCCESS = 0
522
 
    E_INTERRUPTED = -52
523
 
    E_AGAIN = -28
524
453
    CRT_OPENPGP = 2
525
454
    CLIENT = 2
526
455
    SHUT_RDWR = 0
545
474
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
546
475
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
547
476
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
548
 
    credentials_type_t = ctypes.c_int
 
477
    credentials_type_t = ctypes.c_int # 
549
478
    transport_ptr_t = ctypes.c_void_p
550
479
    close_request_t = ctypes.c_int
551
480
    
554
483
        # We need to use the class name "GnuTLS" here, since this
555
484
        # exception might be raised from within GnuTLS.__init__,
556
485
        # which is called before the assignment to the "gnutls"
557
 
        # global variable has happened.
 
486
        # global variable happens.
558
487
        def __init__(self, message = None, code = None, args=()):
559
488
            # Default usage is by a message string, but if a return
560
489
            # code is passed, convert it to a string with
561
490
            # gnutls.strerror()
562
 
            self.code = code
563
491
            if message is None and code is not None:
564
492
                message = GnuTLS.strerror(code)
565
493
            return super(GnuTLS.Error, self).__init__(
603
531
        
604
532
        def send(self, data):
605
533
            data = bytes(data)
606
 
            data_len = len(data)
607
 
            while data_len > 0:
608
 
                data_len -= gnutls.record_send(self._c_object,
609
 
                                               data[-data_len:],
610
 
                                               data_len)
 
534
            if not data:
 
535
                return 0
 
536
            return gnutls.record_send(self._c_object, data, len(data))
611
537
        
612
538
        def bye(self):
613
539
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
614
540
    
615
 
    # Error handling functions
 
541
    # Error handling function
616
542
    def _error_code(result):
617
543
        """A function to raise exceptions on errors, suitable
618
544
        for the 'restype' attribute on ctypes functions"""
622
548
            raise gnutls.CertificateSecurityError(code = result)
623
549
        raise gnutls.Error(code = result)
624
550
    
625
 
    def _retry_on_error(result, func, arguments):
626
 
        """A function to retry on some errors, suitable
627
 
        for the 'errcheck' attribute on ctypes functions"""
628
 
        while result < 0:
629
 
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
630
 
                return _error_code(result)
631
 
            result = func(*arguments)
632
 
        return result
633
 
    
634
551
    # Unless otherwise indicated, the function declarations below are
635
552
    # all from the gnutls/gnutls.h C header file.
636
553
    
652
569
    record_send.argtypes = [session_t, ctypes.c_void_p,
653
570
                            ctypes.c_size_t]
654
571
    record_send.restype = ctypes.c_ssize_t
655
 
    record_send.errcheck = _retry_on_error
656
572
    
657
573
    certificate_allocate_credentials = (
658
574
        _library.gnutls_certificate_allocate_credentials)
704
620
    handshake = _library.gnutls_handshake
705
621
    handshake.argtypes = [session_t]
706
622
    handshake.restype = _error_code
707
 
    handshake.errcheck = _retry_on_error
708
623
    
709
624
    transport_set_ptr = _library.gnutls_transport_set_ptr
710
625
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
713
628
    bye = _library.gnutls_bye
714
629
    bye.argtypes = [session_t, close_request_t]
715
630
    bye.restype = _error_code
716
 
    bye.errcheck = _retry_on_error
717
631
    
718
632
    check_version = _library.gnutls_check_version
719
633
    check_version.argtypes = [ctypes.c_char_p]
748
662
                                                ctypes.c_size_t)]
749
663
    openpgp_crt_get_fingerprint.restype = _error_code
750
664
    
751
 
    # Remove non-public functions
752
 
    del _error_code, _retry_on_error
 
665
    # Remove non-public function
 
666
    del _error_code
753
667
# Create the global "gnutls" object, simulating a module
754
668
gnutls = GnuTLS()
755
669
 
773
687
    checker:    subprocess.Popen(); a running checker process used
774
688
                                    to see if the client lives.
775
689
                                    'None' if no process is running.
776
 
    checker_callback_tag: a GLib event source tag, or None
 
690
    checker_callback_tag: a gobject event source tag, or None
777
691
    checker_command: string; External command which is run to check
778
692
                     if client lives.  %() expansions are done at
779
693
                     runtime with vars(self) as dict, so that for
780
694
                     instance %(name)s can be used in the command.
781
 
    checker_initiator_tag: a GLib event source tag, or None
 
695
    checker_initiator_tag: a gobject event source tag, or None
782
696
    created:    datetime.datetime(); (UTC) object creation
783
697
    client_structure: Object describing what attributes a client has
784
698
                      and is used for storing the client at exit
785
699
    current_checker_command: string; current running checker_command
786
 
    disable_initiator_tag: a GLib event source tag, or None
 
700
    disable_initiator_tag: a gobject event source tag, or None
787
701
    enabled:    bool()
788
702
    fingerprint: string (40 or 32 hexadecimal digits); used to
789
703
                 uniquely identify the client
852
766
            client["fingerprint"] = (section["fingerprint"].upper()
853
767
                                     .replace(" ", ""))
854
768
            if "secret" in section:
855
 
                client["secret"] = codecs.decode(section["secret"]
856
 
                                                 .encode("utf-8"),
857
 
                                                 "base64")
 
769
                client["secret"] = section["secret"].decode("base64")
858
770
            elif "secfile" in section:
859
771
                with open(os.path.expanduser(os.path.expandvars
860
772
                                             (section["secfile"])),
913
825
        self.changedstate = multiprocessing_manager.Condition(
914
826
            multiprocessing_manager.Lock())
915
827
        self.client_structure = [attr
916
 
                                 for attr in self.__dict__.keys()
 
828
                                 for attr in self.__dict__.iterkeys()
917
829
                                 if not attr.startswith("_")]
918
830
        self.client_structure.append("client_structure")
919
831
        
945
857
        if not quiet:
946
858
            logger.info("Disabling client %s", self.name)
947
859
        if getattr(self, "disable_initiator_tag", None) is not None:
948
 
            GLib.source_remove(self.disable_initiator_tag)
 
860
            gobject.source_remove(self.disable_initiator_tag)
949
861
            self.disable_initiator_tag = None
950
862
        self.expires = None
951
863
        if getattr(self, "checker_initiator_tag", None) is not None:
952
 
            GLib.source_remove(self.checker_initiator_tag)
 
864
            gobject.source_remove(self.checker_initiator_tag)
953
865
            self.checker_initiator_tag = None
954
866
        self.stop_checker()
955
867
        self.enabled = False
956
868
        if not quiet:
957
869
            self.send_changedstate()
958
 
        # 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
959
871
        return False
960
872
    
961
873
    def __del__(self):
965
877
        # Schedule a new checker to be started an 'interval' from now,
966
878
        # and every interval from then on.
967
879
        if self.checker_initiator_tag is not None:
968
 
            GLib.source_remove(self.checker_initiator_tag)
969
 
        self.checker_initiator_tag = GLib.timeout_add(
 
880
            gobject.source_remove(self.checker_initiator_tag)
 
881
        self.checker_initiator_tag = gobject.timeout_add(
970
882
            int(self.interval.total_seconds() * 1000),
971
883
            self.start_checker)
972
884
        # Schedule a disable() when 'timeout' has passed
973
885
        if self.disable_initiator_tag is not None:
974
 
            GLib.source_remove(self.disable_initiator_tag)
975
 
        self.disable_initiator_tag = GLib.timeout_add(
 
886
            gobject.source_remove(self.disable_initiator_tag)
 
887
        self.disable_initiator_tag = gobject.timeout_add(
976
888
            int(self.timeout.total_seconds() * 1000), self.disable)
977
889
        # Also start a new checker *right now*.
978
890
        self.start_checker()
1014
926
        if timeout is None:
1015
927
            timeout = self.timeout
1016
928
        if self.disable_initiator_tag is not None:
1017
 
            GLib.source_remove(self.disable_initiator_tag)
 
929
            gobject.source_remove(self.disable_initiator_tag)
1018
930
            self.disable_initiator_tag = None
1019
931
        if getattr(self, "enabled", False):
1020
 
            self.disable_initiator_tag = GLib.timeout_add(
 
932
            self.disable_initiator_tag = gobject.timeout_add(
1021
933
                int(timeout.total_seconds() * 1000), self.disable)
1022
934
            self.expires = datetime.datetime.utcnow() + timeout
1023
935
    
1078
990
                args = (pipe[1], subprocess.call, command),
1079
991
                kwargs = popen_args)
1080
992
            self.checker.start()
1081
 
            self.checker_callback_tag = GLib.io_add_watch(
1082
 
                pipe[0].fileno(), GLib.IO_IN,
 
993
            self.checker_callback_tag = gobject.io_add_watch(
 
994
                pipe[0].fileno(), gobject.IO_IN,
1083
995
                self.checker_callback, pipe[0], command)
1084
 
        # Re-run this periodically if run by GLib.timeout_add
 
996
        # Re-run this periodically if run by gobject.timeout_add
1085
997
        return True
1086
998
    
1087
999
    def stop_checker(self):
1088
1000
        """Force the checker process, if any, to stop."""
1089
1001
        if self.checker_callback_tag:
1090
 
            GLib.source_remove(self.checker_callback_tag)
 
1002
            gobject.source_remove(self.checker_callback_tag)
1091
1003
            self.checker_callback_tag = None
1092
1004
        if getattr(self, "checker", None) is None:
1093
1005
            return
1567
1479
                interface_names.add(alt_interface)
1568
1480
                # Is this a D-Bus signal?
1569
1481
                if getattr(attribute, "_dbus_is_signal", False):
1570
 
                    # Extract the original non-method undecorated
1571
 
                    # function by black magic
1572
1482
                    if sys.version_info.major == 2:
 
1483
                        # Extract the original non-method undecorated
 
1484
                        # function by black magic
1573
1485
                        nonmethod_func = (dict(
1574
1486
                            zip(attribute.func_code.co_freevars,
1575
1487
                                attribute.__closure__))
1576
1488
                                          ["func"].cell_contents)
1577
1489
                    else:
1578
 
                        nonmethod_func = (dict(
1579
 
                            zip(attribute.__code__.co_freevars,
1580
 
                                attribute.__closure__))
1581
 
                                          ["func"].cell_contents)
 
1490
                        nonmethod_func = attribute
1582
1491
                    # Create a new, but exactly alike, function
1583
1492
                    # object, and decorate it to be a new D-Bus signal
1584
1493
                    # with the alternate D-Bus interface name
1585
 
                    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__)
1586
1508
                    new_function = (dbus.service.signal(
1587
1509
                        alt_interface,
1588
1510
                        attribute._dbus_signature)(new_function))
1627
1549
                            alt_interface,
1628
1550
                            attribute._dbus_in_signature,
1629
1551
                            attribute._dbus_out_signature)
1630
 
                        (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)))
1631
1557
                    # Copy annotations, if any
1632
1558
                    try:
1633
1559
                        attr[attrname]._dbus_annotations = dict(
1645
1571
                        attribute._dbus_access,
1646
1572
                        attribute._dbus_get_args_options
1647
1573
                        ["byte_arrays"])
1648
 
                                      (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)))
1649
1580
                    # Copy annotations, if any
1650
1581
                    try:
1651
1582
                        attr[attrname]._dbus_annotations = dict(
1660
1591
                    # to the class.
1661
1592
                    attr[attrname] = (
1662
1593
                        dbus_interface_annotations(alt_interface)
1663
 
                        (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)))
1664
1599
            if deprecate:
1665
1600
                # Deprecate all alternate interfaces
1666
1601
                iname="_AlternateDBusNames_interface_annotation{}"
1679
1614
            if interface_names:
1680
1615
                # Replace the class with a new subclass of it with
1681
1616
                # 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)
 
1617
                cls = type(b"{}Alternate".format(cls.__name__),
 
1618
                           (cls, ), attr)
1688
1619
        return cls
1689
1620
    
1690
1621
    return wrapper
1848
1779
    
1849
1780
    def approve(self, value=True):
1850
1781
        self.approved = value
1851
 
        GLib.timeout_add(int(self.approval_duration.total_seconds()
1852
 
                             * 1000), self._reset_approved)
 
1782
        gobject.timeout_add(int(self.approval_duration.total_seconds()
 
1783
                                * 1000), self._reset_approved)
1853
1784
        self.send_changedstate()
1854
1785
    
1855
1786
    ## D-Bus methods, signals & properties
2065
1996
                if (getattr(self, "disable_initiator_tag", None)
2066
1997
                    is None):
2067
1998
                    return
2068
 
                GLib.source_remove(self.disable_initiator_tag)
2069
 
                self.disable_initiator_tag = GLib.timeout_add(
 
1999
                gobject.source_remove(self.disable_initiator_tag)
 
2000
                self.disable_initiator_tag = gobject.timeout_add(
2070
2001
                    int((self.expires - now).total_seconds() * 1000),
2071
2002
                    self.disable)
2072
2003
    
2092
2023
            return
2093
2024
        if self.enabled:
2094
2025
            # Reschedule checker run
2095
 
            GLib.source_remove(self.checker_initiator_tag)
2096
 
            self.checker_initiator_tag = GLib.timeout_add(
 
2026
            gobject.source_remove(self.checker_initiator_tag)
 
2027
            self.checker_initiator_tag = gobject.timeout_add(
2097
2028
                value, self.start_checker)
2098
2029
            self.start_checker() # Start one now, too
2099
2030
    
2191
2122
            priority = self.server.gnutls_priority
2192
2123
            if priority is None:
2193
2124
                priority = "NORMAL"
2194
 
            gnutls.priority_set_direct(session._c_object,
2195
 
                                       priority.encode("utf-8"),
 
2125
            gnutls.priority_set_direct(session._c_object, priority,
2196
2126
                                       None)
2197
2127
            
2198
2128
            # Start communication using the Mandos protocol
2285
2215
                    else:
2286
2216
                        delay -= time2 - time
2287
2217
                
2288
 
                try:
2289
 
                    session.send(client.secret)
2290
 
                except gnutls.Error as error:
2291
 
                    logger.warning("gnutls send failed",
2292
 
                                   exc_info = error)
2293
 
                    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
2294
2230
                
2295
2231
                logger.info("Sending secret to %s", client.name)
2296
2232
                # bump the timeout using extended_timeout
2455
2391
        bind to an address or port if they were not specified."""
2456
2392
        if self.interface is not None:
2457
2393
            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
 
2394
                logger.error("SO_BINDTODEVICE does not exist;"
 
2395
                             " cannot bind to interface %s",
 
2396
                             self.interface)
 
2397
            else:
 
2398
                try:
 
2399
                    self.socket.setsockopt(
 
2400
                        socket.SOL_SOCKET, SO_BINDTODEVICE,
 
2401
                        (self.interface + "\0").encode("utf-8"))
 
2402
                except socket.error as error:
 
2403
                    if error.errno == errno.EPERM:
 
2404
                        logger.error("No permission to bind to"
 
2405
                                     " interface %s", self.interface)
 
2406
                    elif error.errno == errno.ENOPROTOOPT:
 
2407
                        logger.error("SO_BINDTODEVICE not available;"
 
2408
                                     " cannot bind to interface %s",
 
2409
                                     self.interface)
 
2410
                    elif error.errno == errno.ENODEV:
 
2411
                        logger.error("Interface %s does not exist,"
 
2412
                                     " cannot bind", self.interface)
 
2413
                    else:
 
2414
                        raise
2479
2415
        # Only bind(2) the socket if we really need to.
2480
2416
        if self.server_address[0] or self.server_address[1]:
2481
2417
            if not self.server_address[0]:
2504
2440
        gnutls_priority GnuTLS priority string
2505
2441
        use_dbus:       Boolean; to emit D-Bus signals or not
2506
2442
    
2507
 
    Assumes a GLib.MainLoop event loop.
 
2443
    Assumes a gobject.MainLoop event loop.
2508
2444
    """
2509
2445
    
2510
2446
    def __init__(self, server_address, RequestHandlerClass,
2535
2471
    
2536
2472
    def add_pipe(self, parent_pipe, proc):
2537
2473
        # Call "handle_ipc" for both data and EOF events
2538
 
        GLib.io_add_watch(
 
2474
        gobject.io_add_watch(
2539
2475
            parent_pipe.fileno(),
2540
 
            GLib.IO_IN | GLib.IO_HUP,
 
2476
            gobject.IO_IN | gobject.IO_HUP,
2541
2477
            functools.partial(self.handle_ipc,
2542
2478
                              parent_pipe = parent_pipe,
2543
2479
                              proc = proc))
2547
2483
                   proc = None,
2548
2484
                   client_object=None):
2549
2485
        # error, or the other end of multiprocessing.Pipe has closed
2550
 
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2486
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
2551
2487
            # Wait for other process to exit
2552
2488
            proc.join()
2553
2489
            return False
2560
2496
            fpr = request[1]
2561
2497
            address = request[2]
2562
2498
            
2563
 
            for c in self.clients.values():
 
2499
            for c in self.clients.itervalues():
2564
2500
                if c.fingerprint == fpr:
2565
2501
                    client = c
2566
2502
                    break
2574
2510
                parent_pipe.send(False)
2575
2511
                return False
2576
2512
            
2577
 
            GLib.io_add_watch(
 
2513
            gobject.io_add_watch(
2578
2514
                parent_pipe.fileno(),
2579
 
                GLib.IO_IN | GLib.IO_HUP,
 
2515
                gobject.IO_IN | gobject.IO_HUP,
2580
2516
                functools.partial(self.handle_ipc,
2581
2517
                                  parent_pipe = parent_pipe,
2582
2518
                                  proc = proc,
2964
2900
            logger.error("Could not open file %r", pidfilename,
2965
2901
                         exc_info=e)
2966
2902
    
2967
 
    for name, group in (("_mandos", "_mandos"),
2968
 
                        ("mandos", "mandos"),
2969
 
                        ("nobody", "nogroup")):
 
2903
    for name in ("_mandos", "mandos", "nobody"):
2970
2904
        try:
2971
2905
            uid = pwd.getpwnam(name).pw_uid
2972
 
            gid = pwd.getpwnam(group).pw_gid
 
2906
            gid = pwd.getpwnam(name).pw_gid
2973
2907
            break
2974
2908
        except KeyError:
2975
2909
            continue
2979
2913
    try:
2980
2914
        os.setgid(gid)
2981
2915
        os.setuid(uid)
2982
 
        if debug:
2983
 
            logger.debug("Did setuid/setgid to {}:{}".format(uid,
2984
 
                                                             gid))
2985
2916
    except OSError as error:
2986
 
        logger.warning("Failed to setuid/setgid to {}:{}: {}"
2987
 
                       .format(uid, gid, os.strerror(error.errno)))
2988
2917
        if error.errno != errno.EPERM:
2989
2918
            raise
2990
2919
    
3012
2941
        # Close all input and output, do double fork, etc.
3013
2942
        daemon()
3014
2943
    
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()
 
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()
3018
2947
    
3019
2948
    global main_loop
3020
2949
    # From the Avahi example code
3021
2950
    DBusGMainLoop(set_as_default=True)
3022
 
    main_loop = GLib.MainLoop()
 
2951
    main_loop = gobject.MainLoop()
3023
2952
    bus = dbus.SystemBus()
3024
2953
    # End of Avahi example code
3025
2954
    if use_dbus:
3069
2998
    if server_settings["restore"]:
3070
2999
        try:
3071
3000
            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"))
 
3001
                clients_data, old_client_settings = pickle.load(
 
3002
                    stored_state)
3117
3003
            os.remove(stored_state_path)
3118
3004
        except IOError as e:
3119
3005
            if e.errno == errno.ENOENT:
3220
3106
        del pidfile
3221
3107
        del pidfilename
3222
3108
    
3223
 
    for termsig in (signal.SIGHUP, signal.SIGTERM):
3224
 
        GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig,
3225
 
                             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())
3226
3111
    
3227
3112
    if use_dbus:
3228
3113
        
3259
3144
            def GetAllClients(self):
3260
3145
                "D-Bus method"
3261
3146
                return dbus.Array(c.dbus_object_path for c in
3262
 
                                  tcp_server.clients.values())
 
3147
                                  tcp_server.clients.itervalues())
3263
3148
            
3264
3149
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3265
3150
                               "true"})
3270
3155
                return dbus.Dictionary(
3271
3156
                    { c.dbus_object_path: c.GetAll(
3272
3157
                        "se.recompile.Mandos.Client")
3273
 
                      for c in tcp_server.clients.values() },
 
3158
                      for c in tcp_server.clients.itervalues() },
3274
3159
                    signature="oa{sv}")
3275
3160
            
3276
3161
            @dbus.service.method(_interface, in_signature="o")
3277
3162
            def RemoveClient(self, object_path):
3278
3163
                "D-Bus method"
3279
 
                for c in tcp_server.clients.values():
 
3164
                for c in tcp_server.clients.itervalues():
3280
3165
                    if c.dbus_object_path == object_path:
3281
3166
                        del tcp_server.clients[c.name]
3282
3167
                        c.remove_from_connection()
3327
3212
        
3328
3213
        mandos_dbus_service = MandosDBusService()
3329
3214
    
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
3215
    def cleanup():
3335
3216
        "Cleanup function; run on exit"
3336
3217
        if zeroconf:
3337
3218
            service.cleanup()
3338
3219
        
3339
 
        mp.active_children()
3340
 
        wn.close()
 
3220
        multiprocessing.active_children()
 
3221
        wnull.close()
3341
3222
        if not (tcp_server.clients or client_settings):
3342
3223
            return
3343
3224
        
3346
3227
        # removed/edited, old secret will thus be unrecovable.
3347
3228
        clients = {}
3348
3229
        with PGPEngine() as pgp:
3349
 
            for client in tcp_server.clients.values():
 
3230
            for client in tcp_server.clients.itervalues():
3350
3231
                key = client_settings[client.name]["secret"]
3351
3232
                client.encrypted_secret = pgp.encrypt(client.secret,
3352
3233
                                                      key)
3376
3257
                    prefix='clients-',
3377
3258
                    dir=os.path.dirname(stored_state_path),
3378
3259
                    delete=False) as stored_state:
3379
 
                pickle.dump((clients, client_settings), stored_state,
3380
 
                            protocol = 2)
 
3260
                pickle.dump((clients, client_settings), stored_state)
3381
3261
                tempname = stored_state.name
3382
3262
            os.rename(tempname, stored_state_path)
3383
3263
        except (IOError, OSError) as e:
3408
3288
    
3409
3289
    atexit.register(cleanup)
3410
3290
    
3411
 
    for client in tcp_server.clients.values():
 
3291
    for client in tcp_server.clients.itervalues():
3412
3292
        if use_dbus:
3413
3293
            # Emit D-Bus signal for adding
3414
3294
            mandos_dbus_service.client_added_signal(client)
3443
3323
                sys.exit(1)
3444
3324
            # End of Avahi example code
3445
3325
        
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))
 
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))
3450
3330
        
3451
3331
        logger.debug("Starting main loop")
3452
3332
        main_loop.run()