/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
723.1.1 by Teddy Hogeborn
Require Python 2.7.
1
#!/usr/bin/python2.7
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2
# -*- mode: python; coding: utf-8 -*-
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
3
# 
4
# Mandos server - give out binary blobs to connecting clients.
5
# 
6
# This program is partly derived from an example program for an Avahi
7
# service publisher, downloaded from
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
336 by Teddy Hogeborn
Code cleanup.
9
# methods "add", "remove", "server_state_changed",
10
# "entry_group_state_changed", "cleanup", and "activate" in the
11
# "AvahiService" class, and some lines in "main".
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
12
# 
28 by Teddy Hogeborn
* server.conf: New file.
13
# Everything else is
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
14
# Copyright © 2008-2015 Teddy Hogeborn
15
# Copyright © 2008-2015 Björn Påhlsson
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
16
# 
17
# This program is free software: you can redistribute it and/or modify
18
# it under the terms of the GNU General Public License as published by
19
# the Free Software Foundation, either version 3 of the License, or
20
# (at your option) any later version.
21
#
22
#     This program is distributed in the hope that it will be useful,
23
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
24
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
#     GNU General Public License for more details.
26
# 
27
# You should have received a copy of the GNU General Public License
109 by Teddy Hogeborn
* .bzrignore: New.
28
# along with this program.  If not, see
29
# <http://www.gnu.org/licenses/>.
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
30
# 
505.1.2 by Teddy Hogeborn
Change "fukt.bsnet.se" to "recompile.se" throughout.
31
# Contact the authors at <mandos@recompile.se>.
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
32
# 
3 by Björn Påhlsson
Python based server
33
463.1.5 by teddy at bsnet
* mandos: Use unicode string literals.
34
from __future__ import (division, absolute_import, print_function,
35
                        unicode_literals)
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
36
579 by Teddy Hogeborn
* mandos: Use all new builtins.
37
from future_builtins import *
38
749 by Teddy Hogeborn
mandos: Some more minor changes to prepare for Python 3.
39
try:
40
    import SocketServer as socketserver
41
except ImportError:
42
    import socketserver
3 by Björn Påhlsson
Python based server
43
import socket
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
44
import argparse
3 by Björn Påhlsson
Python based server
45
import datetime
46
import errno
749 by Teddy Hogeborn
mandos: Some more minor changes to prepare for Python 3.
47
try:
48
    import ConfigParser as configparser
49
except ImportError:
50
    import configparser
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
51
import sys
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
52
import re
53
import os
54
import signal
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
55
import subprocess
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
56
import atexit
57
import stat
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
58
import logging
59
import logging.handlers
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
60
import pwd
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
61
import contextlib
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
62
import struct
63
import fcntl
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
64
import functools
749 by Teddy Hogeborn
mandos: Some more minor changes to prepare for Python 3.
65
try:
66
    import cPickle as pickle
67
except ImportError:
68
    import pickle
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
69
import multiprocessing
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
70
import types
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
71
import binascii
72
import tempfile
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
73
import itertools
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
74
import collections
751 by Teddy Hogeborn
mandos: Use codecs.open() and print() for PID file.
75
import codecs
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
76
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
77
import dbus
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
78
import dbus.service
749 by Teddy Hogeborn
mandos: Some more minor changes to prepare for Python 3.
79
try:
80
    import gobject
81
except ImportError:
82
    from gi.repository import GObject as gobject
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
83
import avahi
84
from dbus.mainloop.glib import DBusGMainLoop
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
85
import ctypes
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
86
import ctypes.util
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
87
import xml.dom.minidom
88
import inspect
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
89
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
90
try:
91
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
92
except AttributeError:
93
    try:
94
        from IN import SO_BINDTODEVICE
95
    except ImportError:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
96
        SO_BINDTODEVICE = None
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
97
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
98
if sys.version_info.major == 2:
99
    str = unicode
100
237.4.59 by Teddy Hogeborn
* Makefile (version): Changed to "1.7.1".
101
version = "1.7.1"
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
102
stored_state_file = "clients.pickle"
13 by Björn Påhlsson
Added following support:
103
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
104
logger = logging.getLogger()
655 by Teddy Hogeborn
Allow self-tests to run without needing /dev/log.
105
syslogger = None
13 by Björn Påhlsson
Added following support:
106
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
107
try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
108
    if_nametoindex = ctypes.cdll.LoadLibrary(
109
        ctypes.util.find_library("c")).if_nametoindex
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
110
except (OSError, AttributeError):
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
111
    
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
112
    def if_nametoindex(interface):
113
        "Get an interface index the hard way, i.e. using fcntl()"
114
        SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
115
        with contextlib.closing(socket.socket()) as s:
116
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
117
                                struct.pack(b"16s16x", interface))
118
        interface_index = struct.unpack("I", ifreq[16:20])[0]
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
119
        return interface_index
120
121
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
122
def initlogger(debug, level=logging.WARNING):
518.1.4 by Björn Påhlsson
restructured logger
123
    """init logger and add loglevel"""
124
    
698 by Teddy Hogeborn
Fix mandos server "--servicename" option, broken since 1.6.4.
125
    global syslogger
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
126
    syslogger = (logging.handlers.SysLogHandler(
127
        facility = logging.handlers.SysLogHandler.LOG_DAEMON,
128
        address = "/dev/log"))
518.1.4 by Björn Påhlsson
restructured logger
129
    syslogger.setFormatter(logging.Formatter
130
                           ('Mandos [%(process)d]: %(levelname)s:'
131
                            ' %(message)s'))
132
    logger.addHandler(syslogger)
133
    
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
134
    if debug:
135
        console = logging.StreamHandler()
136
        console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
137
                                               ' [%(process)d]:'
138
                                               ' %(levelname)s:'
139
                                               ' %(message)s'))
140
        logger.addHandler(console)
518.1.4 by Björn Påhlsson
restructured logger
141
    logger.setLevel(level)
28 by Teddy Hogeborn
* server.conf: New file.
142
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
143
518.1.9 by Björn Påhlsson
renamed variables
144
class PGPError(Exception):
145
    """Exception if encryption/decryption fails"""
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
146
    pass
147
148
518.1.9 by Björn Påhlsson
renamed variables
149
class PGPEngine(object):
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
150
    """A simple class for OpenPGP symmetric encryption & decryption"""
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
151
    
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
152
    def __init__(self):
153
        self.tempdir = tempfile.mkdtemp(prefix="mandos-")
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
154
        self.gnupgargs = ['--batch',
155
                          '--home', self.tempdir,
156
                          '--force-mdc',
157
                          '--quiet',
158
                          '--no-use-agent']
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
159
    
160
    def __enter__(self):
161
        return self
162
    
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
163
    def __exit__(self, exc_type, exc_value, traceback):
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
164
        self._cleanup()
165
        return False
166
    
167
    def __del__(self):
168
        self._cleanup()
169
    
170
    def _cleanup(self):
171
        if self.tempdir is not None:
172
            # Delete contents of tempdir
173
            for root, dirs, files in os.walk(self.tempdir,
174
                                             topdown = False):
175
                for filename in files:
176
                    os.remove(os.path.join(root, filename))
177
                for dirname in dirs:
178
                    os.rmdir(os.path.join(root, dirname))
179
            # Remove tempdir
180
            os.rmdir(self.tempdir)
181
            self.tempdir = None
182
    
183
    def password_encode(self, password):
184
        # Passphrase can not be empty and can not contain newlines or
185
        # NUL bytes.  So we prefix it and hex encode it.
630 by Teddy Hogeborn
* mandos (PGPEngine.password_encode): Bug fix: GnuPG can't handle
186
        encoded = b"mandos" + binascii.hexlify(password)
187
        if len(encoded) > 2048:
188
            # GnuPG can't handle long passwords, so encode differently
189
            encoded = (b"mandos" + password.replace(b"\\", b"\\\\")
190
                       .replace(b"\n", b"\\n")
191
                       .replace(b"\0", b"\\x00"))
192
        return encoded
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
193
    
194
    def encrypt(self, data, password):
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
195
        passphrase = self.password_encode(password)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
196
        with tempfile.NamedTemporaryFile(
197
                dir=self.tempdir) as passfile:
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
198
            passfile.write(passphrase)
199
            passfile.flush()
200
            proc = subprocess.Popen(['gpg', '--symmetric',
201
                                     '--passphrase-file',
202
                                     passfile.name]
203
                                    + self.gnupgargs,
204
                                    stdin = subprocess.PIPE,
205
                                    stdout = subprocess.PIPE,
206
                                    stderr = subprocess.PIPE)
207
            ciphertext, err = proc.communicate(input = data)
208
        if proc.returncode != 0:
209
            raise PGPError(err)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
210
        return ciphertext
211
    
212
    def decrypt(self, data, password):
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
213
        passphrase = self.password_encode(password)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
214
        with tempfile.NamedTemporaryFile(
215
                dir = self.tempdir) as passfile:
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
216
            passfile.write(passphrase)
217
            passfile.flush()
218
            proc = subprocess.Popen(['gpg', '--decrypt',
219
                                     '--passphrase-file',
220
                                     passfile.name]
221
                                    + self.gnupgargs,
222
                                    stdin = subprocess.PIPE,
223
                                    stdout = subprocess.PIPE,
224
                                    stderr = subprocess.PIPE)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
225
            decrypted_plaintext, err = proc.communicate(input = data)
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
226
        if proc.returncode != 0:
227
            raise PGPError(err)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
228
        return decrypted_plaintext
229
230
28 by Teddy Hogeborn
* server.conf: New file.
231
class AvahiError(Exception):
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
232
    def __init__(self, value, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
233
        self.value = value
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
234
        return super(AvahiError, self).__init__(value, *args,
235
                                                **kwargs)
28 by Teddy Hogeborn
* server.conf: New file.
236
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
237
28 by Teddy Hogeborn
* server.conf: New file.
238
class AvahiServiceError(AvahiError):
239
    pass
240
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
241
28 by Teddy Hogeborn
* server.conf: New file.
242
class AvahiGroupError(AvahiError):
243
    pass
244
245
246
class AvahiService(object):
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
247
    """An Avahi (Zeroconf) service.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
248
    
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
249
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
250
    interface: integer; avahi.IF_UNSPEC or an interface index.
251
               Used to optionally bind to the specified interface.
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
252
    name: string; Example: 'Mandos'
253
    type: string; Example: '_mandos._tcp'.
614 by Teddy Hogeborn
* mandos: Comment changes.
254
     See <https://www.iana.org/assignments/service-names-port-numbers>
45 by Teddy Hogeborn
* server.py: Cosmetic changes.
255
    port: integer; what port to announce
256
    TXT: list of strings; TXT record for the service
257
    domain: string; Domain to publish on, default to .local if empty.
258
    host: string; Host to publish records for, default is localhost
259
    max_renames: integer; maximum number of renames
260
    rename_count: integer; counter so we only rename after collisions
261
                  a sensible number of times
336 by Teddy Hogeborn
Code cleanup.
262
    group: D-Bus Entry Group
263
    server: D-Bus Server
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
264
    bus: dbus.SystemBus()
28 by Teddy Hogeborn
* server.conf: New file.
265
    """
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
266
    
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
267
    def __init__(self,
268
                 interface = avahi.IF_UNSPEC,
269
                 name = None,
270
                 servicetype = None,
271
                 port = None,
272
                 TXT = None,
273
                 domain = "",
274
                 host = "",
275
                 max_renames = 32768,
276
                 protocol = avahi.PROTO_UNSPEC,
277
                 bus = None):
28 by Teddy Hogeborn
* server.conf: New file.
278
        self.interface = interface
279
        self.name = name
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
280
        self.type = servicetype
28 by Teddy Hogeborn
* server.conf: New file.
281
        self.port = port
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
282
        self.TXT = TXT if TXT is not None else []
28 by Teddy Hogeborn
* server.conf: New file.
283
        self.domain = domain
284
        self.host = host
285
        self.rename_count = 0
76 by Teddy Hogeborn
* plugins.d/password-request.c (init_gnutls_global): Renamed
286
        self.max_renames = max_renames
314 by Teddy Hogeborn
Support not using IPv6 in server:
287
        self.protocol = protocol
336 by Teddy Hogeborn
Code cleanup.
288
        self.group = None       # our entry group
289
        self.server = None
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
290
        self.bus = bus
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
291
        self.entry_group_state_changed_match = None
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
292
    
738 by Teddy Hogeborn
Handle local Zeroconf service name collisions.
293
    def rename(self, remove=True):
28 by Teddy Hogeborn
* server.conf: New file.
294
        """Derived from the Avahi example code"""
295
        if self.rename_count >= self.max_renames:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
296
            logger.critical("No suitable Zeroconf service name found"
297
                            " after %i retries, exiting.",
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
298
                            self.rename_count)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
299
            raise AvahiServiceError("Too many renames")
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
300
        self.name = str(
301
            self.server.GetAlternativeServiceName(self.name))
738 by Teddy Hogeborn
Handle local Zeroconf service name collisions.
302
        self.rename_count += 1
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
303
        logger.info("Changing Zeroconf service name to %r ...",
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
304
                    self.name)
738 by Teddy Hogeborn
Handle local Zeroconf service name collisions.
305
        if remove:
306
            self.remove()
24.1.160 by Björn Påhlsson
fixed bug with local name collisions after a non-local name collision
307
        try:
308
            self.add()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
309
        except dbus.exceptions.DBusException as error:
738 by Teddy Hogeborn
Handle local Zeroconf service name collisions.
310
            if (error.get_dbus_name()
311
                == "org.freedesktop.Avahi.CollisionError"):
312
                logger.info("Local Zeroconf service name collision.")
313
                return self.rename(remove=False)
314
            else:
315
                logger.critical("D-Bus Exception", exc_info=error)
316
                self.cleanup()
317
                os._exit(1)
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
318
    
28 by Teddy Hogeborn
* server.conf: New file.
319
    def remove(self):
320
        """Derived from the Avahi example code"""
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
321
        if self.entry_group_state_changed_match is not None:
322
            self.entry_group_state_changed_match.remove()
323
            self.entry_group_state_changed_match = None
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
324
        if self.group is not None:
325
            self.group.Reset()
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
326
    
28 by Teddy Hogeborn
* server.conf: New file.
327
    def add(self):
328
        """Derived from the Avahi example code"""
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
329
        self.remove()
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
330
        if self.group is None:
331
            self.group = dbus.Interface(
332
                self.bus.get_object(avahi.DBUS_NAME,
333
                                    self.server.EntryGroupNew()),
334
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
335
        self.entry_group_state_changed_match = (
336
            self.group.connect_to_signal(
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
337
                'StateChanged', self.entry_group_state_changed))
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
338
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
336 by Teddy Hogeborn
Code cleanup.
339
                     self.name, self.type)
340
        self.group.AddService(
341
            self.interface,
342
            self.protocol,
343
            dbus.UInt32(0),     # flags
344
            self.name, self.type,
345
            self.domain, self.host,
346
            dbus.UInt16(self.port),
347
            avahi.string_array_to_txt_array(self.TXT))
348
        self.group.Commit()
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
349
    
336 by Teddy Hogeborn
Code cleanup.
350
    def entry_group_state_changed(self, state, error):
351
        """Derived from the Avahi example code"""
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
352
        logger.debug("Avahi entry group state change: %i", state)
336 by Teddy Hogeborn
Code cleanup.
353
        
354
        if state == avahi.ENTRY_GROUP_ESTABLISHED:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
355
            logger.debug("Zeroconf service established.")
336 by Teddy Hogeborn
Code cleanup.
356
        elif state == avahi.ENTRY_GROUP_COLLISION:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
357
            logger.info("Zeroconf service name collision.")
336 by Teddy Hogeborn
Code cleanup.
358
            self.rename()
359
        elif state == avahi.ENTRY_GROUP_FAILURE:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
360
            logger.critical("Avahi: Error in group state changed %s",
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
361
                            str(error))
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
362
            raise AvahiGroupError("State changed: {!s}".format(error))
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
363
    
336 by Teddy Hogeborn
Code cleanup.
364
    def cleanup(self):
365
        """Derived from the Avahi example code"""
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
366
        if self.group is not None:
367
            try:
368
                self.group.Free()
369
            except (dbus.exceptions.UnknownMethodException,
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
370
                    dbus.exceptions.DBusException):
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
371
                pass
372
            self.group = None
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
373
        self.remove()
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
374
    
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
375
    def server_state_changed(self, state, error=None):
336 by Teddy Hogeborn
Code cleanup.
376
        """Derived from the Avahi example code"""
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
377
        logger.debug("Avahi server state change: %i", state)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
378
        bad_states = {
379
            avahi.SERVER_INVALID: "Zeroconf server invalid",
380
            avahi.SERVER_REGISTERING: None,
381
            avahi.SERVER_COLLISION: "Zeroconf server name collision",
382
            avahi.SERVER_FAILURE: "Zeroconf server failure",
383
        }
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
384
        if state in bad_states:
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
385
            if bad_states[state] is not None:
386
                if error is None:
387
                    logger.error(bad_states[state])
388
                else:
389
                    logger.error(bad_states[state] + ": %r", error)
390
            self.cleanup()
336 by Teddy Hogeborn
Code cleanup.
391
        elif state == avahi.SERVER_RUNNING:
769 by Teddy Hogeborn
Handle local Zeroconf service name collisions on startup, too.
392
            try:
393
                self.add()
394
            except dbus.exceptions.DBusException as error:
395
                if (error.get_dbus_name()
396
                    == "org.freedesktop.Avahi.CollisionError"):
397
                    logger.info("Local Zeroconf service name"
398
                                " collision.")
399
                    return self.rename(remove=False)
400
                else:
401
                    logger.critical("D-Bus Exception", exc_info=error)
402
                    self.cleanup()
403
                    os._exit(1)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
404
        else:
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
405
            if error is None:
406
                logger.debug("Unknown state: %r", state)
407
            else:
408
                logger.debug("Unknown state: %r: %r", state, error)
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
409
    
336 by Teddy Hogeborn
Code cleanup.
410
    def activate(self):
411
        """Derived from the Avahi example code"""
412
        if self.server is None:
413
            self.server = dbus.Interface(
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
414
                self.bus.get_object(avahi.DBUS_NAME,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
415
                                    avahi.DBUS_PATH_SERVER,
416
                                    follow_name_owner_changes=True),
336 by Teddy Hogeborn
Code cleanup.
417
                avahi.DBUS_INTERFACE_SERVER)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
418
        self.server.connect_to_signal("StateChanged",
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
419
                                      self.server_state_changed)
336 by Teddy Hogeborn
Code cleanup.
420
        self.server_state_changed(self.server.GetState())
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
421
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
422
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
423
class AvahiServiceToSyslog(AvahiService):
738 by Teddy Hogeborn
Handle local Zeroconf service name collisions.
424
    def rename(self, *args, **kwargs):
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
425
        """Add the new name to the syslog messages"""
738 by Teddy Hogeborn
Handle local Zeroconf service name collisions.
426
        ret = AvahiService.rename(self, *args, **kwargs)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
427
        syslogger.setFormatter(logging.Formatter(
428
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
429
            .format(self.name)))
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
430
        return ret
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
431
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
432
# Pretend that we have a GnuTLS module
433
class GnuTLS(object):
434
    """This isn't so much a class as it is a module-like namespace.
435
    It is instantiated once, and simulates having a GnuTLS module."""
436
    
437
    _library = ctypes.cdll.LoadLibrary(
438
        ctypes.util.find_library("gnutls"))
439
    _need_version = "3.3.0"
440
    def __init__(self):
441
        # Need to use class name "GnuTLS" here, since this method is
442
        # called before the assignment to the "gnutls" global variable
443
        # happens.
444
        if GnuTLS.check_version(self._need_version) is None:
445
            raise GnuTLS.Error("Needs GnuTLS {} or later"
446
                               .format(self._need_version))
447
    
448
    # Unless otherwise indicated, the constants and types below are
449
    # all from the gnutls/gnutls.h C header file.
450
    
451
    # Constants
452
    E_SUCCESS = 0
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
453
    E_INTERRUPTED = -52
454
    E_AGAIN = -28
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
455
    CRT_OPENPGP = 2
456
    CLIENT = 2
457
    SHUT_RDWR = 0
458
    CRD_CERTIFICATE = 1
459
    E_NO_CERTIFICATE_FOUND = -49
460
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
461
    
462
    # Types
463
    class session_int(ctypes.Structure):
464
        _fields_ = []
465
    session_t = ctypes.POINTER(session_int)
466
    class certificate_credentials_st(ctypes.Structure):
467
        _fields_ = []
468
    certificate_credentials_t = ctypes.POINTER(
469
        certificate_credentials_st)
470
    certificate_type_t = ctypes.c_int
471
    class datum_t(ctypes.Structure):
472
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
473
                    ('size', ctypes.c_uint)]
