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