/mandos/release

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