474
    class openpgp_crt_int(ctypes.Structure):
475
        _fields_ = []
476
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
477
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
478
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
479
    credentials_type_t = ctypes.c_int # 
480
    transport_ptr_t = ctypes.c_void_p
481
    close_request_t = ctypes.c_int
482
    
483
    # Exceptions
484
    class Error(Exception):
485
        # We need to use the class name "GnuTLS" here, since this
486
        # exception might be raised from within GnuTLS.__init__,
487
        # which is called before the assignment to the "gnutls"
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
488
        # global variable has happened.
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
489
        def __init__(self, message = None, code = None, args=()):
490
            # Default usage is by a message string, but if a return
491
            # code is passed, convert it to a string with
492
            # gnutls.strerror()
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
493
            self.code = code
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
494
            if message is None and code is not None:
495
                message = GnuTLS.strerror(code)
496
            return super(GnuTLS.Error, self).__init__(
497
                message, *args)
498
    
499
    class CertificateSecurityError(Error):
500
        pass
501
    
502
    # Classes
503
    class Credentials(object):
504
        def __init__(self):
505
            self._c_object = gnutls.certificate_credentials_t()
506
            gnutls.certificate_allocate_credentials(
507
                ctypes.byref(self._c_object))
508
            self.type = gnutls.CRD_CERTIFICATE
509
        
510
        def __del__(self):
511
            gnutls.certificate_free_credentials(self._c_object)
512
    
513
    class ClientSession(object):
514
        def __init__(self, socket, credentials = None):
515
            self._c_object = gnutls.session_t()
516
            gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT)
517
            gnutls.set_default_priority(self._c_object)
518
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
519
            gnutls.handshake_set_private_extensions(self._c_object,
520
                                                    True)
521
            self.socket = socket
522
            if credentials is None:
523
                credentials = gnutls.Credentials()
524
            gnutls.credentials_set(self._c_object, credentials.type,
525
                                   ctypes.cast(credentials._c_object,
526
                                               ctypes.c_void_p))
527
            self.credentials = credentials
528
        
529
        def __del__(self):
530
            gnutls.deinit(self._c_object)
531
        
532
        def handshake(self):
533
            return gnutls.handshake(self._c_object)
534
        
535
        def send(self, data):
536
            data = bytes(data)
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
537
            data_len = len(data)
538
            while data_len > 0:
539
                data_len -= gnutls.record_send(self._c_object,
540
                                               data[-data_len:],
541
                                               data_len)
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
542
        
543
        def bye(self):
544
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
545
    
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
546
    # Error handling functions
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
547
    def _error_code(result):
548
        """A function to raise exceptions on errors, suitable
549
        for the 'restype' attribute on ctypes functions"""
550
        if result >= 0:
551
            return result
552
        if result == gnutls.E_NO_CERTIFICATE_FOUND:
553
            raise gnutls.CertificateSecurityError(code = result)
554
        raise gnutls.Error(code = result)
555
    
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
556
    def _retry_on_error(result, func, arguments):
557
        """A function to retry on some errors, suitable
558
        for the 'errcheck' attribute on ctypes functions"""
559
        while result < 0:
560
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
561
                return _error_code(result)
562
            result = func(*arguments)
563
        return result
564
    
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
565
    # Unless otherwise indicated, the function declarations below are
566
    # all from the gnutls/gnutls.h C header file.
567
    
568
    # Functions
569
    priority_set_direct = _library.gnutls_priority_set_direct
570
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
571
                                    ctypes.POINTER(ctypes.c_char_p)]
572
    priority_set_direct.restype = _error_code
573
    
574
    init = _library.gnutls_init
575
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
576
    init.restype = _error_code
577
    
578
    set_default_priority = _library.gnutls_set_default_priority
579
    set_default_priority.argtypes = [session_t]
580
    set_default_priority.restype = _error_code
581
    
582
    record_send = _library.gnutls_record_send
583
    record_send.argtypes = [session_t, ctypes.c_void_p,
584
                            ctypes.c_size_t]
585
    record_send.restype = ctypes.c_ssize_t
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
586
    record_send.errcheck = _retry_on_error
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
587
    
588
    certificate_allocate_credentials = (
589
        _library.gnutls_certificate_allocate_credentials)
590
    certificate_allocate_credentials.argtypes = [
591
        ctypes.POINTER(certificate_credentials_t)]
592
    certificate_allocate_credentials.restype = _error_code
593
    
594
    certificate_free_credentials = (
595
        _library.gnutls_certificate_free_credentials)
596
    certificate_free_credentials.argtypes = [certificate_credentials_t]
597
    certificate_free_credentials.restype = None
598
    
599
    handshake_set_private_extensions = (
600
        _library.gnutls_handshake_set_private_extensions)
601
    handshake_set_private_extensions.argtypes = [session_t,
602
                                                 ctypes.c_int]
603
    handshake_set_private_extensions.restype = None
604
    
605
    credentials_set = _library.gnutls_credentials_set
606
    credentials_set.argtypes = [session_t, credentials_type_t,
607
                                ctypes.c_void_p]
608
    credentials_set.restype = _error_code
609
    
610
    strerror = _library.gnutls_strerror
611
    strerror.argtypes = [ctypes.c_int]
612
    strerror.restype = ctypes.c_char_p
613
    
614
    certificate_type_get = _library.gnutls_certificate_type_get
615
    certificate_type_get.argtypes = [session_t]
616
    certificate_type_get.restype = _error_code
617
    
618
    certificate_get_peers = _library.gnutls_certificate_get_peers
619
    certificate_get_peers.argtypes = [session_t,
620
                                      ctypes.POINTER(ctypes.c_uint)]
621
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
622
    
623
    global_set_log_level = _library.gnutls_global_set_log_level
624
    global_set_log_level.argtypes = [ctypes.c_int]
625
    global_set_log_level.restype = None
626
    
627
    global_set_log_function = _library.gnutls_global_set_log_function
628
    global_set_log_function.argtypes = [log_func]
629
    global_set_log_function.restype = None
630
    
631
    deinit = _library.gnutls_deinit
632
    deinit.argtypes = [session_t]
633
    deinit.restype = None
634
    
635
    handshake = _library.gnutls_handshake
636
    handshake.argtypes = [session_t]
637
    handshake.restype = _error_code
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
638
    handshake.errcheck = _retry_on_error
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
639
    
640
    transport_set_ptr = _library.gnutls_transport_set_ptr
641
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
642
    transport_set_ptr.restype = None
643
    
644
    bye = _library.gnutls_bye
645
    bye.argtypes = [session_t, close_request_t]
646
    bye.restype = _error_code
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
647
    bye.errcheck = _retry_on_error
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
648
    
649
    check_version = _library.gnutls_check_version
650
    check_version.argtypes = [ctypes.c_char_p]
651
    check_version.restype = ctypes.c_char_p
652
    
653
    # All the function declarations below are from gnutls/openpgp.h
654
    
655
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
656
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
657
    openpgp_crt_init.restype = _error_code
658
    
659
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
660
    openpgp_crt_import.argtypes = [openpgp_crt_t,
661
                                   ctypes.POINTER(datum_t),
662
                                   openpgp_crt_fmt_t]
663
    openpgp_crt_import.restype = _error_code
664
    
665
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
666
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
667
                                        ctypes.POINTER(ctypes.c_uint)]
668
    openpgp_crt_verify_self.restype = _error_code
669
    
670
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
671
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
672
    openpgp_crt_deinit.restype = None
673
    
674
    openpgp_crt_get_fingerprint = (
675
        _library.gnutls_openpgp_crt_get_fingerprint)
676
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
677
                                            ctypes.c_void_p,
678
                                            ctypes.POINTER(
679
                                                ctypes.c_size_t)]
680
    openpgp_crt_get_fingerprint.restype = _error_code
681
    
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
682
    # Remove non-public functions
683
    del _error_code, _retry_on_error
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
684
# Create the global "gnutls" object, simulating a module
685
gnutls = GnuTLS()
686
749.1.2 by Teddy Hogeborn
mandos (subprocess_call_pipe): Rename to "call_pipe" and take "func".
687
def call_pipe(connection,       # : multiprocessing.Connection
688
              func, *args, **kwargs):
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
689
    """This function is meant to be called by multiprocessing.Process
690
    
749.1.2 by Teddy Hogeborn
mandos (subprocess_call_pipe): Rename to "call_pipe" and take "func".
691
    This function runs func(*args, **kwargs), and writes the resulting
692
    return value on the provided multiprocessing.Connection.
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
693
    """
749.1.2 by Teddy Hogeborn
mandos (subprocess_call_pipe): Rename to "call_pipe" and take "func".
694
    connection.send(func(*args, **kwargs))
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
695
    connection.close()
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
696
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
697
class Client(object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
698
    """A representation of a client host served by this server.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
699
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
700
    Attributes:
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
701
    approved:   bool(); 'None' if not yet approved/disapproved
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
702
    approval_delay: datetime.timedelta(); Time to wait for approval
703
    approval_duration: datetime.timedelta(); Duration of one approval
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
704
    checker:    subprocess.Popen(); a running checker process used
705
                                    to see if the client lives.
706
                                    'None' if no process is running.
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
707
    checker_callback_tag: a gobject event source tag, or None
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
708
    checker_command: string; External command which is run to check
709
                     if client lives.  %() expansions are done at
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
710
                     runtime with vars(self) as dict, so that for
711
                     instance %(name)s can be used in the command.
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
712
    checker_initiator_tag: a gobject event source tag, or None
713
    created:    datetime.datetime(); (UTC) object creation
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
714
    client_structure: Object describing what attributes a client has
715
                      and is used for storing the client at exit
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
716
    current_checker_command: string; current running checker_command
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
717
    disable_initiator_tag: a gobject event source tag, or None
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
718
    enabled:    bool()
719
    fingerprint: string (40 or 32 hexadecimal digits); used to
720
                 uniquely identify the client
721
    host:       string; available for use by the checker command
722
    interval:   datetime.timedelta(); How often to start a new checker
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
723
    last_approval_request: datetime.datetime(); (UTC) or None
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
724
    last_checked_ok: datetime.datetime(); (UTC) or None
521 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-crypto".
725
    last_checker_status: integer between 0 and 255 reflecting exit
518.1.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
726
                         status of last checker. -1 reflects crashed
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
727
                         checker, -2 means no checker completed yet.
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
728
    last_checker_signal: The signal which killed the last checker, if
729
                         last_checker_status is -1
518.2.3 by Teddy Hogeborn
Make "enabled" a client config option.
730
    last_enabled: datetime.datetime(); (UTC) or None
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
731
    name:       string; from the config file, used in log messages and
732
                        D-Bus identifiers
733
    secret:     bytestring; sent verbatim (over TLS) to client
734
    timeout:    datetime.timedelta(); How long from last_checked_ok
735
                                      until this client is disabled
552 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
736
    extended_timeout:   extra long timeout when secret has been sent
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
737
    runtime_expansions: Allowed attributes for runtime expansion.
497 by Teddy Hogeborn
* DBUS-API: Document new "Expires" and "ExtendedTimeout" properties.
738
    expires:    datetime.datetime(); time (UTC) when a client will be
24.1.179 by Björn Påhlsson
New feature:
739
                disabled, or None
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
740
    server_settings: The server_settings dict from main()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
741
    """
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
742
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
743
    runtime_expansions = ("approval_delay", "approval_duration",
584 by Teddy Hogeborn
* mandos (Client.runtime_expansions): Add "expires" and (bug fix)
744
                          "created", "enabled", "expires",
745
                          "fingerprint", "host", "interval",
746
                          "last_approval_request", "last_checked_ok",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
747
                          "last_enabled", "name", "timeout")
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
748
    client_defaults = {
749
        "timeout": "PT5M",
750
        "extended_timeout": "PT15M",
751
        "interval": "PT2M",
752
        "checker": "fping -q -- %%(host)s",
753
        "host": "",
754
        "approval_delay": "PT0S",
755
        "approval_duration": "PT1S",
756
        "approved_by_default": "True",
757
        "enabled": "True",
758
    }
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
759
    
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
760
    @staticmethod
761
    def config_parser(config):
542 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
762
        """Construct a new dict of client settings of this form:
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
763
        { client_name: {setting_name: value, ...}, ...}
542 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
764
        with exceptions for any special settings as defined above.
765
        NOTE: Must be a pure function. Must return the same result
766
        value given the same arguments.
767
        """
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
768
        settings = {}
769
        for client_name in config.sections():
770
            section = dict(config.items(client_name))
771
            client = settings[client_name] = {}
772
            
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
773
            client["host"] = section["host"]
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
774
            # Reformat values from string types to Python types
775
            client["approved_by_default"] = config.getboolean(
776
                client_name, "approved_by_default")
543 by Teddy Hogeborn
* mandos: Break some long lines.
777
            client["enabled"] = config.getboolean(client_name,
778
                                                  "enabled")
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
779
            
739 by Teddy Hogeborn
mandos (Client.config_parser): Move comment to correct place.
780
            # Uppercase and remove spaces from fingerprint for later
781
            # comparison purposes with return value from the
782
            # fingerprint() function
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
783
            client["fingerprint"] = (section["fingerprint"].upper()
784
                                     .replace(" ", ""))
785
            if "secret" in section:
786
                client["secret"] = section["secret"].decode("base64")
787
            elif "secfile" in section:
788
                with open(os.path.expanduser(os.path.expandvars
789
                                             (section["secfile"])),
790
                          "rb") as secfile:
791
                    client["secret"] = secfile.read()
792
            else:
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
793
                raise TypeError("No secret or secfile for section {}"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
794
                                .format(section))
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
795
            client["timeout"] = string_to_delta(section["timeout"])
796
            client["extended_timeout"] = string_to_delta(
797
                section["extended_timeout"])
798
            client["interval"] = string_to_delta(section["interval"])
799
            client["approval_delay"] = string_to_delta(
800
                section["approval_delay"])
801
            client["approval_duration"] = string_to_delta(
802
                section["approval_duration"])
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
803
            client["checker_command"] = section["checker"]
804
            client["last_approval_request"] = None
805
            client["last_checked_ok"] = None
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
806
            client["last_checker_status"] = -2
542 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
807
        
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
808
        return settings
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
809
    
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
810
    def __init__(self, settings, name = None, server_settings=None):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
811
        self.name = name
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
812
        if server_settings is None:
813
            server_settings = {}
814
        self.server_settings = server_settings
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
815
        # adding all client settings
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
816
        for setting, value in settings.items():
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
817
            setattr(self, setting, value)
818
        
542 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
819
        if self.enabled:
820
            if not hasattr(self, "last_enabled"):
821
                self.last_enabled = datetime.datetime.utcnow()
822
            if not hasattr(self, "expires"):
823
                self.expires = (datetime.datetime.utcnow()
824
                                + self.timeout)
825
        else:
826
            self.last_enabled = None
827
            self.expires = None
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
828
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
829
        logger.debug("Creating client %r", self.name)
830
        logger.debug("  Fingerprint: %s", self.fingerprint)
543 by Teddy Hogeborn
* mandos: Break some long lines.
831
        self.created = settings.get("created",
832
                                    datetime.datetime.utcnow())
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
833
        
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
834
        # attributes specific for this server instance
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
835
        self.checker = None
836
        self.checker_initiator_tag = None
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
837
        self.disable_initiator_tag = None
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
838
        self.checker_callback_tag = None
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
839
        self.current_checker_command = None
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
840
        self.approved = None
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
841
        self.approvals_pending = 0
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
842
        self.changedstate = multiprocessing_manager.Condition(
843
            multiprocessing_manager.Lock())
844
        self.client_structure = [attr
845
                                 for attr in self.__dict__.iterkeys()
521 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-crypto".
846
                                 if not attr.startswith("_")]
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
847
        self.client_structure.append("client_structure")
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
848
        
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
849
        for name, t in inspect.getmembers(
850
                type(self), lambda obj: isinstance(obj, property)):
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
851
            if not name.startswith("_"):
852
                self.client_structure.append(name)
853
    
520 by Björn Påhlsson
merge persistent state
854
    # Send notice to process children that client state has changed
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
855
    def send_changedstate(self):
520 by Björn Påhlsson
merge persistent state
856
        with self.changedstate:
857
            self.changedstate.notify_all()
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
858
    
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
859
    def enable(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
860
        """Start this client's checker and timeout hooks"""
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
861
        if getattr(self, "enabled", False):
341 by Teddy Hogeborn
Code cleanup and one bug fix.
862
            # Already enabled
863
            return
24.1.179 by Björn Påhlsson
New feature:
864
        self.expires = datetime.datetime.utcnow() + self.timeout
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
865
        self.enabled = True
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
866
        self.last_enabled = datetime.datetime.utcnow()
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
867
        self.init_checker()
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
868
        self.send_changedstate()
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
869
    
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
870
    def disable(self, quiet=True):
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
871
        """Disable this client."""
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
872
        if not getattr(self, "enabled", False):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
873
            return False
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
874
        if not quiet:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
875
            logger.info("Disabling client %s", self.name)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
876
        if getattr(self, "disable_initiator_tag", None) is not None:
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
877
            gobject.source_remove(self.disable_initiator_tag)
878
            self.disable_initiator_tag = None
24.1.179 by Björn Påhlsson
New feature:
879
        self.expires = None
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
880
        if getattr(self, "checker_initiator_tag", None) is not None:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
881
            gobject.source_remove(self.checker_initiator_tag)
882
            self.checker_initiator_tag = None
883
        self.stop_checker()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
884
        self.enabled = False
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
885
        if not quiet:
886
            self.send_changedstate()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
887
        # Do not run this again if called by a gobject.timeout_add
888
        return False
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
889
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
890
    def __del__(self):
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
891
        self.disable()
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
892
    
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
893
    def init_checker(self):
894
        # Schedule a new checker to be started an 'interval' from now,
895
        # and every interval from then on.
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
896
        if self.checker_initiator_tag is not None:
897
            gobject.source_remove(self.checker_initiator_tag)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
898
        self.checker_initiator_tag = gobject.timeout_add(
899
            int(self.interval.total_seconds() * 1000),
900
            self.start_checker)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
901
        # Schedule a disable() when 'timeout' has passed
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
902
        if self.disable_initiator_tag is not None:
903
            gobject.source_remove(self.disable_initiator_tag)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
904
        self.disable_initiator_tag = gobject.timeout_add(
905
            int(self.timeout.total_seconds() * 1000), self.disable)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
906
        # Also start a new checker *right now*.
907
        self.start_checker()
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
908
    
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
909
    def checker_callback(self, source, condition, connection,
910
                         command):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
911
        """The checker has completed, so take appropriate actions."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
912
        self.checker_callback_tag = None
913
        self.checker = None
749.1.2 by Teddy Hogeborn
mandos (subprocess_call_pipe): Rename to "call_pipe" and take "func".
914
        # Read return code from connection (see call_pipe)
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
915
        returncode = connection.recv()
916
        connection.close()
917
        
918
        if returncode >= 0:
919
            self.last_checker_status = returncode
920
            self.last_checker_signal = None
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
921
            if self.last_checker_status == 0:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
922
                logger.info("Checker for %(name)s succeeded",
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
923
                            vars(self))
281 by Teddy Hogeborn
* mandos (Client.bump_timeout): Renamed to "checked_ok". All callers
924
                self.checked_ok()
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
925
            else:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
926
                logger.info("Checker for %(name)s failed", vars(self))
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
927
        else:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
928
            self.last_checker_status = -1
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
929
            self.last_checker_signal = -returncode
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
930
            logger.warning("Checker for %(name)s crashed?",
13 by Björn Påhlsson
Added following support:
931
                           vars(self))
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
932
        return False
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
933
    
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
934
    def checked_ok(self):
935
        """Assert that the client has been seen, alive and well."""
936
        self.last_checked_ok = datetime.datetime.utcnow()
937
        self.last_checker_status = 0
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
938
        self.last_checker_signal = None
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
939
        self.bump_timeout()
940
    
941
    def bump_timeout(self, timeout=None):
942
        """Bump up the timeout for this client."""
24.1.179 by Björn Påhlsson
New feature:
943
        if timeout is None:
944
            timeout = self.timeout
505.1.17 by Teddy Hogeborn
* mandos (Client.checked_ok): Bug fix: Handle disabled client.
945
        if self.disable_initiator_tag is not None:
946
            gobject.source_remove(self.disable_initiator_tag)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
947
            self.disable_initiator_tag = None
505.1.17 by Teddy Hogeborn
* mandos (Client.checked_ok): Bug fix: Handle disabled client.
948
        if getattr(self, "enabled", False):
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
949
            self.disable_initiator_tag = gobject.timeout_add(
950
                int(timeout.total_seconds() * 1000), self.disable)
505.1.17 by Teddy Hogeborn
* mandos (Client.checked_ok): Bug fix: Handle disabled client.
951
            self.expires = datetime.datetime.utcnow() + timeout
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
952
    
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
953
    def need_approval(self):
954
        self.last_approval_request = datetime.datetime.utcnow()
955
    
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
956
    def start_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
957
        """Start a new checker subprocess if one is not running.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
958
        
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
959
        If a checker already exists, leave it running and do
960
        nothing."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
961
        # The reason for not killing a running checker is that if we
577 by Teddy Hogeborn
* mandos (Client.start_checker): Reworded comment.
962
        # did that, and if a checker (for some reason) started running
963
        # slowly and taking more than 'interval' time, then the client
964
        # would inevitably timeout, since no checker would get a
965
        # chance to run to completion.  If we instead leave running
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
966
        # checkers alone, the checker would have to take more time
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
967
        # than 'timeout' for the client to be disabled, which is as it
968
        # should be.
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
969
        
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
970
        if self.checker is not None and not self.checker.is_alive():
971
            logger.warning("Checker was not alive; joining")
972
            self.checker.join()
973
            self.checker = None
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
974
        # Start a new checker if needed
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
975
        if self.checker is None:
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
976
            # Escape attributes for the shell
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
977
            escaped_attrs = {
978
                attr: re.escape(str(getattr(self, attr)))
979
                for attr in self.runtime_expansions }
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
980
            try:
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
981
                command = self.checker_command % escaped_attrs
982
            except TypeError as error:
983
                logger.error('Could not format string "%s"',
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
984
                             self.checker_command,
985
                             exc_info=error)
986
                return True     # Try again later
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
987
            self.current_checker_command = command
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
988
            logger.info("Starting checker %r for %s", command,
989
                        self.name)
990
            # We don't need to redirect stdout and stderr, since
991
            # in normal mode, that is already done by daemon(),
992
            # and in debug mode we don't want to.  (Stdin is
993
            # always replaced by /dev/null.)
994
            # The exception is when not debugging but nevertheless
995
            # running in the foreground; use the previously
996
            # created wnull.
997
            popen_args = { "close_fds": True,
998
                           "shell": True,
999
                           "cwd": "/" }
1000
            if (not self.server_settings["debug"]
1001
                and self.server_settings["foreground"]):
1002
                popen_args.update({"stdout": wnull,
1003
                                   "stderr": wnull })
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
1004
            pipe = multiprocessing.Pipe(duplex = False)
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1005
            self.checker = multiprocessing.Process(
749.1.2 by Teddy Hogeborn
mandos (subprocess_call_pipe): Rename to "call_pipe" and take "func".
1006
                target = call_pipe,
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
1007
                args = (pipe[1], subprocess.call, command),
749.1.2 by Teddy Hogeborn
mandos (subprocess_call_pipe): Rename to "call_pipe" and take "func".
1008
                kwargs = popen_args)
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1009
            self.checker.start()
1010
            self.checker_callback_tag = gobject.io_add_watch(
1011
                pipe[0].fileno(), gobject.IO_IN,
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
1012
                self.checker_callback, pipe[0], command)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1013
        # Re-run this periodically if run by gobject.timeout_add
1014
        return True
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
1015
    
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
1016
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1017
        """Force the checker process, if any, to stop."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1018
        if self.checker_callback_tag:
1019
            gobject.source_remove(self.checker_callback_tag)
1020
            self.checker_callback_tag = None
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1021
        if getattr(self, "checker", None) is None:
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
1022
            return
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1023
        logger.debug("Stopping checker for %(name)s", vars(self))
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1024
        self.checker.terminate()
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
1025
        self.checker = None
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
1026
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1027
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1028
def dbus_service_property(dbus_interface,
1029
                          signature="v",
1030
                          access="readwrite",
1031
                          byte_arrays=False):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1032
    """Decorators for marking methods of a DBusObjectWithProperties to
1033
    become properties on the D-Bus.
1034
    
1035
    The decorated method will be called with no arguments by "Get"
1036
    and with one argument by "Set".
1037
    
1038
    The parameters, where they are supported, are the same as
1039
    dbus.service.method, except there is only "signature", since the
1040
    type from Get() and the type sent to Set() is the same.
1041
    """
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1042
    # Encoding deeply encoded byte arrays is not supported yet by the
1043
    # "Set" method, so we fail early here:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1044
    if byte_arrays and signature != "ay":
1045
        raise ValueError("Byte arrays not supported for non-'ay'"
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1046
                         " signature {!r}".format(signature))
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1047
    
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1048
    def decorator(func):
1049
        func._dbus_is_property = True
1050
        func._dbus_interface = dbus_interface
1051
        func._dbus_signature = signature
1052
        func._dbus_access = access
1053
        func._dbus_name = func.__name__
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1054
        if func._dbus_name.endswith("_dbus_property"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1055
            func._dbus_name = func._dbus_name[:-14]
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1056
        func._dbus_get_args_options = {'byte_arrays': byte_arrays }
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1057
        return func
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1058
    
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1059
    return decorator
1060
1061
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1062
def dbus_interface_annotations(dbus_interface):
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1063
    """Decorator for marking functions returning interface annotations
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1064
    
1065
    Usage:
1066
    
1067
    @dbus_interface_annotations("org.example.Interface")
1068
    def _foo(self):  # Function name does not matter
1069
        return {"org.freedesktop.DBus.Deprecated": "true",
1070
                "org.freedesktop.DBus.Property.EmitsChangedSignal":
1071
                    "false"}
1072
    """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1073
    
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1074
    def decorator(func):
1075
        func._dbus_is_interface = True
1076
        func._dbus_interface = dbus_interface
1077
        func._dbus_name = dbus_interface
1078
        return func
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1079
    
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1080
    return decorator
1081
1082
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1083
def dbus_annotations(annotations):
1084
    """Decorator to annotate D-Bus methods, signals or properties
1085
    Usage:
1086
    
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1087
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true",
1088
                       "org.freedesktop.DBus.Property."
1089
                       "EmitsChangedSignal": "false"})
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1090
    @dbus_service_property("org.example.Interface", signature="b",
1091
                           access="r")
1092
    def Property_dbus_property(self):
1093
        return dbus.Boolean(False)
784 by Teddy Hogeborn
Refactor D-Bus annotation class out from D-Bus properties class.
1094
    
1095
    See also the DBusObjectWithAnnotations class.
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1096
    """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1097
    
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1098
    def decorator(func):
1099
        func._dbus_annotations = annotations
1100
        return func
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1101
    
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1102
    return decorator
1103
1104
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1105
class DBusPropertyException(dbus.exceptions.DBusException):
1106
    """A base class for D-Bus property-related exceptions
1107
    """
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
1108
    pass
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1109
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1110
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1111
class DBusPropertyAccessException(DBusPropertyException):
1112
    """A property's access permissions disallows an operation.
1113
    """
1114
    pass
1115
1116
1117
class DBusPropertyNotFound(DBusPropertyException):
1118
    """An attempt was made to access a non-existing property.
1119
    """
1120
    pass
1121
1122
784 by Teddy Hogeborn
Refactor D-Bus annotation class out from D-Bus properties class.
1123
class DBusObjectWithAnnotations(dbus.service.Object):
1124
    """A D-Bus object with annotations.
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1125
    
784 by Teddy Hogeborn
Refactor D-Bus annotation class out from D-Bus properties class.
1126
    Classes inheriting from this can use the dbus_annotations
1127
    decorator to add annotations to methods or signals.
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1128
    """
1129
    
1130
    @staticmethod
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1131
    def _is_dbus_thing(thing):
1132
        """Returns a function testing if an attribute is a D-Bus thing
1133
        
1134
        If called like _is_dbus_thing("method") it returns a function
1135
        suitable for use as predicate to inspect.getmembers().
1136
        """
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1137
        return lambda obj: getattr(obj, "_dbus_is_{}".format(thing),
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1138
                                   False)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1139
    
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1140
    def _get_all_dbus_things(self, thing):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1141
        """Returns a generator of (name, attribute) pairs
1142
        """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1143
        return ((getattr(athing.__get__(self), "_dbus_name", name),
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1144
                 athing.__get__(self))
24.1.186 by Björn Påhlsson
transitional stuff actually working
1145
                for cls in self.__class__.__mro__
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1146
                for name, athing in
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1147
                inspect.getmembers(cls, self._is_dbus_thing(thing)))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1148
    
784 by Teddy Hogeborn
Refactor D-Bus annotation class out from D-Bus properties class.
1149
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1150
                         out_signature = "s",
1151
                         path_keyword = 'object_path',
1152
                         connection_keyword = 'connection')
1153
    def Introspect(self, object_path, connection):
1154
        """Overloading of standard D-Bus method.
1155
        
1156
        Inserts annotation tags on methods and signals.
1157
        """
1158
        xmlstring = dbus.service.Object.Introspect(self, object_path,
1159
                                                   connection)
1160
        try:
1161
            document = xml.dom.minidom.parseString(xmlstring)
1162
            
1163
            for if_tag in document.getElementsByTagName("interface"):
1164
                # Add annotation tags
1165
                for typ in ("method", "signal"):
1166
                    for tag in if_tag.getElementsByTagName(typ):
1167
                        annots = dict()
1168
                        for name, prop in (self.
1169
                                           _get_all_dbus_things(typ)):
1170
                            if (name == tag.getAttribute("name")
1171
                                and prop._dbus_interface
1172
                                == if_tag.getAttribute("name")):
1173
                                annots.update(getattr(
1174
                                    prop, "_dbus_annotations", {}))
1175
                        for name, value in annots.items():
1176
                            ann_tag = document.createElement(
1177
                                "annotation")
1178
                            ann_tag.setAttribute("name", name)
1179
                            ann_tag.setAttribute("value", value)
1180
                            tag.appendChild(ann_tag)
1181
                # Add interface annotation tags
1182
                for annotation, value in dict(
1183
                    itertools.chain.from_iterable(
1184
                        annotations().items()
1185
                        for name, annotations
1186
                        in self._get_all_dbus_things("interface")
1187
                        if name == if_tag.getAttribute("name")
1188
                        )).items():
1189
                    ann_tag = document.createElement("annotation")
1190
                    ann_tag.setAttribute("name", annotation)
1191
                    ann_tag.setAttribute("value", value)
1192
                    if_tag.appendChild(ann_tag)
1193
                # Fix argument name for the Introspect method itself
1194
                if (if_tag.getAttribute("name")
1195
                                == dbus.INTROSPECTABLE_IFACE):
1196
                    for cn in if_tag.getElementsByTagName("method"):
1197
                        if cn.getAttribute("name") == "Introspect":
1198
                            for arg in cn.getElementsByTagName("arg"):
1199
                                if (arg.getAttribute("direction")
1200
                                    == "out"):
1201
                                    arg.setAttribute("name",
1202
                                                     "xml_data")
1203
            xmlstring = document.toxml("utf-8")
1204
            document.unlink()
1205
        except (AttributeError, xml.dom.DOMException,
1206
                xml.parsers.expat.ExpatError) as error:
1207
            logger.error("Failed to override Introspection method",
1208
                         exc_info=error)
1209
        return xmlstring
1210
1211
1212
class DBusObjectWithProperties(DBusObjectWithAnnotations):
1213
    """A D-Bus object with properties.
1214
    
1215
    Classes inheriting from this can use the dbus_service_property
1216
    decorator to expose methods as D-Bus properties.  It exposes the
1217
    standard Get(), Set(), and GetAll() methods on the D-Bus.
1218
    """
1219
    
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1220
    def _get_dbus_property(self, interface_name, property_name):
1221
        """Returns a bound method if one exists which is a D-Bus
1222
        property with the specified name and interface.
1223
        """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1224
        for cls in self.__class__.__mro__:
1225
            for name, value in inspect.getmembers(
1226
                    cls, self._is_dbus_thing("property")):
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1227
                if (value._dbus_name == property_name
1228
                    and value._dbus_interface == interface_name):
24.1.186 by Björn Påhlsson
transitional stuff actually working
1229
                    return value.__get__(self)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1230
        
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1231
        # No such property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1232
        raise DBusPropertyNotFound("{}:{}.{}".format(
1233
            self.dbus_object_path, interface_name, property_name))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1234
    
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
1235
    @classmethod
1236
    def _get_all_interface_names(cls):
1237
        """Get a sequence of all interfaces supported by an object"""
1238
        return (name for name in set(getattr(getattr(x, attr),
1239
                                             "_dbus_interface", None)
1240
                                     for x in (inspect.getmro(cls))
1241
                                     for attr in dir(x))
1242
                if name is not None)
1243
    
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1244
    @dbus.service.method(dbus.PROPERTIES_IFACE,
1245
                         in_signature="ss",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1246
                         out_signature="v")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1247
    def Get(self, interface_name, property_name):
1248
        """Standard D-Bus property Get() method, see D-Bus standard.
1249
        """
1250
        prop = self._get_dbus_property(interface_name, property_name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1251
        if prop._dbus_access == "write":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1252
            raise DBusPropertyAccessException(property_name)
1253
        value = prop()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1254
        if not hasattr(value, "variant_level"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1255
            return value
1256
        return type(value)(value, variant_level=value.variant_level+1)
1257
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1258
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1259
    def Set(self, interface_name, property_name, value):
1260
        """Standard D-Bus property Set() method, see D-Bus standard.
1261
        """
1262
        prop = self._get_dbus_property(interface_name, property_name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1263
        if prop._dbus_access == "read":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1264
            raise DBusPropertyAccessException(property_name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1265
        if prop._dbus_get_args_options["byte_arrays"]:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1266
            # The byte_arrays option is not supported yet on
1267
            # signatures other than "ay".
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1268
            if prop._dbus_signature != "ay":
649 by Teddy Hogeborn
Minor code fix; raise only exception instances, not classes.
1269
                raise ValueError("Byte arrays not supported for non-"
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1270
                                 "'ay' signature {!r}"
649 by Teddy Hogeborn
Minor code fix; raise only exception instances, not classes.
1271
                                 .format(prop._dbus_signature))
546 by Teddy Hogeborn
* debian/rules (binary-common): Exclude network-hooks.d from
1272
            value = dbus.ByteArray(b''.join(chr(byte)
1273
                                            for byte in value))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1274
        prop(value)
1275
    
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1276
    @dbus.service.method(dbus.PROPERTIES_IFACE,
1277
                         in_signature="s",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1278
                         out_signature="a{sv}")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1279
    def GetAll(self, interface_name):
1280
        """Standard D-Bus property GetAll() method, see D-Bus
1281
        standard.
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1282
        
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1283
        Note: Will not include properties with access="write".
1284
        """
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
1285
        properties = {}
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1286
        for name, prop in self._get_all_dbus_things("property"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1287
            if (interface_name
1288
                and interface_name != prop._dbus_interface):
1289
                # Interface non-empty but did not match
1290
                continue
1291
            # Ignore write-only properties
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1292
            if prop._dbus_access == "write":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1293
                continue
1294
            value = prop()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1295
            if not hasattr(value, "variant_level"):
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
1296
                properties[name] = value
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1297
                continue
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1298
            properties[name] = type(value)(
1299
                value, variant_level = value.variant_level + 1)
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
1300
        return dbus.Dictionary(properties, signature="sv")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1301
    
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1302
    @dbus.service.signal(dbus.PROPERTIES_IFACE, signature="sa{sv}as")
1303
    def PropertiesChanged(self, interface_name, changed_properties,
1304
                          invalidated_properties):
1305
        """Standard D-Bus PropertiesChanged() signal, see D-Bus
1306
        standard.
1307
        """
1308
        pass
1309
    
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1310
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1311
                         out_signature="s",
1312
                         path_keyword='object_path',
1313
                         connection_keyword='connection')
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1314
    def Introspect(self, object_path, connection):
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1315
        """Overloading of standard D-Bus method.
1316
        
1317
        Inserts property tags and interface annotation tags.
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1318
        """
784 by Teddy Hogeborn
Refactor D-Bus annotation class out from D-Bus properties class.
1319
        xmlstring = DBusObjectWithAnnotations.Introspect(self,
1320
                                                         object_path,
1321
                                                         connection)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1322
        try:
1323
            document = xml.dom.minidom.parseString(xmlstring)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1324
            
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1325
            def make_tag(document, name, prop):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1326
                e = document.createElement("property")
1327
                e.setAttribute("name", name)
1328
                e.setAttribute("type", prop._dbus_signature)
1329
                e.setAttribute("access", prop._dbus_access)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1330
                return e
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1331
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1332
            for if_tag in document.getElementsByTagName("interface"):
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1333
                # Add property tags
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1334
                for tag in (make_tag(document, name, prop)
1335
                            for name, prop
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1336
                            in self._get_all_dbus_things("property")
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1337
                            if prop._dbus_interface
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1338
                            == if_tag.getAttribute("name")):
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1339
                    if_tag.appendChild(tag)
784 by Teddy Hogeborn
Refactor D-Bus annotation class out from D-Bus properties class.
1340
                # Add annotation tags for properties
1341
                for tag in if_tag.getElementsByTagName("property"):
1342
                    annots = dict()
1343
                    for name, prop in self._get_all_dbus_things(
1344
                            "property"):
1345
                        if (name == tag.getAttribute("name")
1346
                            and prop._dbus_interface
1347
                            == if_tag.getAttribute("name")):
1348
                            annots.update(getattr(
1349
                                prop, "_dbus_annotations", {}))
1350
                    for name, value in annots.items():
1351
                        ann_tag = document.createElement(
1352
                            "annotation")
1353
                        ann_tag.setAttribute("name", name)
1354
                        ann_tag.setAttribute("value", value)
1355
                        tag.appendChild(ann_tag)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1356
                # Add the names to the return values for the
1357
                # "org.freedesktop.DBus.Properties" methods
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1358
                if (if_tag.getAttribute("name")
1359
                    == "org.freedesktop.DBus.Properties"):
1360
                    for cn in if_tag.getElementsByTagName("method"):
1361
                        if cn.getAttribute("name") == "Get":
1362
                            for arg in cn.getElementsByTagName("arg"):
1363
                                if (arg.getAttribute("direction")
1364
                                    == "out"):
1365
                                    arg.setAttribute("name", "value")
1366
                        elif cn.getAttribute("name") == "GetAll":
1367
                            for arg in cn.getElementsByTagName("arg"):
1368
                                if (arg.getAttribute("direction")
1369
                                    == "out"):
1370
                                    arg.setAttribute("name", "props")
1371
            xmlstring = document.toxml("utf-8")
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1372
            document.unlink()
1373
        except (AttributeError, xml.dom.DOMException,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1374
                xml.parsers.expat.ExpatError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1375
            logger.error("Failed to override Introspection method",
565 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
1376
                         exc_info=error)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1377
        return xmlstring
1378
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
1379
try:
1380
    dbus.OBJECT_MANAGER_IFACE
1381
except AttributeError:
1382
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
1383
1384
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
1385
    """A D-Bus object with an ObjectManager.
1386
    
1387
    Classes inheriting from this exposes the standard
1388
    GetManagedObjects call and the InterfacesAdded and
1389
    InterfacesRemoved signals on the standard
1390
    "org.freedesktop.DBus.ObjectManager" interface.
1391
    
1392
    Note: No signals are sent automatically; they must be sent
1393
    manually.
1394
    """
1395
    @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
1396
                         out_signature = "a{oa{sa{sv}}}")
1397
    def GetManagedObjects(self):
1398
        """This function must be overridden"""
1399
        raise NotImplementedError()
1400
    
1401
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
1402
                         signature = "oa{sa{sv}}")
1403
    def InterfacesAdded(self, object_path, interfaces_and_properties):
1404
        pass
1405
    
786 by Teddy Hogeborn
Server Bug fix: Make D-Bus introspection work again.
1406
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas")
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
1407
    def InterfacesRemoved(self, object_path, interfaces):
1408
        pass
1409
    
1410
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1411
                         out_signature = "s",
1412
                         path_keyword = 'object_path',
1413
                         connection_keyword = 'connection')
1414
    def Introspect(self, object_path, connection):
1415
        """Overloading of standard D-Bus method.
1416
        
1417
        Override return argument name of GetManagedObjects to be
1418
        "objpath_interfaces_and_properties"
1419
        """
786 by Teddy Hogeborn
Server Bug fix: Make D-Bus introspection work again.
1420
        xmlstring = DBusObjectWithAnnotations.Introspect(self,
1421
                                                         object_path,
1422
                                                         connection)
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
1423
        try:
1424
            document = xml.dom.minidom.parseString(xmlstring)
1425
            
1426
            for if_tag in document.getElementsByTagName("interface"):
1427
                # Fix argument name for the GetManagedObjects method
1428
                if (if_tag.getAttribute("name")
1429
                                == dbus.OBJECT_MANAGER_IFACE):
1430
                    for cn in if_tag.getElementsByTagName("method"):
1431
                        if (cn.getAttribute("name")
1432
                            == "GetManagedObjects"):
1433
                            for arg in cn.getElementsByTagName("arg"):
1434
                                if (arg.getAttribute("direction")
1435
                                    == "out"):
1436
                                    arg.setAttribute(
1437
                                        "name",
1438
                                        "objpath_interfaces"
1439
                                        "_and_properties")
1440
            xmlstring = document.toxml("utf-8")
1441
            document.unlink()
1442
        except (AttributeError, xml.dom.DOMException,
1443
                xml.parsers.expat.ExpatError) as error:
1444
            logger.error("Failed to override Introspection method",
1445
                         exc_info = error)
1446
        return xmlstring
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1447
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
1448
def datetime_to_dbus(dt, variant_level=0):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1449
    """Convert a UTC datetime.datetime() to a D-Bus type."""
1450
    if dt is None:
1451
        return dbus.String("", variant_level = variant_level)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1452
    return dbus.String(dt.isoformat(), variant_level=variant_level)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1453
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1454
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1455
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
1456
    """A class decorator; applied to a subclass of
1457
    dbus.service.Object, it will add alternate D-Bus attributes with
1458
    interface names according to the "alt_interface_names" mapping.
1459
    Usage:
1460
    
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
1461
    @alternate_dbus_interfaces({"org.example.Interface":
1462
                                    "net.example.AlternateInterface"})
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1463
    class SampleDBusObject(dbus.service.Object):
1464
        @dbus.service.method("org.example.Interface")
1465
        def SampleDBusMethod():
1466
            pass
1467
    
1468
    The above "SampleDBusMethod" on "SampleDBusObject" will be
1469
    reachable via two interfaces: "org.example.Interface" and
1470
    "net.example.AlternateInterface", the latter of which will have
1471
    its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to
1472
    "true", unless "deprecate" is passed with a False value.
1473
    
1474
    This works for methods and signals, and also for D-Bus properties
1475
    (from DBusObjectWithProperties) and interfaces (from the
1476
    dbus_interface_annotations decorator).
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1477
    """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1478
    
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1479
    def wrapper(cls):
1480
        for orig_interface_name, alt_interface_name in (
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1481
                alt_interface_names.items()):
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1482
            attr = {}
1483
            interface_names = set()
1484
            # Go though all attributes of the class
1485
            for attrname, attribute in inspect.getmembers(cls):
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1486
                # Ignore non-D-Bus attributes, and D-Bus attributes
1487
                # with the wrong interface name
1488
                if (not hasattr(attribute, "_dbus_interface")
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1489
                    or not attribute._dbus_interface.startswith(
1490
                        orig_interface_name)):
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1491
                    continue
1492
                # Create an alternate D-Bus interface name based on
1493
                # the current name
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1494
                alt_interface = attribute._dbus_interface.replace(
1495
                    orig_interface_name, alt_interface_name)
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1496
                interface_names.add(alt_interface)
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1497
                # Is this a D-Bus signal?
1498
                if getattr(attribute, "_dbus_is_signal", False):
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
1499
                    if sys.version_info.major == 2:
754 by Teddy Hogeborn
mandos: More Python 3 fixes.
1500
                        # Extract the original non-method undecorated
1501
                        # function by black magic
1502
                        nonmethod_func = (dict(
1503
                            zip(attribute.func_code.co_freevars,
1504
                                attribute.__closure__))
1505
                                          ["func"].cell_contents)
1506
                    else:
1507
                        nonmethod_func = attribute
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1508
                    # Create a new, but exactly alike, function
1509
                    # object, and decorate it to be a new D-Bus signal
1510
                    # with the alternate D-Bus interface name
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
1511
                    if sys.version_info.major == 2:
754 by Teddy Hogeborn
mandos: More Python 3 fixes.
1512
                        new_function = types.FunctionType(
1513
                            nonmethod_func.func_code,
1514
                            nonmethod_func.func_globals,
1515
                            nonmethod_func.func_name,
1516
                            nonmethod_func.func_defaults,
1517
                            nonmethod_func.func_closure)
1518
                    else:
1519
                        new_function = types.FunctionType(
1520
                            nonmethod_func.__code__,
1521
                            nonmethod_func.__globals__,
1522
                            nonmethod_func.__name__,
1523
                            nonmethod_func.__defaults__,
1524
                            nonmethod_func.__closure__)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1525
                    new_function = (dbus.service.signal(
754 by Teddy Hogeborn
mandos: More Python 3 fixes.
1526
                        alt_interface,
1527
                        attribute._dbus_signature)(new_function))
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1528
                    # Copy annotations, if any
1529
                    try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1530
                        new_function._dbus_annotations = dict(
1531
                            attribute._dbus_annotations)
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1532
                    except AttributeError:
1533
                        pass
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1534
                    # Define a creator of a function to call both the
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1535
                    # original and alternate functions, so both the
1536
                    # original and alternate signals gets sent when
1537
                    # the function is called
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1538
                    def fixscope(func1, func2):
1539
                        """This function is a scope container to pass
1540
                        func1 and func2 to the "call_both" function
1541
                        outside of its arguments"""
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1542
                        
786 by Teddy Hogeborn
Server Bug fix: Make D-Bus introspection work again.
1543
                        @functools.wraps(func2)
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1544
                        def call_both(*args, **kwargs):
1545
                            """This function will emit two D-Bus
1546
                            signals by calling func1 and func2"""
1547
                            func1(*args, **kwargs)
1548
                            func2(*args, **kwargs)
786 by Teddy Hogeborn
Server Bug fix: Make D-Bus introspection work again.
1549
                        # Make wrapper function look like a D-Bus signal
1550
                        for name, attr in inspect.getmembers(func2):
1551
                            if name.startswith("_dbus_"):
1552
                                setattr(call_both, name, attr)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1553
                        
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1554
                        return call_both
1555
                    # Create the "call_both" function and add it to
1556
                    # the class
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1557
                    attr[attrname] = fixscope(attribute, new_function)
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1558
                # Is this a D-Bus method?
1559
                elif getattr(attribute, "_dbus_is_method", False):
1560
                    # Create a new, but exactly alike, function
1561
                    # object.  Decorate it to be a new D-Bus method
1562
                    # with the alternate D-Bus interface name.  Add it
1563
                    # to the class.
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1564
                    attr[attrname] = (
1565
                        dbus.service.method(
1566
                            alt_interface,
1567
                            attribute._dbus_in_signature,
1568
                            attribute._dbus_out_signature)
1569
                        (types.FunctionType(attribute.func_code,
1570
                                            attribute.func_globals,
1571
                                            attribute.func_name,
1572
                                            attribute.func_defaults,
1573
                                            attribute.func_closure)))
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1574
                    # Copy annotations, if any
1575
                    try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1576
                        attr[attrname]._dbus_annotations = dict(
1577
                            attribute._dbus_annotations)
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1578
                    except AttributeError:
1579
                        pass
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1580
                # Is this a D-Bus property?
1581
                elif getattr(attribute, "_dbus_is_property", False):
1582
                    # Create a new, but exactly alike, function
1583
                    # object, and decorate it to be a new D-Bus
1584
                    # property with the alternate D-Bus interface
1585
                    # name.  Add it to the class.
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1586
                    attr[attrname] = (dbus_service_property(
1587
                        alt_interface, attribute._dbus_signature,
1588
                        attribute._dbus_access,
1589
                        attribute._dbus_get_args_options
1590
                        ["byte_arrays"])
1591
                                      (types.FunctionType(
1592
                                          attribute.func_code,
1593
                                          attribute.func_globals,
1594
                                          attribute.func_name,
1595
                                          attribute.func_defaults,
1596
                                          attribute.func_closure)))
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1597
                    # Copy annotations, if any
1598
                    try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1599
                        attr[attrname]._dbus_annotations = dict(
1600
                            attribute._dbus_annotations)
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1601
                    except AttributeError:
1602
                        pass
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1603
                # Is this a D-Bus interface?
1604
                elif getattr(attribute, "_dbus_is_interface", False):
1605
                    # Create a new, but exactly alike, function
1606
                    # object.  Decorate it to be a new D-Bus interface
1607
                    # with the alternate D-Bus interface name.  Add it
1608
                    # to the class.
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1609
                    attr[attrname] = (
1610
                        dbus_interface_annotations(alt_interface)
1611
                        (types.FunctionType(attribute.func_code,
1612
                                            attribute.func_globals,
1613
                                            attribute.func_name,
1614
                                            attribute.func_defaults,
1615
                                            attribute.func_closure)))
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1616
            if deprecate:
1617
                # Deprecate all alternate interfaces
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1618
                iname="_AlternateDBusNames_interface_annotation{}"
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1619
                for interface_name in interface_names:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1620
                    
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1621
                    @dbus_interface_annotations(interface_name)
1622
                    def func(self):
1623
                        return { "org.freedesktop.DBus.Deprecated":
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1624
                                 "true" }
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1625
                    # Find an unused name
1626
                    for aname in (iname.format(i)
1627
                                  for i in itertools.count()):
1628
                        if aname not in attr:
1629
                            attr[aname] = func
1630
                            break
1631
            if interface_names:
1632
                # Replace the class with a new subclass of it with
1633
                # methods, signals, etc. as created above.
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1634
                cls = type(b"{}Alternate".format(cls.__name__),
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1635
                           (cls, ), attr)
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1636
        return cls
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1637
    
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1638
    return wrapper
1639
1640
1641
@alternate_dbus_interfaces({"se.recompile.Mandos":
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1642
                            "se.bsnet.fukt.Mandos"})
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1643
class ClientDBus(Client, DBusObjectWithProperties):
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1644
    """A Client class using D-Bus
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1645
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1646
    Attributes:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1647
    dbus_object_path: dbus.ObjectPath
1648
    bus: dbus.SystemBus()
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1649
    """
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1650
    
1651
    runtime_expansions = (Client.runtime_expansions
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1652
                          + ("dbus_object_path", ))
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1653
    
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1654
    _interface = "se.recompile.Mandos.Client"
1655
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1656
    # dbus.service.Object doesn't use super(), so we can't either.
1657
    
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
1658
    def __init__(self, bus = None, *args, **kwargs):
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
1659
        self.bus = bus
1660
        Client.__init__(self, *args, **kwargs)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1661
        # Only now, when this client is initialized, can it show up on
1662
        # the D-Bus
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1663
        client_object_name = str(self.name).translate(
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1664
            {ord("."): ord("_"),
1665
             ord("-"): ord("_")})
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1666
        self.dbus_object_path = dbus.ObjectPath(
1667
            "/clients/" + client_object_name)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1668
        DBusObjectWithProperties.__init__(self, self.bus,
1669
                                          self.dbus_object_path)
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1670
    
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1671
    def notifychangeproperty(transform_func, dbus_name,
1672
                             type_func=lambda x: x,
1673
                             variant_level=1,
1674
                             invalidate_only=False,
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1675
                             _interface=_interface):
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1676
        """ Modify a variable so that it's a property which announces
1677
        its changes to DBus.
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1678
        
505.1.15 by Teddy Hogeborn
Bug fix: Make D-Bus properties settable again.
1679
        transform_fun: Function that takes a value and a variant_level
1680
                       and transforms it to a D-Bus type.
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1681
        dbus_name: D-Bus name of the variable
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1682
        type_func: Function that transform the value before sending it
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1683
                   to the D-Bus.  Default: no transform
1684
        variant_level: D-Bus variant level.  Default: 1
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1685
        """
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1686
        attrname = "_{}".format(dbus_name)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1687
        
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1688
        def setter(self, value):
1689
            if hasattr(self, "dbus_object_path"):
505.1.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1690
                if (not hasattr(self, attrname) or
1691
                    type_func(getattr(self, attrname, None))
1692
                    != type_func(value)):
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1693
                    if invalidate_only:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1694
                        self.PropertiesChanged(
1695
                            _interface, dbus.Dictionary(),
1696
                            dbus.Array((dbus_name, )))
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1697
                    else:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1698
                        dbus_value = transform_func(
1699
                            type_func(value),
1700
                            variant_level = variant_level)
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1701
                        self.PropertyChanged(dbus.String(dbus_name),
1702
                                             dbus_value)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1703
                        self.PropertiesChanged(
1704
                            _interface,
1705
                            dbus.Dictionary({ dbus.String(dbus_name):
1706
                                              dbus_value }),
1707
                            dbus.Array())
505.1.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1708
            setattr(self, attrname, value)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1709
        
505.1.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1710
        return property(lambda self: getattr(self, attrname), setter)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1711
    
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1712
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
1713
    approvals_pending = notifychangeproperty(dbus.Boolean,
1714
                                             "ApprovalPending",
1715
                                             type_func = bool)
1716
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1717
    last_enabled = notifychangeproperty(datetime_to_dbus,
1718
                                        "LastEnabled")
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1719
    checker = notifychangeproperty(
1720
        dbus.Boolean, "CheckerRunning",
1721
        type_func = lambda checker: checker is not None)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1722
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1723
                                           "LastCheckedOK")
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1724
    last_checker_status = notifychangeproperty(dbus.Int16,
1725
                                               "LastCheckerStatus")
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1726
    last_approval_request = notifychangeproperty(
1727
        datetime_to_dbus, "LastApprovalRequest")
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1728
    approved_by_default = notifychangeproperty(dbus.Boolean,
1729
                                               "ApprovedByDefault")
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1730
    approval_delay = notifychangeproperty(
1731
        dbus.UInt64, "ApprovalDelay",
1732
        type_func = lambda td: td.total_seconds() * 1000)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1733
    approval_duration = notifychangeproperty(
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1734
        dbus.UInt64, "ApprovalDuration",
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1735
        type_func = lambda td: td.total_seconds() * 1000)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1736
    host = notifychangeproperty(dbus.String, "Host")
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1737
    timeout = notifychangeproperty(
1738
        dbus.UInt64, "Timeout",
1739
        type_func = lambda td: td.total_seconds() * 1000)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1740
    extended_timeout = notifychangeproperty(
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1741
        dbus.UInt64, "ExtendedTimeout",
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1742
        type_func = lambda td: td.total_seconds() * 1000)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1743
    interval = notifychangeproperty(
1744
        dbus.UInt64, "Interval",
1745
        type_func = lambda td: td.total_seconds() * 1000)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1746
    checker_command = notifychangeproperty(dbus.String, "Checker")
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1747
    secret = notifychangeproperty(dbus.ByteArray, "Secret",
1748
                                  invalidate_only=True)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1749
    
1750
    del notifychangeproperty
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1751
    
1752
    def __del__(self, *args, **kwargs):
1753
        try:
1754
            self.remove_from_connection()
329 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
1755
        except LookupError:
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1756
            pass
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1757
        if hasattr(DBusObjectWithProperties, "__del__"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1758
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1759
        Client.__del__(self, *args, **kwargs)
1760
    
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1761
    def checker_callback(self, source, condition,
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
1762
                         connection, command, *args, **kwargs):
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1763
        ret = Client.checker_callback(self, source, condition,
756 by Teddy Hogeborn
Merge change to use multiprocessing module to run checkers.
1764
                                      connection, command, *args,
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1765
                                      **kwargs)
1766
        exitstatus = self.last_checker_status
1767
        if exitstatus >= 0:
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1768
            # Emit D-Bus signal
1769
            self.CheckerCompleted(dbus.Int16(exitstatus),
783 by Teddy Hogeborn
Revert change to D-Bus API.
1770
                                  # This is specific to GNU libC
1771
                                  dbus.Int64(exitstatus << 8),
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1772
                                  dbus.String(command))
1773
        else:
1774
            # Emit D-Bus signal
1775
            self.CheckerCompleted(dbus.Int16(-1),
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1776
                                  dbus.Int64(
783 by Teddy Hogeborn
Revert change to D-Bus API.
1777
                                      # This is specific to GNU libC
1778
                                      (exitstatus << 8)
1779
                                      | self.last_checker_signal),
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1780
                                  dbus.String(command))
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
1781
        return ret
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1782
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1783
    def start_checker(self, *args, **kwargs):
658 by Teddy Hogeborn
* mandos (ClientDBus.start_checker): Removed unused variable
1784
        old_checker_pid = getattr(self.checker, "pid", None)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1785
        r = Client.start_checker(self, *args, **kwargs)
329 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
1786
        # Only if new checker process was started
1787
        if (self.checker is not None
1788
            and old_checker_pid != self.checker.pid):
1789
            # Emit D-Bus signal
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1790
            self.CheckerStarted(self.current_checker_command)
1791
        return r
1792
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1793
    def _reset_approved(self):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1794
        self.approved = None
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1795
        return False
1796
    
1797
    def approve(self, value=True):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1798
        self.approved = value
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1799
        gobject.timeout_add(int(self.approval_duration.total_seconds()
1800
                                * 1000), self._reset_approved)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1801
        self.send_changedstate()
24.2.1 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-urwid".
1802
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1803
    ## D-Bus methods, signals & properties
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1804
    
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1805
    ## Interfaces
1806
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1807
    ## Signals
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1808
    
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1809
    # CheckerCompleted - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1810
    @dbus.service.signal(_interface, signature="nxs")
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
1811
    def CheckerCompleted(self, exitcode, waitstatus, command):
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1812
        "D-Bus signal"
1813
        pass
1814
    
1815
    # CheckerStarted - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1816
    @dbus.service.signal(_interface, signature="s")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1817
    def CheckerStarted(self, command):
1818
        "D-Bus signal"
1819
        pass
1820
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1821
    # PropertyChanged - signal
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1822
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1823
    @dbus.service.signal(_interface, signature="sv")
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1824
    def PropertyChanged(self, property, value):
1825
        "D-Bus signal"
1826
        pass
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1827
    
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1828
    # GotSecret - signal
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1829
    @dbus.service.signal(_interface)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1830
    def GotSecret(self):
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1831
        """D-Bus signal
1832
        Is sent after a successful transfer of secret from the Mandos
1833
        server to mandos-client
1834
        """
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1835
        pass
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1836
    
1837
    # Rejected - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1838
    @dbus.service.signal(_interface, signature="s")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1839
    def Rejected(self, reason):
1840
        "D-Bus signal"
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1841
        pass
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1842
    
1843
    # NeedApproval - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1844
    @dbus.service.signal(_interface, signature="tb")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1845
    def NeedApproval(self, timeout, default):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1846
        "D-Bus signal"
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1847
        return self.need_approval()
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1848
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1849
    ## Methods
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1850
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1851
    # Approve - method
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1852
    @dbus.service.method(_interface, in_signature="b")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1853
    def Approve(self, value):
1854
        self.approve(value)
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1855
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1856
    # CheckedOK - method
1857
    @dbus.service.method(_interface)
1858
    def CheckedOK(self):
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1859
        self.checked_ok()
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1860
    
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1861
    # Enable - method
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
1862
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1863
    @dbus.service.method(_interface)
1864
    def Enable(self):
1865
        "D-Bus method"
1866
        self.enable()
1867
    
1868
    # StartChecker - method
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
1869
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1870
    @dbus.service.method(_interface)
1871
    def StartChecker(self):
1872
        "D-Bus method"
1873
        self.start_checker()
1874
    
1875
    # Disable - method
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
1876
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1877
    @dbus.service.method(_interface)
1878
    def Disable(self):
1879
        "D-Bus method"
1880
        self.disable()
1881
    
1882
    # StopChecker - method
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
1883
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1884
    @dbus.service.method(_interface)
1885
    def StopChecker(self):
1886
        self.stop_checker()
1887
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1888
    ## Properties
1889
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1890
    # ApprovalPending - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1891
    @dbus_service_property(_interface, signature="b", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1892
    def ApprovalPending_dbus_property(self):
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1893
        return dbus.Boolean(bool(self.approvals_pending))
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1894
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1895
    # ApprovedByDefault - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1896
    @dbus_service_property(_interface,
1897
                           signature="b",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1898
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1899
    def ApprovedByDefault_dbus_property(self, value=None):
1900
        if value is None:       # get
1901
            return dbus.Boolean(self.approved_by_default)
1902
        self.approved_by_default = bool(value)
1903
    
1904
    # ApprovalDelay - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1905
    @dbus_service_property(_interface,
1906
                           signature="t",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1907
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1908
    def ApprovalDelay_dbus_property(self, value=None):
1909
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1910
            return dbus.UInt64(self.approval_delay.total_seconds()
1911
                               * 1000)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1912
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
1913
    
1914
    # ApprovalDuration - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1915
    @dbus_service_property(_interface,
1916
                           signature="t",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1917
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1918
    def ApprovalDuration_dbus_property(self, value=None):
1919
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1920
            return dbus.UInt64(self.approval_duration.total_seconds()
1921
                               * 1000)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1922
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1923
    
1924
    # Name - property
780 by Teddy Hogeborn
Add D-Bus annotations on a few properties on the Client object.
1925
    @dbus_annotations(
1926
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1927
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1928
    def Name_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1929
        return dbus.String(self.name)
1930
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1931
    # Fingerprint - property
780 by Teddy Hogeborn
Add D-Bus annotations on a few properties on the Client object.
1932
    @dbus_annotations(
1933
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1934
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1935
    def Fingerprint_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1936
        return dbus.String(self.fingerprint)
1937
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1938
    # Host - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1939
    @dbus_service_property(_interface,
1940
                           signature="s",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1941
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1942
    def Host_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1943
        if value is None:       # get
1944
            return dbus.String(self.host)
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1945
        self.host = str(value)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1946
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1947
    # Created - property
780 by Teddy Hogeborn
Add D-Bus annotations on a few properties on the Client object.
1948
    @dbus_annotations(
1949
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1950
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1951
    def Created_dbus_property(self):
518.2.3 by Teddy Hogeborn
Make "enabled" a client config option.
1952
        return datetime_to_dbus(self.created)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1953
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1954
    # LastEnabled - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1955
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1956
    def LastEnabled_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1957
        return datetime_to_dbus(self.last_enabled)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1958
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1959
    # Enabled - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1960
    @dbus_service_property(_interface,
1961
                           signature="b",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1962
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1963
    def Enabled_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1964
        if value is None:       # get
1965
            return dbus.Boolean(self.enabled)
1966
        if value:
1967
            self.enable()
1968
        else:
1969
            self.disable()
1970
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1971
    # LastCheckedOK - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1972
    @dbus_service_property(_interface,
1973
                           signature="s",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1974
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1975
    def LastCheckedOK_dbus_property(self, value=None):
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1976
        if value is not None:
1977
            self.checked_ok()
1978
            return
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1979
        return datetime_to_dbus(self.last_checked_ok)
24.1.179 by Björn Påhlsson
New feature:
1980
    
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1981
    # LastCheckerStatus - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1982
    @dbus_service_property(_interface, signature="n", access="read")
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1983
    def LastCheckerStatus_dbus_property(self):
1984
        return dbus.Int16(self.last_checker_status)
1985
    
24.1.179 by Björn Påhlsson
New feature:
1986
    # Expires - property
1987
    @dbus_service_property(_interface, signature="s", access="read")
1988
    def Expires_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1989
        return datetime_to_dbus(self.expires)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1990
    
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1991
    # LastApprovalRequest - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1992
    @dbus_service_property(_interface, signature="s", access="read")
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1993
    def LastApprovalRequest_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1994
        return datetime_to_dbus(self.last_approval_request)
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1995
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1996
    # Timeout - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
1997
    @dbus_service_property(_interface,
1998
                           signature="t",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1999
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2000
    def Timeout_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2001
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
2002
            return dbus.UInt64(self.timeout.total_seconds() * 1000)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
2003
        old_timeout = self.timeout
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2004
        self.timeout = datetime.timedelta(0, 0, 0, value)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
2005
        # Reschedule disabling
543 by Teddy Hogeborn
* mandos: Break some long lines.
2006
        if self.enabled:
2007
            now = datetime.datetime.utcnow()
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
2008
            self.expires += self.timeout - old_timeout
2009
            if self.expires <= now:
543 by Teddy Hogeborn
* mandos: Break some long lines.
2010
                # The timeout has passed
2011
                self.disable()
2012
            else:
2013
                if (getattr(self, "disable_initiator_tag", None)
2014
                    is None):
2015
                    return
2016
                gobject.source_remove(self.disable_initiator_tag)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2017
                self.disable_initiator_tag = gobject.timeout_add(
2018
                    int((self.expires - now).total_seconds() * 1000),
2019
                    self.disable)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2020
    
24.1.179 by Björn Påhlsson
New feature:
2021
    # ExtendedTimeout - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2022
    @dbus_service_property(_interface,
2023
                           signature="t",
24.1.179 by Björn Påhlsson
New feature:
2024
                           access="readwrite")
2025
    def ExtendedTimeout_dbus_property(self, value=None):
2026
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
2027
            return dbus.UInt64(self.extended_timeout.total_seconds()
2028
                               * 1000)
24.1.179 by Björn Påhlsson
New feature:
2029
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2030
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2031
    # Interval - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2032
    @dbus_service_property(_interface,
2033
                           signature="t",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2034
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2035
    def Interval_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2036
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
2037
            return dbus.UInt64(self.interval.total_seconds() * 1000)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2038
        self.interval = datetime.timedelta(0, 0, 0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2039
        if getattr(self, "checker_initiator_tag", None) is None:
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2040
            return
518.2.3 by Teddy Hogeborn
Make "enabled" a client config option.
2041
        if self.enabled:
2042
            # Reschedule checker run
2043
            gobject.source_remove(self.checker_initiator_tag)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2044
            self.checker_initiator_tag = gobject.timeout_add(
2045
                value, self.start_checker)
2046
            self.start_checker() # Start one now, too
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2047
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2048
    # Checker - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2049
    @dbus_service_property(_interface,
2050
                           signature="s",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2051
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2052
    def Checker_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2053
        if value is None:       # get
2054
            return dbus.String(self.checker_command)
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
2055
        self.checker_command = str(value)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
2056
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2057
    # CheckerRunning - property
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2058
    @dbus_service_property(_interface,
2059
                           signature="b",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2060
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2061
    def CheckerRunning_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2062
        if value is None:       # get
2063
            return dbus.Boolean(self.checker is not None)
2064
        if value:
2065
            self.start_checker()
2066
        else:
2067
            self.stop_checker()
2068
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2069
    # ObjectPath - property
780 by Teddy Hogeborn
Add D-Bus annotations on a few properties on the Client object.
2070
    @dbus_annotations(
782 by Teddy Hogeborn
Deprecate the D-Bus property "se.recompile.Mandos.Client.ObjectPath".
2071
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const",
2072
         "org.freedesktop.DBus.Deprecated": "true"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2073
    @dbus_service_property(_interface, signature="o", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2074
    def ObjectPath_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
2075
        return self.dbus_object_path # is already a dbus.ObjectPath
2076
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2077
    # Secret = property
780 by Teddy Hogeborn
Add D-Bus annotations on a few properties on the Client object.
2078
    @dbus_annotations(
2079
        {"org.freedesktop.DBus.Property.EmitsChangedSignal":
2080
         "invalidates"})
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2081
    @dbus_service_property(_interface,
2082
                           signature="ay",
2083
                           access="write",
2084
                           byte_arrays=True)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2085
    def Secret_dbus_property(self, value):
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2086
        self.secret = bytes(value)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
2087
    
2088
    del _interface
3 by Björn Påhlsson
Python based server
2089
2090
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2091
class ProxyClient(object):
2092
    def __init__(self, child_pipe, fpr, address):
2093
        self._pipe = child_pipe
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2094
        self._pipe.send(('init', fpr, address))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2095
        if not self._pipe.recv():
752 by Teddy Hogeborn
mandos: Generate better messages in exceptions.
2096
            raise KeyError(fpr)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2097
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2098
    def __getattribute__(self, name):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
2099
        if name == '_pipe':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2100
            return super(ProxyClient, self).__getattribute__(name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2101
        self._pipe.send(('getattr', name))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2102
        data = self._pipe.recv()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2103
        if data[0] == 'data':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2104
            return data[1]
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2105
        if data[0] == 'function':
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2106
            
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2107
            def func(*args, **kwargs):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2108
                self._pipe.send(('funcall', name, args, kwargs))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2109
                return self._pipe.recv()[1]
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2110
            
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2111
            return func
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2112
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2113
    def __setattr__(self, name, value):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
2114
        if name == '_pipe':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2115
            return super(ProxyClient, self).__setattr__(name, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2116
        self._pipe.send(('setattr', name, value))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2117
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2118
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2119
class ClientHandler(socketserver.BaseRequestHandler, object):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2120
    """A class to handle client connections.
2121
    
2122
    Instantiated once for each connection to handle it.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2123
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
2124
    
3 by Björn Påhlsson
Python based server
2125
    def handle(self):
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2126
        with contextlib.closing(self.server.child_pipe) as child_pipe:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2127
            logger.info("TCP connection from: %s",
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
2128
                        str(self.client_address))
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2129
            logger.debug("Pipe FD: %d",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2130
                         self.server.child_pipe.fileno())
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2131
            
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2132
            session = gnutls.ClientSession(self.request)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2133
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2134
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
2135
            #                      "+AES-256-CBC", "+SHA1",
2136
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
2137
            #                      "+DHE-DSS"))
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2138
            # Use a fallback default, since this MUST be set.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2139
            priority = self.server.gnutls_priority
2140
            if priority is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2141
                priority = "NORMAL"
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2142
            gnutls.priority_set_direct(session._c_object, priority,
2143
                                       None)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2144
            
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
2145
            # Start communication using the Mandos protocol
2146
            # Get protocol number
2147
            line = self.request.makefile().readline()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2148
            logger.debug("Protocol version: %r", line)
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
2149
            try:
2150
                if int(line.strip().split()[0]) > 1:
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2151
                    raise RuntimeError(line)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2152
            except (ValueError, IndexError, RuntimeError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2153
                logger.error("Unknown protocol version: %s", error)
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
2154
                return
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2155
            
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
2156
            # Start GnuTLS connection
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2157
            try:
2158
                session.handshake()
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2159
            except gnutls.Error as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2160
                logger.warning("Handshake failed: %s", error)
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2161
                # Do not run session.bye() here: the session is not
2162
                # established.  Just abandon the request.
2163
                return
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2164
            logger.debug("Handshake succeeded")
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2165
            
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
2166
            approval_required = False
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2167
            try:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2168
                try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2169
                    fpr = self.fingerprint(
2170
                        self.peer_certificate(session))
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2171
                except (TypeError, gnutls.Error) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2172
                    logger.warning("Bad certificate: %s", error)
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2173
                    return
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2174
                logger.debug("Fingerprint: %s", fpr)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2175
                
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2176
                try:
2177
                    client = ProxyClient(child_pipe, fpr,
2178
                                         self.client_address)
2179
                except KeyError:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2180
                    return
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2181
                
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2182
                if client.approval_delay:
2183
                    delay = client.approval_delay
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
2184
                    client.approvals_pending += 1
2185
                    approval_required = True
2186
                
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
2187
                while True:
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2188
                    if not client.enabled:
24.1.174 by Björn Påhlsson
* Makefile (CFLAGS): Added "-lrt" to include real time library.
2189
                        logger.info("Client %s is disabled",
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2190
                                    client.name)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2191
                        if self.server.use_dbus:
2192
                            # Emit D-Bus signal
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2193
                            client.Rejected("Disabled")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2194
                        return
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
2195
                    
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
2196
                    if client.approved or not client.approval_delay:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
2197
                        #We are approved or approval is disabled
2198
                        break
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
2199
                    elif client.approved is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2200
                        logger.info("Client %s needs approval",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2201
                                    client.name)
2202
                        if self.server.use_dbus:
2203
                            # Emit D-Bus signal
2204
                            client.NeedApproval(
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
2205
                                client.approval_delay.total_seconds()
2206
                                * 1000, client.approved_by_default)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2207
                    else:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2208
                        logger.warning("Client %s was not approved",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2209
                                       client.name)
2210
                        if self.server.use_dbus:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
2211
                            # Emit D-Bus signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2212
                            client.Rejected("Denied")
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
2213
                        return
2214
                    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2215
                    #wait until timeout or approved
2216
                    time = datetime.datetime.now()
2217
                    client.changedstate.acquire()
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
2218
                    client.changedstate.wait(delay.total_seconds())
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2219
                    client.changedstate.release()
2220
                    time2 = datetime.datetime.now()
2221
                    if (time2 - time) >= delay:
2222
                        if not client.approved_by_default:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2223
                            logger.warning("Client %s timed out while"
2224
                                           " waiting for approval",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2225
                                           client.name)
2226
                            if self.server.use_dbus:
2227
                                # Emit D-Bus signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2228
                                client.Rejected("Approval timed out")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2229
                            return
2230
                        else:
2231
                            break
2232
                    else:
2233
                        delay -= time2 - time
2234
                
802 by Teddy Hogeborn
Handle GnuTLS errors and partial sends in gnutls "module".
2235
                try:
2236
                    session.send(client.secret)
2237
                except gnutls.Error as error:
2238
                    logger.warning("gnutls send failed",
2239
                                   exc_info = error)
2240
                    return
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2241
                
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2242
                logger.info("Sending secret to %s", client.name)
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2243
                # bump the timeout using extended_timeout
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
2244
                client.bump_timeout(client.extended_timeout)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2245
                if self.server.use_dbus:
2246
                    # Emit D-Bus signal
2247
                    client.GotSecret()
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
2248
            
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2249
            finally:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
2250
                if approval_required:
2251
                    client.approvals_pending -= 1
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
2252
                try:
2253
                    session.bye()
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2254
                except gnutls.Error as error:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2255
                    logger.warning("GnuTLS bye failed",
2256
                                   exc_info=error)
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2257
    
2258
    @staticmethod
2259
    def peer_certificate(session):
2260
        "Return the peer's OpenPGP certificate as a bytestring"
2261
        # If not an OpenPGP certificate...
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2262
        if (gnutls.certificate_type_get(session._c_object)
2263
            != gnutls.CRT_OPENPGP):
2264
            # ...return invalid data
2265
            return b""
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2266
        list_size = ctypes.c_uint(1)
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2267
        cert_list = (gnutls.certificate_get_peers
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2268
                     (session._c_object, ctypes.byref(list_size)))
2269
        if not bool(cert_list) and list_size.value != 0:
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2270
            raise gnutls.Error("error getting peer certificate")
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2271
        if list_size.value == 0:
2272
            return None
2273
        cert = cert_list[0]
2274
        return ctypes.string_at(cert.data, cert.size)
2275
    
2276
    @staticmethod
2277
    def fingerprint(openpgp):
2278
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
2279
        # New GnuTLS "datum" with the OpenPGP public key
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2280
        datum = gnutls.datum_t(
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2281
            ctypes.cast(ctypes.c_char_p(openpgp),
2282
                        ctypes.POINTER(ctypes.c_ubyte)),
2283
            ctypes.c_uint(len(openpgp)))
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2284
        # New empty GnuTLS certificate
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2285
        crt = gnutls.openpgp_crt_t()
2286
        gnutls.openpgp_crt_init(ctypes.byref(crt))
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2287
        # Import the OpenPGP public key into the certificate
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2288
        gnutls.openpgp_crt_import(crt, ctypes.byref(datum),
2289
                                  gnutls.OPENPGP_FMT_RAW)
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2290
        # Verify the self signature in the key
2291
        crtverify = ctypes.c_uint()
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2292
        gnutls.openpgp_crt_verify_self(crt, 0,
2293
                                       ctypes.byref(crtverify))
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2294
        if crtverify.value != 0:
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2295
            gnutls.openpgp_crt_deinit(crt)
2296
            raise gnutls.CertificateSecurityError("Verify failed")
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2297
        # New buffer for the fingerprint
2298
        buf = ctypes.create_string_buffer(20)
2299
        buf_len = ctypes.c_size_t()
2300
        # Get the fingerprint from the certificate into the buffer
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2301
        gnutls.openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
2302
                                           ctypes.byref(buf_len))
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2303
        # Deinit the certificate
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2304
        gnutls.openpgp_crt_deinit(crt)
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2305
        # Convert the buffer to a Python bytestring
2306
        fpr = ctypes.string_at(buf, buf_len.value)
2307
        # Convert the bytestring to hexadecimal notation
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2308
        hex_fpr = binascii.hexlify(fpr).upper()
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
2309
        return hex_fpr
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2310
2311
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2312
class MultiprocessingMixIn(object):
2313
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2314
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2315
    def sub_process_main(self, request, address):
2316
        try:
2317
            self.finish_request(request, address)
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2318
        except Exception:
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2319
            self.handle_error(request, address)
2320
        self.close_request(request)
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2321
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2322
    def process_request(self, request, address):
2323
        """Start a new process to process the request."""
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2324
        proc = multiprocessing.Process(target = self.sub_process_main,
579 by Teddy Hogeborn
* mandos: Use all new builtins.
2325
                                       args = (request, address))
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2326
        proc.start()
2327
        return proc
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2328
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2329
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2330
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
2331
    """ adds a pipe to the MixIn """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2332
    
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2333
    def process_request(self, request, client_address):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2334
        """Overrides and wraps the original process_request().
2335
        
355 by Teddy Hogeborn
* mandos: White-space fixes only.
2336
        This function creates a new pipe in self.pipe
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2337
        """
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2338
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2339
        
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2340
        proc = MultiprocessingMixIn.process_request(self, request,
2341
                                                    client_address)
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
2342
        self.child_pipe.close()
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2343
        self.add_pipe(parent_pipe, proc)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2344
    
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2345
    def add_pipe(self, parent_pipe, proc):
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2346
        """Dummy function; override as necessary"""
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2347
        raise NotImplementedError()
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2348
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2349
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2350
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2351
                     socketserver.TCPServer, object):
237.2.13 by Teddy Hogeborn
Merge from trunk. Notable changes:
2352
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2353
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2354
    Attributes:
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2355
        enabled:        Boolean; whether this server is activated yet
2356
        interface:      None or a network interface name (string)
2357
        use_ipv6:       Boolean; to use IPv6 or not
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2358
    """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2359
    
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2360
    def __init__(self, server_address, RequestHandlerClass,
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2361
                 interface=None,
2362
                 use_ipv6=True,
2363
                 socketfd=None):
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2364
        """If socketfd is set, use that file descriptor instead of
2365
        creating a new one with socket.socket().
2366
        """
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2367
        self.interface = interface
2368
        if use_ipv6:
2369
            self.address_family = socket.AF_INET6
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2370
        if socketfd is not None:
2371
            # Save the file descriptor
2372
            self.socketfd = socketfd
2373
            # Save the original socket.socket() function
2374
            self.socket_socket = socket.socket
2375
            # To implement --socket, we monkey patch socket.socket.
2376
            # 
2377
            # (When socketserver.TCPServer is a new-style class, we
2378
            # could make self.socket into a property instead of monkey
2379
            # patching socket.socket.)
2380
            # 
2381
            # Create a one-time-only replacement for socket.socket()
2382
            @functools.wraps(socket.socket)
2383
            def socket_wrapper(*args, **kwargs):
2384
                # Restore original function so subsequent calls are
2385
                # not affected.
2386
                socket.socket = self.socket_socket
2387
                del self.socket_socket
2388
                # This time only, return a new socket object from the
2389
                # saved file descriptor.
2390
                return socket.fromfd(self.socketfd, *args, **kwargs)
2391
            # Replace socket.socket() function with wrapper
2392
            socket.socket = socket_wrapper
2393
        # The socketserver.TCPServer.__init__ will call
2394
        # socket.socket(), which might be our replacement,
2395
        # socket_wrapper(), if socketfd was set.
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2396
        socketserver.TCPServer.__init__(self, server_address,
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2397
                                        RequestHandlerClass)
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2398
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2399
    def server_bind(self):
2400
        """This overrides the normal server_bind() function
2401
        to bind to an interface if one was specified, and also NOT to
2402
        bind to an address or port if they were not specified."""
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2403
        if self.interface is not None:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
2404
            if SO_BINDTODEVICE is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2405
                logger.error("SO_BINDTODEVICE does not exist;"
2406
                             " cannot bind to interface %s",
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
2407
                             self.interface)
2408
            else:
2409
                try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2410
                    self.socket.setsockopt(
2411
                        socket.SOL_SOCKET, SO_BINDTODEVICE,
2412
                        (self.interface + "\0").encode("utf-8"))
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2413
                except socket.error as error:
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
2414
                    if error.errno == errno.EPERM:
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2415
                        logger.error("No permission to bind to"
2416
                                     " interface %s", self.interface)
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
2417
                    elif error.errno == errno.ENOPROTOOPT:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2418
                        logger.error("SO_BINDTODEVICE not available;"
2419
                                     " cannot bind to interface %s",
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
2420
                                     self.interface)
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
2421
                    elif error.errno == errno.ENODEV:
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2422
                        logger.error("Interface %s does not exist,"
2423
                                     " cannot bind", self.interface)
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
2424
                    else:
2425
                        raise
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2426
        # Only bind(2) the socket if we really need to.
2427
        if self.server_address[0] or self.server_address[1]:
2428
            if not self.server_address[0]:
314 by Teddy Hogeborn
Support not using IPv6 in server:
2429
                if self.address_family == socket.AF_INET6:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2430
                    any_address = "::" # in6addr_any
314 by Teddy Hogeborn
Support not using IPv6 in server:
2431
                else:
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2432
                    any_address = "0.0.0.0" # INADDR_ANY
314 by Teddy Hogeborn
Support not using IPv6 in server:
2433
                self.server_address = (any_address,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2434
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
2435
            elif not self.server_address[1]:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2436
                self.server_address = (self.server_address[0], 0)
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2437
#                 if self.interface:
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
2438
#                     self.server_address = (self.server_address[0],
2439
#                                            0, # port
2440
#                                            0, # flowinfo
2441
#                                            if_nametoindex
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2442
#                                            (self.interface))
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2443
            return socketserver.TCPServer.server_bind(self)
339 by Teddy Hogeborn
Code cleanup.
2444
2445
2446
class MandosServer(IPv6_TCPServer):
2447
    """Mandos server.
2448
    
2449
    Attributes:
2450
        clients:        set of Client objects
2451
        gnutls_priority GnuTLS priority string
2452
        use_dbus:       Boolean; to emit D-Bus signals or not
340 by Teddy Hogeborn
Code cleanup.
2453
    
2454
    Assumes a gobject.MainLoop event loop.
339 by Teddy Hogeborn
Code cleanup.
2455
    """
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2456
    
339 by Teddy Hogeborn
Code cleanup.
2457
    def __init__(self, server_address, RequestHandlerClass,
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2458
                 interface=None,
2459
                 use_ipv6=True,
2460
                 clients=None,
2461
                 gnutls_priority=None,
2462
                 use_dbus=True,
2463
                 socketfd=None):
339 by Teddy Hogeborn
Code cleanup.
2464
        self.enabled = False
2465
        self.clients = clients
341 by Teddy Hogeborn
Code cleanup and one bug fix.
2466
        if self.clients is None:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2467
            self.clients = {}
339 by Teddy Hogeborn
Code cleanup.
2468
        self.use_dbus = use_dbus
2469
        self.gnutls_priority = gnutls_priority
2470
        IPv6_TCPServer.__init__(self, server_address,
2471
                                RequestHandlerClass,
2472
                                interface = interface,
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2473
                                use_ipv6 = use_ipv6,
2474
                                socketfd = socketfd)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2475
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2476
    def server_activate(self):
2477
        if self.enabled:
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2478
            return socketserver.TCPServer.server_activate(self)
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2479
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2480
    def enable(self):
2481
        self.enabled = True
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2482
    
2483
    def add_pipe(self, parent_pipe, proc):
340 by Teddy Hogeborn
Code cleanup.
2484
        # Call "handle_ipc" for both data and EOF events
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2485
        gobject.io_add_watch(
2486
            parent_pipe.fileno(),
2487
            gobject.IO_IN | gobject.IO_HUP,
2488
            functools.partial(self.handle_ipc,
2489
                              parent_pipe = parent_pipe,
2490
                              proc = proc))
505.1.9 by Teddy Hogeborn
Whitespace
2491
    
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2492
    def handle_ipc(self, source, condition,
2493
                   parent_pipe=None,
2494
                   proc = None,
2495
                   client_object=None):
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2496
        # error, or the other end of multiprocessing.Pipe has closed
577 by Teddy Hogeborn
* mandos (Client.start_checker): Reworded comment.
2497
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2498
            # Wait for other process to exit
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2499
            proc.join()
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
2500
            return False
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2501
        
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2502
        # Read a request from the child
2503
        request = parent_pipe.recv()
2504
        command = request[0]
2505
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2506
        if command == 'init':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2507
            fpr = request[1]
2508
            address = request[2]
2509
            
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2510
            for c in self.clients.itervalues():
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2511
                if c.fingerprint == fpr:
2512
                    client = c
2513
                    break
2514
            else:
24.1.174 by Björn Påhlsson
* Makefile (CFLAGS): Added "-lrt" to include real time library.
2515
                logger.info("Client not found for fingerprint: %s, ad"
2516
                            "dress: %s", fpr, address)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2517
                if self.use_dbus:
2518
                    # Emit D-Bus signal
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2519
                    mandos_dbus_service.ClientNotFound(fpr,
2520
                                                       address[0])
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2521
                parent_pipe.send(False)
2522
                return False
2523
            
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2524
            gobject.io_add_watch(
2525
                parent_pipe.fileno(),
2526
                gobject.IO_IN | gobject.IO_HUP,
2527
                functools.partial(self.handle_ipc,
2528
                                  parent_pipe = parent_pipe,
2529
                                  proc = proc,
2530
                                  client_object = client))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2531
            parent_pipe.send(True)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2532
            # remove the old hook in favor of the new above hook on
2533
            # same fileno
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2534
            return False
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2535
        if command == 'funcall':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2536
            funcname = request[1]
2537
            args = request[2]
2538
            kwargs = request[3]
2539
            
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2540
            parent_pipe.send(('data', getattr(client_object,
2541
                                              funcname)(*args,
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2542
                                                        **kwargs)))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2543
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2544
        if command == 'getattr':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2545
            attrname = request[1]
755 by Teddy Hogeborn
mandos: More Python 3 fixes.
2546
            if isinstance(client_object.__getattribute__(attrname),
2547
                          collections.Callable):
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2548
                parent_pipe.send(('function', ))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2549
            else:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2550
                parent_pipe.send((
2551
                    'data', client_object.__getattribute__(attrname)))
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
2552
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2553
        if command == 'setattr':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2554
            attrname = request[1]
2555
            value = request[2]
2556
            setattr(client_object, attrname, value)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2557
        
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2558
        return True
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
2559
3 by Björn Påhlsson
Python based server
2560
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2561
def rfc3339_duration_to_delta(duration):
2562
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2563
    
2564
    >>> rfc3339_duration_to_delta("P7D")
2565
    datetime.timedelta(7)
2566
    >>> rfc3339_duration_to_delta("PT60S")
2567
    datetime.timedelta(0, 60)
2568
    >>> rfc3339_duration_to_delta("PT60M")
2569
    datetime.timedelta(0, 3600)
2570
    >>> rfc3339_duration_to_delta("PT24H")
2571
    datetime.timedelta(1)
2572
    >>> rfc3339_duration_to_delta("P1W")
2573
    datetime.timedelta(7)
2574
    >>> rfc3339_duration_to_delta("PT5M30S")
2575
    datetime.timedelta(0, 330)
2576
    >>> rfc3339_duration_to_delta("P1DT3M20S")
2577
    datetime.timedelta(1, 200)
2578
    """
2579
    
2580
    # Parsing an RFC 3339 duration with regular expressions is not
2581
    # possible - there would have to be multiple places for the same
2582
    # values, like seconds.  The current code, while more esoteric, is
2583
    # cleaner without depending on a parsing library.  If Python had a
2584
    # built-in library for parsing we would use it, but we'd like to
2585
    # avoid excessive use of external libraries.
2586
    
2587
    # New type for defining tokens, syntax, and semantics all-in-one
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2588
    Token = collections.namedtuple("Token", (
2589
        "regexp",  # To match token; if "value" is not None, must have
2590
                   # a "group" containing digits
2591
        "value",   # datetime.timedelta or None
2592
        "followers"))           # Tokens valid after this token
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2593
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
2594
    # the "duration" ABNF definition in RFC 3339, Appendix A.
2595
    token_end = Token(re.compile(r"$"), None, frozenset())
2596
    token_second = Token(re.compile(r"(\d+)S"),
2597
                         datetime.timedelta(seconds=1),
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2598
                         frozenset((token_end, )))
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2599
    token_minute = Token(re.compile(r"(\d+)M"),
2600
                         datetime.timedelta(minutes=1),
2601
                         frozenset((token_second, token_end)))
2602
    token_hour = Token(re.compile(r"(\d+)H"),
2603
                       datetime.timedelta(hours=1),
2604
                       frozenset((token_minute, token_end)))
2605
    token_time = Token(re.compile(r"T"),
2606
                       None,
2607
                       frozenset((token_hour, token_minute,
2608
                                  token_second)))
2609
    token_day = Token(re.compile(r"(\d+)D"),
2610
                      datetime.timedelta(days=1),
2611
                      frozenset((token_time, token_end)))
2612
    token_month = Token(re.compile(r"(\d+)M"),
2613
                        datetime.timedelta(weeks=4),
2614
                        frozenset((token_day, token_end)))
2615
    token_year = Token(re.compile(r"(\d+)Y"),
2616
                       datetime.timedelta(weeks=52),
2617
                       frozenset((token_month, token_end)))
2618
    token_week = Token(re.compile(r"(\d+)W"),
2619
                       datetime.timedelta(weeks=1),
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2620
                       frozenset((token_end, )))
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2621
    token_duration = Token(re.compile(r"P"), None,
2622
                           frozenset((token_year, token_month,
2623
                                      token_day, token_time,
721 by Teddy Hogeborn
Fix two mutually cancelling bugs.
2624
                                      token_week)))
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2625
    # Define starting values
2626
    value = datetime.timedelta() # Value so far
2627
    found_token = None
753 by Teddy Hogeborn
mandos-ctl: Generate better messages in exceptions.
2628
    followers = frozenset((token_duration, )) # Following valid tokens
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2629
    s = duration                # String left to parse
2630
    # Loop until end token is found
2631
    while found_token is not token_end:
2632
        # Search for any currently valid tokens
2633
        for token in followers:
2634
            match = token.regexp.match(s)
2635
            if match is not None:
2636
                # Token found
2637
                if token.value is not None:
2638
                    # Value found, parse digits
2639
                    factor = int(match.group(1), 10)
2640
                    # Add to value so far
2641
                    value += factor * token.value
2642
                # Strip token from string
2643
                s = token.regexp.sub("", s, 1)
2644
                # Go to found token
2645
                found_token = token
2646
                # Set valid next tokens
2647
                followers = found_token.followers
2648
                break
2649
        else:
2650
            # No currently valid tokens were found
752 by Teddy Hogeborn
mandos: Generate better messages in exceptions.
2651
            raise ValueError("Invalid RFC 3339 duration: {!r}"
2652
                             .format(duration))
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2653
    # End token found
2654
    return value
2655
2656
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2657
def string_to_delta(interval):
2658
    """Parse a string and return a datetime.timedelta
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2659
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2660
    >>> string_to_delta('7d')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2661
    datetime.timedelta(7)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2662
    >>> string_to_delta('60s')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2663
    datetime.timedelta(0, 60)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2664
    >>> string_to_delta('60m')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2665
    datetime.timedelta(0, 3600)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2666
    >>> string_to_delta('24h')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2667
    datetime.timedelta(1)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2668
    >>> string_to_delta('1w')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2669
    datetime.timedelta(7)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2670
    >>> string_to_delta('5m 30s')
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2671
    datetime.timedelta(0, 330)
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2672
    """
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2673
    
2674
    try:
2675
        return rfc3339_duration_to_delta(interval)
2676
    except ValueError:
2677
        pass
2678
    
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2679
    timevalue = datetime.timedelta(0)
2680
    for s in interval.split():
2681
        try:
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2682
            suffix = s[-1]
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
2683
            value = int(s[:-1])
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2684
            if suffix == "d":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2685
                delta = datetime.timedelta(value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2686
            elif suffix == "s":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2687
                delta = datetime.timedelta(0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2688
            elif suffix == "m":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2689
                delta = datetime.timedelta(0, 0, 0, 0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2690
            elif suffix == "h":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2691
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2692
            elif suffix == "w":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2693
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
2694
            else:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2695
                raise ValueError("Unknown suffix {!r}".format(suffix))
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2696
        except IndexError as e:
464 by Teddy Hogeborn
* debian/mandos-client.postrm (purge): Bug fix: update initramfs also
2697
            raise ValueError(*(e.args))
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2698
        timevalue += delta
2699
    return timevalue
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2700
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
2701
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
2702
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2703
    """See daemon(3).  Standard BSD Unix function.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2704
    
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2705
    This should really exist as os.daemon, but it doesn't (yet)."""
2706
    if os.fork():
2707
        sys.exit()
2708
    os.setsid()
2709
    if not nochdir:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2710
        os.chdir("/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
2711
    if os.fork():
2712
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2713
    if not noclose:
2714
        # Close all standard open file descriptors
560.1.1 by teddy at recompile
* mandos: Use os.devnull instead of os.path.devnull. Fix some white
2715
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2716
        if not stat.S_ISCHR(os.fstat(null).st_mode):
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2717
            raise OSError(errno.ENODEV,
2718
                          "{} not a character device"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2719
                          .format(os.devnull))
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2720
        os.dup2(null, sys.stdin.fileno())
2721
        os.dup2(null, sys.stdout.fileno())
2722
        os.dup2(null, sys.stderr.fileno())
2723
        if null > 2:
2724
            os.close(null)
2725
2726
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2727
def main():
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2728
    
379 by Teddy Hogeborn
* mandos: Fix line lengths.
2729
    ##################################################################
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2730
    # Parsing of options, both command line and config file
2731
    
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2732
    parser = argparse.ArgumentParser()
2733
    parser.add_argument("-v", "--version", action="version",
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2734
                        version = "%(prog)s {}".format(version),
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2735
                        help="show version number and exit")
2736
    parser.add_argument("-i", "--interface", metavar="IF",
2737
                        help="Bind to interface IF")
2738
    parser.add_argument("-a", "--address",
2739
                        help="Address to listen for requests on")
2740
    parser.add_argument("-p", "--port", type=int,
2741
                        help="Port number to receive requests on")
2742
    parser.add_argument("--check", action="store_true",
2743
                        help="Run self-test")
2744
    parser.add_argument("--debug", action="store_true",
2745
                        help="Debug mode; run in foreground and log"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2746
                        " to terminal", default=None)
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2747
    parser.add_argument("--debuglevel", metavar="LEVEL",
2748
                        help="Debug level for stdout output")
2749
    parser.add_argument("--priority", help="GnuTLS"
2750
                        " priority string (see GnuTLS documentation)")
2751
    parser.add_argument("--servicename",
2752
                        metavar="NAME", help="Zeroconf service name")
2753
    parser.add_argument("--configdir",
2754
                        default="/etc/mandos", metavar="DIR",
2755
                        help="Directory to search for configuration"
2756
                        " files")
2757
    parser.add_argument("--no-dbus", action="store_false",
2758
                        dest="use_dbus", help="Do not provide D-Bus"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2759
                        " system bus interface", default=None)
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2760
    parser.add_argument("--no-ipv6", action="store_false",
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2761
                        dest="use_ipv6", help="Do not use IPv6",
2762
                        default=None)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2763
    parser.add_argument("--no-restore", action="store_false",
518.1.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
2764
                        dest="restore", help="Do not restore stored"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2765
                        " state", default=None)
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2766
    parser.add_argument("--socket", type=int,
2767
                        help="Specify a file descriptor to a network"
2768
                        " socket to use instead of creating one")
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2769
    parser.add_argument("--statedir", metavar="DIR",
2770
                        help="Directory to save/restore state in")
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2771
    parser.add_argument("--foreground", action="store_true",
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2772
                        help="Run in foreground", default=None)
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2773
    parser.add_argument("--no-zeroconf", action="store_false",
2774
                        dest="zeroconf", help="Do not use Zeroconf",
2775
                        default=None)
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2776
    
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2777
    options = parser.parse_args()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2778
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2779
    if options.check:
2780
        import doctest
654 by Teddy Hogeborn
Fix running of self-tests.
2781
        fail_count, test_count = doctest.testmod()
2782
        sys.exit(os.EX_OK if fail_count == 0 else 1)
3 by Björn Påhlsson
Python based server
2783
    
28 by Teddy Hogeborn
* server.conf: New file.
2784
    # Default values for config file for server-global settings
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2785
    server_defaults = { "interface": "",
2786
                        "address": "",
2787
                        "port": "",
2788
                        "debug": "False",
2789
                        "priority":
742 by Teddy Hogeborn
Add ":!RSA" to GnuTLS priority string, to disallow non-DHE kx.
2790
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
777 by Teddy Hogeborn
Bug fix for GnuTLS 3: be compatible with old 2048-bit DSA keys.
2791
                        ":+SIGN-DSA-SHA256",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2792
                        "servicename": "Mandos",
2793
                        "use_dbus": "True",
2794
                        "use_ipv6": "True",
2795
                        "debuglevel": "",
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2796
                        "restore": "True",
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2797
                        "socket": "",
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2798
                        "statedir": "/var/lib/mandos",
2799
                        "foreground": "False",
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2800
                        "zeroconf": "True",
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2801
                    }
28 by Teddy Hogeborn
* server.conf: New file.
2802
    
2803
    # Parse config file for server-global settings
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2804
    server_config = configparser.SafeConfigParser(server_defaults)
28 by Teddy Hogeborn
* server.conf: New file.
2805
    del server_defaults
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2806
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
2807
    # Convert the SafeConfigParser object to a dict
89 by Teddy Hogeborn
* Makefile: Bug fix: fixed creation of man pages for section 5 pages.
2808
    server_settings = server_config.defaults()
282 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
2809
    # Use the appropriate methods on the non-string config options
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2810
    for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2811
        server_settings[option] = server_config.getboolean("DEFAULT",
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2812
                                                           option)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2813
    if server_settings["port"]:
2814
        server_settings["port"] = server_config.getint("DEFAULT",
2815
                                                       "port")
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2816
    if server_settings["socket"]:
2817
        server_settings["socket"] = server_config.getint("DEFAULT",
2818
                                                         "socket")
591 by Teddy Hogeborn
Merge "--socket" option for server.
2819
        # Later, stdin will, and stdout and stderr might, be dup'ed
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2820
        # over with an opened os.devnull.  But we don't want this to
2821
        # happen with a supplied network socket.
2822
        if 0 <= server_settings["socket"] <= 2:
2823
            server_settings["socket"] = os.dup(server_settings
2824
                                               ["socket"])
28 by Teddy Hogeborn
* server.conf: New file.
2825
    del server_config
2826
    
2827
    # Override the settings from the config file with command line
2828
    # options, if set.
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2829
    for option in ("interface", "address", "port", "debug",
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2830
                   "priority", "servicename", "configdir", "use_dbus",
2831
                   "use_ipv6", "debuglevel", "restore", "statedir",
2832
                   "socket", "foreground", "zeroconf"):
28 by Teddy Hogeborn
* server.conf: New file.
2833
        value = getattr(options, option)
2834
        if value is not None:
2835
            server_settings[option] = value
2836
    del options
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2837
    # Force all strings to be unicode
2838
    for option in server_settings.keys():
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2839
        if isinstance(server_settings[option], bytes):
2840
            server_settings[option] = (server_settings[option]
2841
                                       .decode("utf-8"))
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2842
    # Force all boolean options to be boolean
2843
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2844
                   "foreground", "zeroconf"):
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2845
        server_settings[option] = bool(server_settings[option])
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2846
    # Debug implies foreground
2847
    if server_settings["debug"]:
2848
        server_settings["foreground"] = True
28 by Teddy Hogeborn
* server.conf: New file.
2849
    # Now we have our good server settings in "server_settings"
2850
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2851
    ##################################################################
2852
    
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2853
    if (not server_settings["zeroconf"]
2854
        and not (server_settings["port"]
2855
                 or server_settings["socket"] != "")):
2856
        parser.error("Needs port or socket to work without Zeroconf")
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2857
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2858
    # For convenience
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2859
    debug = server_settings["debug"]
2860
    debuglevel = server_settings["debuglevel"]
2861
    use_dbus = server_settings["use_dbus"]
2862
    use_ipv6 = server_settings["use_ipv6"]
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2863
    stored_state_path = os.path.join(server_settings["statedir"],
2864
                                     stored_state_file)
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2865
    foreground = server_settings["foreground"]
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2866
    zeroconf = server_settings["zeroconf"]
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2867
    
518.1.4 by Björn Påhlsson
restructured logger
2868
    if debug:
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2869
        initlogger(debug, logging.DEBUG)
518.1.4 by Björn Påhlsson
restructured logger
2870
    else:
2871
        if not debuglevel:
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2872
            initlogger(debug)
518.1.4 by Björn Påhlsson
restructured logger
2873
        else:
2874
            level = getattr(logging, debuglevel.upper())
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2875
            initlogger(debug, level)
518.1.4 by Björn Påhlsson
restructured logger
2876
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2877
    if server_settings["servicename"] != "Mandos":
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2878
        syslogger.setFormatter(
2879
            logging.Formatter('Mandos ({}) [%(process)d]:'
2880
                              ' %(levelname)s: %(message)s'.format(
2881
                                  server_settings["servicename"])))
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
2882
    
28 by Teddy Hogeborn
* server.conf: New file.
2883
    # Parse config file with clients
543 by Teddy Hogeborn
* mandos: Break some long lines.
2884
    client_config = configparser.SafeConfigParser(Client
2885
                                                  .client_defaults)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2886
    client_config.read(os.path.join(server_settings["configdir"],
2887
                                    "clients.conf"))
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2888
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2889
    global mandos_dbus_service
2890
    mandos_dbus_service = None
28 by Teddy Hogeborn
* server.conf: New file.
2891
    
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2892
    socketfd = None
2893
    if server_settings["socket"] != "":
2894
        socketfd = server_settings["socket"]
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2895
    tcp_server = MandosServer(
2896
        (server_settings["address"], server_settings["port"]),
2897
        ClientHandler,
2898
        interface=(server_settings["interface"] or None),
2899
        use_ipv6=use_ipv6,
2900
        gnutls_priority=server_settings["priority"],
2901
        use_dbus=use_dbus,
2902
        socketfd=socketfd)
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2903
    if not foreground:
626 by Teddy Hogeborn
* Makefile (CFLAGS, LDFLAGS): Keep default flags from environment.
2904
        pidfilename = "/run/mandos.pid"
636 by Teddy Hogeborn
Fall back to /var/run for pidfile if /run is not a directory.
2905
        if not os.path.isdir("/run/."):
2906
            pidfilename = "/var/run/mandos.pid"
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2907
        pidfile = None
439 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
2908
        try:
751 by Teddy Hogeborn
mandos: Use codecs.open() and print() for PID file.
2909
            pidfile = codecs.open(pidfilename, "w", encoding="utf-8")
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2910
        except IOError as e:
2911
            logger.error("Could not open file %r", pidfilename,
2912
                         exc_info=e)
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
2913
    
566 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2914
    for name in ("_mandos", "mandos", "nobody"):
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2915
        try:
566 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2916
            uid = pwd.getpwnam(name).pw_uid
2917
            gid = pwd.getpwnam(name).pw_gid
2918
            break
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2919
        except KeyError:
566 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2920
            continue
2921
    else:
2922
        uid = 65534
2923
        gid = 65534
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2924
    try:
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2925
        os.setgid(gid)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2926
        os.setuid(uid)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2927
    except OSError as error:
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
2928
        if error.errno != errno.EPERM:
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2929
            raise
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
2930
    
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2931
    if debug:
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2932
        # Enable all possible GnuTLS debugging
2933
        
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2934
        # "Use a log level over 10 to enable all debugging options."
2935
        # - GnuTLS manual
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2936
        gnutls.global_set_log_level(11)
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2937
        
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2938
        @gnutls.log_func
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2939
        def debug_gnutls(level, string):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2940
            logger.debug("GnuTLS: %s", string[:-1])
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2941
        
801 by Teddy Hogeborn
Stop using python-gnutls. Use GnuTLS 3.3 or later directly.
2942
        gnutls.global_set_log_function(debug_gnutls)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2943
        
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
2944
        # Redirect stdin so all checkers get /dev/null
560.1.1 by teddy at recompile
* mandos: Use os.devnull instead of os.path.devnull. Fix some white
2945
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
2946
        os.dup2(null, sys.stdin.fileno())
2947
        if null > 2:
2948
            os.close(null)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2949
    
458 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2950
    # Need to fork before connecting to D-Bus
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2951
    if not foreground:
458 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2952
        # Close all input and output, do double fork, etc.
2953
        daemon()
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2954
    
590 by Teddy Hogeborn
* mandos: Change comment.
2955
    # multiprocessing will use threads, so before we use gobject we
2956
    # need to inform gobject that threads will be used.
518.2.11 by Teddy Hogeborn
* mandos (main): Tell gobject we're (or, rather, multiprocessing is)
2957
    gobject.threads_init()
2958
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2959
    global main_loop
2960
    # From the Avahi example code
560.1.1 by teddy at recompile
* mandos: Use os.devnull instead of os.path.devnull. Fix some white
2961
    DBusGMainLoop(set_as_default=True)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2962
    main_loop = gobject.MainLoop()
2963
    bus = dbus.SystemBus()
2964
    # End of Avahi example code
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2965
    if use_dbus:
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2966
        try:
505 by Björn Påhlsson
The domain name has changed, so the D-Bus bus and interface names must
2967
            bus_name = dbus.service.BusName("se.recompile.Mandos",
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2968
                                            bus,
2969
                                            do_not_queue=True)
2970
            old_bus_name = dbus.service.BusName(
2971
                "se.bsnet.fukt.Mandos", bus,
2972
                do_not_queue=True)
750 by Teddy Hogeborn
mandos: Disable D-Bus if any DBusException is raised when connecting.
2973
        except dbus.exceptions.DBusException as e:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2974
            logger.error("Disabling D-Bus:", exc_info=e)
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2975
            use_dbus = False
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2976
            server_settings["use_dbus"] = False
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2977
            tcp_server.use_dbus = False
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2978
    if zeroconf:
2979
        protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2980
        service = AvahiServiceToSyslog(
2981
            name = server_settings["servicename"],
2982
            servicetype = "_mandos._tcp",
2983
            protocol = protocol,
2984
            bus = bus)
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2985
        if server_settings["interface"]:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
2986
            service.interface = if_nametoindex(
2987
                server_settings["interface"].encode("utf-8"))
458 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2988
    
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
2989
    global multiprocessing_manager
2990
    multiprocessing_manager = multiprocessing.Manager()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2991
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
2992
    client_class = Client
2993
    if use_dbus:
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
2994
        client_class = functools.partial(ClientDBus, bus = bus)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2995
    
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2996
    client_settings = Client.config_parser(client_config)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2997
    old_client_settings = {}
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2998
    clients_data = {}
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2999
    
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
3000
    # This is used to redirect stdout and stderr for checker processes
3001
    global wnull
3002
    wnull = open(os.devnull, "w") # A writable /dev/null
3003
    # Only used if server is running in foreground but not in debug
3004
    # mode
3005
    if debug or not foreground:
3006
        wnull.close()
3007
    
518.1.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
3008
    # Get client data and settings from last running state.
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3009
    if server_settings["restore"]:
3010
        try:
3011
            with open(stored_state_path, "rb") as stored_state:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3012
                clients_data, old_client_settings = pickle.load(
3013
                    stored_state)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3014
            os.remove(stored_state_path)
3015
        except IOError as e:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
3016
            if e.errno == errno.ENOENT:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3017
                logger.warning("Could not load persistent state:"
3018
                               " {}".format(os.strerror(e.errno)))
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
3019
            else:
3020
                logger.critical("Could not load persistent state:",
3021
                                exc_info=e)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3022
                raise
505.1.23 by Teddy Hogeborn
* mandos (main): Handle EOFError when reading state file.
3023
        except EOFError as e:
3024
            logger.warning("Could not load persistent state: "
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3025
                           "EOFError:",
3026
                           exc_info=e)
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
3027
    
518.1.9 by Björn Påhlsson
renamed variables
3028
    with PGPEngine() as pgp:
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
3029
        for client_name, client in clients_data.items():
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
3030
            # Skip removed clients
3031
            if client_name not in client_settings:
3032
                continue
3033
            
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3034
            # Decide which value to use after restoring saved state.
3035
            # We have three different values: Old config file,
3036
            # new config file, and saved state.
3037
            # New config value takes precedence if it differs from old
3038
            # config value, otherwise use saved state.
3039
            for name, value in client_settings[client_name].items():
3040
                try:
3041
                    # For each value in new config, check if it
3042
                    # differs from the old config value (Except for
3043
                    # the "secret" attribute)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3044
                    if (name != "secret"
3045
                        and (value !=
3046
                             old_client_settings[client_name][name])):
518.2.10 by Teddy Hogeborn
* mandos (main): Bug fix: Syntax fix when restoring settings.
3047
                        client[name] = value
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3048
                except KeyError:
3049
                    pass
3050
            
3051
            # Clients who has passed its expire date can still be
749.1.1 by Teddy Hogeborn
mandos: Use multiprocessing module to run checkers.
3052
            # enabled if its last checker was successful.  A Client
552 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
3053
            # whose checker succeeded before we stored its state is
3054
            # assumed to have successfully run all checkers during
3055
            # downtime.
518.2.8 by Teddy Hogeborn
* mandos (ClientDBus.Host_dbus_property,
3056
            if client["enabled"]:
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
3057
                if datetime.datetime.utcnow() >= client["expires"]:
3058
                    if not client["last_checked_ok"]:
3059
                        logger.warning(
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
3060
                            "disabling client {} - Client never "
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3061
                            "performed a successful checker".format(
3062
                                client_name))
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
3063
                        client["enabled"] = False
3064
                    elif client["last_checker_status"] != 0:
3065
                        logger.warning(
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
3066
                            "disabling client {} - Client last"
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3067
                            " checker failed with error code"
3068
                            " {}".format(
3069
                                client_name,
3070
                                client["last_checker_status"]))
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3071
                        client["enabled"] = False
3072
                    else:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3073
                        client["expires"] = (
3074
                            datetime.datetime.utcnow()
3075
                            + client["timeout"])
542 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
3076
                        logger.debug("Last checker succeeded,"
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3077
                                     " keeping {} enabled".format(
3078
                                         client_name))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
3079
            try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3080
                client["secret"] = pgp.decrypt(
3081
                    client["encrypted_secret"],
3082
                    client_settings[client_name]["secret"])
518.1.9 by Björn Påhlsson
renamed variables
3083
            except PGPError:
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3084
                # If decryption fails, we use secret from new settings
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3085
                logger.debug("Failed to decrypt {} old secret".format(
3086
                    client_name))
3087
                client["secret"] = (client_settings[client_name]
3088
                                    ["secret"])
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3089
    
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
3090
    # Add/remove clients based on new changes made to config
543 by Teddy Hogeborn
* mandos: Break some long lines.
3091
    for client_name in (set(old_client_settings)
3092
                        - set(client_settings)):
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
3093
        del clients_data[client_name]
543 by Teddy Hogeborn
* mandos: Break some long lines.
3094
    for client_name in (set(client_settings)
3095
                        - set(old_client_settings)):
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
3096
        clients_data[client_name] = client_settings[client_name]
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
3097
    
552 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
3098
    # Create all client objects
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
3099
    for client_name, client in clients_data.items():
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
3100
        tcp_server.clients[client_name] = client_class(
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3101
            name = client_name,
3102
            settings = client,
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
3103
            server_settings = server_settings)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
3104
    
341 by Teddy Hogeborn
Code cleanup and one bug fix.
3105
    if not tcp_server.clients:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3106
        logger.warning("No clients defined")
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
3107
    
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
3108
    if not foreground:
3109
        if pidfile is not None:
751 by Teddy Hogeborn
mandos: Use codecs.open() and print() for PID file.
3110
            pid = os.getpid()
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
3111
            try:
3112
                with pidfile:
751 by Teddy Hogeborn
mandos: Use codecs.open() and print() for PID file.
3113
                    print(pid, file=pidfile)
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
3114
            except IOError:
3115
                logger.error("Could not write to file %r with PID %d",
3116
                             pidfilename, pid)
3117
        del pidfile
439 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
3118
        del pidfilename
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
3119
    
28 by Teddy Hogeborn
* server.conf: New file.
3120
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
3121
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
3122
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3123
    if use_dbus:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3124
        
3125
        @alternate_dbus_interfaces(
3126
            { "se.recompile.Mandos": "se.bsnet.fukt.Mandos" })
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3127
        class MandosDBusService(DBusObjectWithObjectManager):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3128
            """A D-Bus proxy object"""
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3129
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3130
            def __init__(self):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3131
                dbus.service.Object.__init__(self, bus, "/")
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3132
            
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
3133
            _interface = "se.recompile.Mandos"
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3134
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3135
            @dbus.service.signal(_interface, signature="o")
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
3136
            def ClientAdded(self, objpath):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3137
                "D-Bus signal"
3138
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3139
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3140
            @dbus.service.signal(_interface, signature="ss")
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
3141
            def ClientNotFound(self, fingerprint, address):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
3142
                "D-Bus signal"
3143
                pass
3144
            
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3145
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3146
                               "true"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3147
            @dbus.service.signal(_interface, signature="os")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3148
            def ClientRemoved(self, objpath, name):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3149
                "D-Bus signal"
3150
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3151
            
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3152
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3153
                               "true"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3154
            @dbus.service.method(_interface, out_signature="ao")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3155
            def GetAllClients(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
3156
                "D-Bus method"
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3157
                return dbus.Array(c.dbus_object_path for c in
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3158
                                  tcp_server.clients.itervalues())
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3159
            
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3160
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
3161
                               "true"})
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
3162
            @dbus.service.method(_interface,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3163
                                 out_signature="a{oa{sv}}")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3164
            def GetAllClientsWithProperties(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
3165
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3166
                return dbus.Dictionary(
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3167
                    { c.dbus_object_path: c.GetAll(
3168
                        "se.recompile.Mandos.Client")
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
3169
                      for c in tcp_server.clients.itervalues() },
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3170
                    signature="oa{sv}")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3171
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3172
            @dbus.service.method(_interface, in_signature="o")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3173
            def RemoveClient(self, object_path):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
3174
                "D-Bus method"
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3175
                for c in tcp_server.clients.itervalues():
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3176
                    if c.dbus_object_path == object_path:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3177
                        del tcp_server.clients[c.name]
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
3178
                        c.remove_from_connection()
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3179
                        # Don't signal the disabling
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
3180
                        c.disable(quiet=True)
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3181
                        # Emit D-Bus signal for removal
3182
                        self.client_removed_signal(c)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3183
                        return
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
3184
                raise KeyError(object_path)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3185
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3186
            del _interface
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3187
            
3188
            @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
3189
                                 out_signature = "a{oa{sa{sv}}}")
3190
            def GetManagedObjects(self):
3191
                """D-Bus method"""
3192
                return dbus.Dictionary(
3193
                    { client.dbus_object_path:
3194
                      dbus.Dictionary(
3195
                          { interface: client.GetAll(interface)
3196
                            for interface in
3197
                                 client._get_all_interface_names()})
3198
                      for client in tcp_server.clients.values()})
3199
            
3200
            def client_added_signal(self, client):
3201
                """Send the new standard signal and the old signal"""
3202
                if use_dbus:
3203
                    # New standard signal
3204
                    self.InterfacesAdded(
3205
                        client.dbus_object_path,
3206
                        dbus.Dictionary(
3207
                            { interface: client.GetAll(interface)
3208
                              for interface in
3209
                              client._get_all_interface_names()}))
3210
                    # Old signal
3211
                    self.ClientAdded(client.dbus_object_path)
3212
            
3213
            def client_removed_signal(self, client):
3214
                """Send the new standard signal and the old signal"""
3215
                if use_dbus:
3216
                    # New standard signal
3217
                    self.InterfacesRemoved(
3218
                        client.dbus_object_path,
3219
                        client._get_all_interface_names())
3220
                    # Old signal
3221
                    self.ClientRemoved(client.dbus_object_path,
3222
                                       client.name)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
3223
        
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
3224
        mandos_dbus_service = MandosDBusService()
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
3225
    
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
3226
    def cleanup():
3227
        "Cleanup function; run on exit"
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
3228
        if zeroconf:
3229
            service.cleanup()
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
3230
        
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
3231
        multiprocessing.active_children()
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
3232
        wnull.close()
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3233
        if not (tcp_server.clients or client_settings):
3234
            return
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
3235
        
521 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-crypto".
3236
        # Store client before exiting. Secrets are encrypted with key
3237
        # based on what config file has. If config file is
3238
        # removed/edited, old secret will thus be unrecovable.
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
3239
        clients = {}
518.1.9 by Björn Påhlsson
renamed variables
3240
        with PGPEngine() as pgp:
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3241
            for client in tcp_server.clients.itervalues():
3242
                key = client_settings[client.name]["secret"]
518.1.9 by Björn Påhlsson
renamed variables
3243
                client.encrypted_secret = pgp.encrypt(client.secret,
3244
                                                      key)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3245
                client_dict = {}
3246
                
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
3247
                # A list of attributes that can not be pickled
3248
                # + secret.
723.1.2 by Teddy Hogeborn
Use set literals.
3249
                exclude = { "bus", "changedstate", "secret",
3250
                            "checker", "server_settings" }
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3251
                for name, typ in inspect.getmembers(dbus.service
3252
                                                    .Object):
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3253
                    exclude.add(name)
3254
                
3255
                client_dict["encrypted_secret"] = (client
3256
                                                   .encrypted_secret)
3257
                for attr in client.client_structure:
3258
                    if attr not in exclude:
3259
                        client_dict[attr] = getattr(client, attr)
3260
                
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
3261
                clients[client.name] = client_dict
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3262
                del client_settings[client.name]["secret"]
3263
        
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3264
        try:
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3265
            with tempfile.NamedTemporaryFile(
3266
                    mode='wb',
3267
                    suffix=".pickle",
3268
                    prefix='clients-',
3269
                    dir=os.path.dirname(stored_state_path),
3270
                    delete=False) as stored_state:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3271
                pickle.dump((clients, client_settings), stored_state)
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3272
                tempname = stored_state.name
505.1.26 by teddy at bsnet
* mandos (main/cleanup): Write new file, then rename. Use
3273
            os.rename(tempname, stored_state_path)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
3274
        except (IOError, OSError) as e:
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
3275
            if not debug:
3276
                try:
3277
                    os.remove(tempname)
3278
                except NameError:
3279
                    pass
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
3280
            if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST):
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
3281
                logger.warning("Could not save persistent state: {}"
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
3282
                               .format(os.strerror(e.errno)))
3283
            else:
3284
                logger.warning("Could not save persistent state:",
3285
                               exc_info=e)
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
3286
                raise
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
3287
        
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3288
        # Delete all clients, and settings from config
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
3289
        while tcp_server.clients:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3290
            name, client = tcp_server.clients.popitem()
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
3291
            if use_dbus:
3292
                client.remove_from_connection()
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3293
            # Don't signal the disabling
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
3294
            client.disable(quiet=True)
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3295
            # Emit D-Bus signal for removal
796 by Teddy Hogeborn
On cleanup, Signal ClientRemoved only if using D-Bus.
3296
            if use_dbus:
3297
                mandos_dbus_service.client_removed_signal(client)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3298
        client_settings.clear()
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
3299
    
3300
    atexit.register(cleanup)
3301
    
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3302
    for client in tcp_server.clients.itervalues():
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
3303
        if use_dbus:
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
3304
            # Emit D-Bus signal for adding
3305
            mandos_dbus_service.client_added_signal(client)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
3306
        # Need to initiate checking of clients
3307
        if client.enabled:
3308
            client.init_checker()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
3309
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
3310
    tcp_server.enable()
3311
    tcp_server.server_activate()
3312
    
28 by Teddy Hogeborn
* server.conf: New file.
3313
    # Find out what port we got
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
3314
    if zeroconf:
3315
        service.port = tcp_server.socket.getsockname()[1]
314 by Teddy Hogeborn
Support not using IPv6 in server:
3316
    if use_ipv6:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3317
        logger.info("Now listening on address %r, port %d,"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
3318
                    " flowinfo %d, scope_id %d",
3319
                    *tcp_server.socket.getsockname())
314 by Teddy Hogeborn
Support not using IPv6 in server:
3320
    else:                       # IPv4
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
3321
        logger.info("Now listening on address %r, port %d",
3322
                    *tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
3323
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
3324
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
3325
    
3326
    try:
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
3327
        if zeroconf:
3328
            # From the Avahi example code
3329
            try:
3330
                service.activate()
3331
            except dbus.exceptions.DBusException as error:
3332
                logger.critical("D-Bus Exception", exc_info=error)
3333
                cleanup()
3334
                sys.exit(1)
3335
            # End of Avahi example code
28 by Teddy Hogeborn
* server.conf: New file.
3336
        
3337
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
3338
                             lambda *args, **kwargs:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
3339
                             (tcp_server.handle_request
3340
                              (*args[2:], **kwargs) or True))
28 by Teddy Hogeborn
* server.conf: New file.
3341
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3342
        logger.debug("Starting main loop")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
3343
        main_loop.run()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
3344
    except AvahiError as error:
565 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
3345
        logger.critical("Avahi Error", exc_info=error)
401 by Teddy Hogeborn
* README (FAQ): Fix typo.
3346
        cleanup()
28 by Teddy Hogeborn
* server.conf: New file.
3347
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
3348
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
3349
        if debug:
463.1.5 by teddy at bsnet
* mandos: Use unicode string literals.
3350
            print("", file=sys.stderr)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3351
        logger.debug("Server received KeyboardInterrupt")
3352
    logger.debug("Server exiting")
401 by Teddy Hogeborn
* README (FAQ): Fix typo.
3353
    # Must run before the D-Bus bus name gets deregistered
3354
    cleanup()
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
3355
746 by Teddy Hogeborn
mandos: Do minor formatting and whitespace adjustments.
3356
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
3357
if __name__ == '__main__':
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
3358
    main()