/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
723.1.1 by Teddy Hogeborn
Require Python 2.7.
1
#!/usr/bin/python2.7
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2
# -*- mode: python; coding: utf-8 -*-
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
3
# 
4
# Mandos server - give out binary blobs to connecting clients.
5
# 
6
# This program is partly derived from an example program for an Avahi
7
# service publisher, downloaded from
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
336 by Teddy Hogeborn
Code cleanup.
9
# methods "add", "remove", "server_state_changed",
10
# "entry_group_state_changed", "cleanup", and "activate" in the
11
# "AvahiService" class, and some lines in "main".
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
12
# 
28 by Teddy Hogeborn
* server.conf: New file.
13
# Everything else is
649 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
# 
505.1.2 by Teddy Hogeborn
Change "fukt.bsnet.se" to "recompile.se" throughout.
31
# Contact the authors at <mandos@recompile.se>.
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
32
# 
3 by Björn Påhlsson
Python based server
33
463.1.5 by teddy at bsnet
* mandos: Use unicode string literals.
34
from __future__ import (division, absolute_import, print_function,
35
                        unicode_literals)
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
36
579 by Teddy Hogeborn
* mandos: Use all new builtins.
37
from future_builtins import *
38
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
39
import SocketServer as socketserver
3 by Björn Påhlsson
Python based server
40
import socket
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
41
import argparse
3 by Björn Påhlsson
Python based server
42
import datetime
43
import errno
44
import gnutls.crypto
45
import gnutls.connection
46
import gnutls.errors
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
47
import gnutls.library.functions
48
import gnutls.library.constants
49
import gnutls.library.types
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
50
import ConfigParser as configparser
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
51
import sys
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
52
import re
53
import os
54
import signal
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
55
import subprocess
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
56
import atexit
57
import stat
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
58
import logging
59
import logging.handlers
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
60
import pwd
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
61
import contextlib
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
62
import struct
63
import fcntl
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
64
import functools
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
65
import cPickle as pickle
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
66
import multiprocessing
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
67
import types
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
68
import binascii
69
import tempfile
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
70
import itertools
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
71
import collections
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
72
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
73
import dbus
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
74
import dbus.service
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
75
import gobject
76
import avahi
77
from dbus.mainloop.glib import DBusGMainLoop
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
78
import ctypes
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
79
import ctypes.util
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
80
import xml.dom.minidom
81
import inspect
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
82
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
83
try:
84
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
85
except AttributeError:
86
    try:
87
        from IN import SO_BINDTODEVICE
88
    except ImportError:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
89
        SO_BINDTODEVICE = None
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
90
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
91
if sys.version_info.major == 2:
92
    str = unicode
93
237.4.53 by Teddy Hogeborn
* Makefile (version): Changed to "1.6.8".
94
version = "1.6.8"
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
95
stored_state_file = "clients.pickle"
13 by Björn Påhlsson
Added following support:
96
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
97
logger = logging.getLogger()
655 by Teddy Hogeborn
Allow self-tests to run without needing /dev/log.
98
syslogger = None
13 by Björn Påhlsson
Added following support:
99
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
100
try:
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,
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
110
                                struct.pack(b"16s16x", interface))
111
        interface_index = struct.unpack("I", ifreq[16:20])[0]
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
112
        return interface_index
113
114
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
115
def initlogger(debug, level=logging.WARNING):
518.1.4 by Björn Påhlsson
restructured logger
116
    """init logger and add loglevel"""
117
    
698 by Teddy Hogeborn
Fix mandos server "--servicename" option, broken since 1.6.4.
118
    global syslogger
655 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,
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
122
                  address = "/dev/log"))
518.1.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
    
530 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)
518.1.4 by Björn Påhlsson
restructured logger
135
    logger.setLevel(level)
28 by Teddy Hogeborn
* server.conf: New file.
136
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
137
518.1.9 by Björn Påhlsson
renamed variables
138
class PGPError(Exception):
139
    """Exception if encryption/decryption fails"""
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
140
    pass
141
142
518.1.9 by Björn Påhlsson
renamed variables
143
class PGPEngine(object):
518.2.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-")
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
147
        self.gnupgargs = ['--batch',
148
                          '--home', self.tempdir,
149
                          '--force-mdc',
150
                          '--quiet',
151
                          '--no-use-agent']
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
152
    
153
    def __enter__(self):
154
        return self
155
    
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
156
    def __exit__(self, exc_type, exc_value, traceback):
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
157
        self._cleanup()
158
        return False
159
    
160
    def __del__(self):
161
        self._cleanup()
162
    
163
    def _cleanup(self):
164
        if self.tempdir is not None:
165
            # Delete contents of tempdir
166
            for root, dirs, files in os.walk(self.tempdir,
167
                                             topdown = False):
168
                for filename in files:
169
                    os.remove(os.path.join(root, filename))
170
                for dirname in dirs:
171
                    os.rmdir(os.path.join(root, dirname))
172
            # Remove tempdir
173
            os.rmdir(self.tempdir)
174
            self.tempdir = None
175
    
176
    def password_encode(self, password):
177
        # Passphrase can not be empty and can not contain newlines or
178
        # NUL bytes.  So we prefix it and hex encode it.
630 by Teddy Hogeborn
* mandos (PGPEngine.password_encode): Bug fix: GnuPG can't handle
179
        encoded = b"mandos" + binascii.hexlify(password)
180
        if len(encoded) > 2048:
181
            # GnuPG can't handle long passwords, so encode differently
182
            encoded = (b"mandos" + password.replace(b"\\", b"\\\\")
183
                       .replace(b"\n", b"\\n")
184
                       .replace(b"\0", b"\\x00"))
185
        return encoded
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
186
    
187
    def encrypt(self, data, password):
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
188
        passphrase = self.password_encode(password)
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)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
203
        return ciphertext
204
    
205
    def decrypt(self, data, password):
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
206
        passphrase = self.password_encode(password)
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)
518.2.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):
242 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
729 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.
331 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.
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
244
    name: string; Example: 'Mandos'
245
    type: string; Example: '_mandos._tcp'.
614 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
336 by Teddy Hogeborn
Code cleanup.
254
    group: D-Bus Entry Group
255
    server: D-Bus Server
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
256
    bus: dbus.SystemBus()
28 by Teddy Hogeborn
* server.conf: New file.
257
    """
573 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,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
261
                 domain = "", host = "", max_renames = 32768,
337 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
314 by Teddy Hogeborn
Support not using IPv6 in server:
272
        self.protocol = protocol
336 by Teddy Hogeborn
Code cleanup.
273
        self.group = None       # our entry group
274
        self.server = None
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
275
        self.bus = bus
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
276
        self.entry_group_state_changed_match = None
573 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:
463.1.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)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
284
            raise AvahiServiceError("Too many renames")
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
285
        self.name = str(self.server
286
                        .GetAlternativeServiceName(self.name))
463.1.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()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
292
        except dbus.exceptions.DBusException as error:
565 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
573 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"""
482 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
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
303
        if self.group is not None:
304
            self.group.Reset()
573 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"""
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
308
        self.remove()
483 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)
482 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(
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
316
                'StateChanged', self.entry_group_state_changed))
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
317
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
336 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()
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
328
    
336 by Teddy Hogeborn
Code cleanup.
329
    def entry_group_state_changed(self, state, error):
330
        """Derived from the Avahi example code"""
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
331
        logger.debug("Avahi entry group state change: %i", state)
336 by Teddy Hogeborn
Code cleanup.
332
        
333
        if state == avahi.ENTRY_GROUP_ESTABLISHED:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
334
            logger.debug("Zeroconf service established.")
336 by Teddy Hogeborn
Code cleanup.
335
        elif state == avahi.ENTRY_GROUP_COLLISION:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
336
            logger.info("Zeroconf service name collision.")
336 by Teddy Hogeborn
Code cleanup.
337
            self.rename()
338
        elif state == avahi.ENTRY_GROUP_FAILURE:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
339
            logger.critical("Avahi: Error in group state changed %s",
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
340
                            str(error))
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
341
            raise AvahiGroupError("State changed: {!s}"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
342
                                  .format(error))
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
343
    
336 by Teddy Hogeborn
Code cleanup.
344
    def cleanup(self):
345
        """Derived from the Avahi example code"""
483 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,
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
350
                    dbus.exceptions.DBusException):
483 by Teddy Hogeborn
* mandos: Never call .Reset() on a defunct Avahi entry group.
351
                pass
352
            self.group = None
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
353
        self.remove()
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
354
    
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
355
    def server_state_changed(self, state, error=None):
336 by Teddy Hogeborn
Code cleanup.
356
        """Derived from the Avahi example code"""
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
357
        logger.debug("Avahi server state change: %i", state)
482 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:
483 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()
336 by Teddy Hogeborn
Code cleanup.
372
        elif state == avahi.SERVER_RUNNING:
373
            self.add()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
374
        else:
483 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)
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
379
    
336 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(
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
384
                self.bus.get_object(avahi.DBUS_NAME,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
385
                                    avahi.DBUS_PATH_SERVER,
386
                                    follow_name_owner_changes=True),
336 by Teddy Hogeborn
Code cleanup.
387
                avahi.DBUS_INTERFACE_SERVER)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
388
        self.server.connect_to_signal("StateChanged",
336 by Teddy Hogeborn
Code cleanup.
389
                                 self.server_state_changed)
390
        self.server_state_changed(self.server.GetState())
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
391
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
392
505.1.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
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
398
                               ('Mandos ({}) [%(process)d]:'
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
399
                                ' %(levelname)s: %(message)s'
400
                                .format(self.name)))
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
401
        return ret
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
402
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
403
328 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.
331 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:
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
408
    approved:   bool(); 'None' if not yet approved/disapproved
438 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
238 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.
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
414
    checker_callback_tag: a gobject event source tag, or None
438 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.
438 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
518.1.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
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
423
    current_checker_command: string; current running checker_command
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
424
    disable_initiator_tag: a gobject event source tag, or None
438 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
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
430
    last_approval_request: datetime.datetime(); (UTC) or None
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
431
    last_checked_ok: datetime.datetime(); (UTC) or None
521 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-crypto".
432
    last_checker_status: integer between 0 and 255 reflecting exit
518.1.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
433
                         status of last checker. -1 reflects crashed
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
434
                         checker, -2 means no checker completed yet.
518.2.3 by Teddy Hogeborn
Make "enabled" a client config option.
435
    last_enabled: datetime.datetime(); (UTC) or None
438 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
552 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
441
    extended_timeout:   extra long timeout when secret has been sent
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
442
    runtime_expansions: Allowed attributes for runtime expansion.
497 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
617 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
    """
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
447
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
448
    runtime_expansions = ("approval_delay", "approval_duration",
584 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",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
452
                          "last_enabled", "name", "timeout")
609 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",
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
456
                        "checker": "fping -q -- %%(host)s",
457
                        "host": "",
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
458
                        "approval_delay": "PT0S",
459
                        "approval_duration": "PT1S",
518.1.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
    
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
464
    @staticmethod
465
    def config_parser(config):
542 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:
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
467
        { client_name: {setting_name: value, ...}, ...}
542 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
        """
518.1.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
            
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
477
            client["host"] = section["host"]
518.1.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")
543 by Teddy Hogeborn
* mandos: Break some long lines.
481
            client["enabled"] = config.getboolean(client_name,
482
                                                  "enabled")
518.1.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:
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
494
                raise TypeError("No secret or secfile for section {}"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
495
                                .format(section))
518.1.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"])
518.1.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
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
507
            client["last_checker_status"] = -2
542 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
508
        
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
509
        return settings
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
510
    
617 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):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
512
        self.name = name
617 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
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
516
        # adding all client settings
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
517
        for setting, value in settings.items():
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
518
            setattr(self, setting, value)
519
        
542 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
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
529
        
463.1.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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
534
        logger.debug("  Fingerprint: %s", self.fingerprint)
543 by Teddy Hogeborn
* mandos: Break some long lines.
535
        self.created = settings.get("created",
536
                                    datetime.datetime.utcnow())
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
537
        
518.1.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
239 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
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
543
        self.current_checker_command = None
518.2.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
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
546
        self.changedstate = (multiprocessing_manager
547
                             .Condition(multiprocessing_manager
548
                                        .Lock()))
518.1.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()
521 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-crypto".
551
                                 if not attr.startswith("_")]
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
552
        self.client_structure.append("client_structure")
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
553
        
518.1.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),
521 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-crypto".
555
                                          lambda obj:
556
                                              isinstance(obj,
557
                                                         property)):
518.1.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
    
520 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):
520 by Björn Påhlsson
merge persistent state
563
        with self.changedstate:
564
            self.changedstate.notify_all()
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
565
    
239 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"""
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
568
        if getattr(self, "enabled", False):
341 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
239 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()
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
574
        self.init_checker()
581 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
    
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
577
    def disable(self, quiet=True):
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
578
        """Disable this client."""
463.1.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
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
581
        if not quiet:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
582
            logger.info("Disabling client %s", self.name)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
583
        if getattr(self, "disable_initiator_tag", None) is not None:
239 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
581 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()
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
591
        self.enabled = False
581 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):
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
598
        self.disable()
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
599
    
518.1.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.
581 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)
518.1.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
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
606
                                      (int(self.interval
607
                                           .total_seconds() * 1000),
518.1.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
581 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)
518.1.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
723.1.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))
518.1.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()
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
618
    
238 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
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
623
        if os.WIFEXITED(condition):
518.2.8 by Teddy Hogeborn
* mandos (ClientDBus.Host_dbus_property,
624
            self.last_checker_status = os.WEXITSTATUS(condition)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
625
            if self.last_checker_status == 0:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
626
                logger.info("Checker for %(name)s succeeded",
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
627
                            vars(self))
281 by Teddy Hogeborn
* mandos (Client.bump_timeout): Renamed to "checked_ok". All callers
628
                self.checked_ok()
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
629
            else:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
630
                logger.info("Checker for %(name)s failed",
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
631
                            vars(self))
632
        else:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
633
            self.last_checker_status = -1
463.1.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
    
556 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
505.1.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)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
649
            self.disable_initiator_tag = None
505.1.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
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
652
                                          (int(timeout.total_seconds()
653
                                               * 1000), self.disable))
505.1.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
    
442 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.
331 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
577 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
411 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.
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
672
        
673
        # If a checker exists, make sure it is not a zombie
383 by Teddy Hogeborn
* mandos (Client.start_checker): Bug fix: Fix race condition with
674
        try:
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
675
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
642 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
383 by Teddy Hogeborn
* mandos (Client.start_checker): Bug fix: Fix race condition with
681
        else:
315 by Teddy Hogeborn
* mandos (Client.current_checker_command): New attribute.
682
            if pid:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
683
                logger.warning("Checker was a zombie")
315 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:
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
689
            # Escape attributes for the shell
723.1.3 by Teddy Hogeborn
Use dictionary comprehensions.
690
            escaped_attrs = { attr:
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
691
                                  re.escape(str(getattr(self, attr)))
723.1.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:
583 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
328 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:
463.1.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.)
617 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,
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
717
                                                shell=True, cwd="/",
718
                                                **popen_args)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
719
            except OSError as error:
565 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
720
                logger.error("Failed to start subprocess",
721
                             exc_info=error)
610 by Teddy Hogeborn
* mandos (Client.start_checker): Return True (retry later) if starting
722
                return True
583 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.
610 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:
611 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)
610 by Teddy Hogeborn
* mandos (Client.start_checker): Return True (retry later) if starting
736
                    return True
737
                raise
583 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
463.1.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
463.1.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:
564 by Teddy Hogeborn
* mandos (Client.stop_checker): Use new Popen.terminate() method.
753
            self.checker.terminate()
400 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:
564 by Teddy Hogeborn
* mandos (Client.stop_checker): Use new Popen.terminate() method.
756
            #    self.checker.kill()
482 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
518.1.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
463.1.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):
380 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
    """
411 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:
463.1.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'"
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
779
                         " signature {!r}".format(signature))
380 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__
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
786
        if func._dbus_name.endswith("_dbus_property"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
787
            func._dbus_name = func._dbus_name[:-14]
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
788
        func._dbus_get_args_options = {'byte_arrays': byte_arrays }
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
789
        return func
790
    return decorator
791
792
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
793
def dbus_interface_annotations(dbus_interface):
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
794
    """Decorator for marking functions returning interface annotations
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
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
561.1.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
    
732 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"})
561.1.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
380 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
    """
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
833
    pass
380 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
    
380 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
560.1.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
        """
723.1.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),
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
863
                                   False)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
864
    
560.1.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):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
866
        """Returns a generator of (name, attribute) pairs
867
        """
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
868
        return ((getattr(athing.__get__(self), "_dbus_name",
869
                         name),
560.1.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__
560.1.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)))
380 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__:
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
881
            for name, value in (inspect.getmembers
560.1.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"))):
505.1.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
        
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
888
        # No such property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
889
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
890
                                   + interface_name + "."
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
891
                                   + property_name)
892
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
893
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
894
                         out_signature="v")
380 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)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
899
        if prop._dbus_access == "write":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
900
            raise DBusPropertyAccessException(property_name)
901
        value = prop()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
902
        if not hasattr(value, "variant_level"):
380 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
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
906
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
380 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)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
911
        if prop._dbus_access == "read":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
912
            raise DBusPropertyAccessException(property_name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
913
        if prop._dbus_get_args_options["byte_arrays"]:
411 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".
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
916
            if prop._dbus_signature != "ay":
649 by Teddy Hogeborn
Minor code fix; raise only exception instances, not classes.
917
                raise ValueError("Byte arrays not supported for non-"
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
918
                                 "'ay' signature {!r}"
649 by Teddy Hogeborn
Minor code fix; raise only exception instances, not classes.
919
                                 .format(prop._dbus_signature))
546 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))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
922
        prop(value)
923
    
463.1.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}")
380 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
        
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
930
        Note: Will not include properties with access="write".
931
        """
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
932
        properties = {}
560.1.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"):
380 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
939
            if prop._dbus_access == "write":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
940
                continue
941
            value = prop()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
942
            if not hasattr(value, "variant_level"):
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
943
                properties[name] = value
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
944
                continue
518.1.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")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
948
    
732 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
    
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
957
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
958
                         out_signature="s",
959
                         path_keyword='object_path',
960
                         connection_keyword='connection')
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
961
    def Introspect(self, object_path, connection):
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
962
        """Overloading of standard D-Bus method.
963
        
964
        Inserts property tags and interface annotation tags.
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
965
        """
966
        xmlstring = dbus.service.Object.Introspect(self, object_path,
386 by Teddy Hogeborn
* mandos (DBusObjectWithProperties.Introspect): Add the name
967
                                                   connection)
387 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):
463.1.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)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
975
                return e
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
976
            for if_tag in document.getElementsByTagName("interface"):
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
977
                # Add property tags
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
978
                for tag in (make_tag(document, name, prop)
979
                            for name, prop
560.1.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")
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
981
                            if prop._dbus_interface
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
982
                            == if_tag.getAttribute("name")):
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
983
                    if_tag.appendChild(tag)
561.1.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
                                               {}))
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
997
                        for name, value in annots.items():
561.1.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)
560.1.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(
576 by Teddy Hogeborn
* mandos (DBusObjectWithProperties.Introspect): Use
1005
                    itertools.chain.from_iterable(
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
1006
                        annotations().items()
576 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")
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
1010
                        )).items():
561.1.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)
387 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
463.1.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")
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1031
            document.unlink()
1032
        except (AttributeError, xml.dom.DOMException,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1033
                xml.parsers.expat.ExpatError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1034
            logger.error("Failed to override Introspection method",
565 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
1035
                         exc_info=error)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1036
        return xmlstring
1037
1038
587 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
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1046
575 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
    
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
1053
    @alternate_dbus_interfaces({"org.example.Interface":
1054
                                    "net.example.AlternateInterface"})
575 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).
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1069
    """
575 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 (
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
1072
            alt_interface_names.items()):
575 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):
505.1.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
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1081
                    .startswith(orig_interface_name)):
505.1.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
575 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)
505.1.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):
614 by Teddy Hogeborn
* mandos: Comment changes.
1091
                    # Extract the original non-method undecorated
1092
                    # function by black magic
505.1.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)))
561.1.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
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1115
                    # Define a creator of a function to call both the
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1116
                    # original and alternate functions, so both the
1117
                    # original and alternate signals gets sent when
1118
                    # the function is called
505.1.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
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1131
                    attr[attrname] = fixscope(attribute, new_function)
505.1.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)))
561.1.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
505.1.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)))
561.1.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
560.1.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)))
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1193
            if deprecate:
1194
                # Deprecate all alternate interfaces
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1195
                iname="_AlternateDBusNames_interface_annotation{}"
575 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.
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1210
                cls = type(b"{}Alternate".format(cls.__name__),
575 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"})
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1218
class ClientDBus(Client, DBusObjectWithProperties):
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1219
    """A Client class using D-Bus
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1220
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1221
    Attributes:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1222
    dbus_object_path: dbus.ObjectPath
1223
    bus: dbus.SystemBus()
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1224
    """
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1225
    
1226
    runtime_expansions = (Client.runtime_expansions
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1227
                          + ("dbus_object_path",))
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1228
    
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1229
    _interface = "se.recompile.Mandos.Client"
1230
    
328 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
    
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
1233
    def __init__(self, bus = None, *args, **kwargs):
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
1234
        self.bus = bus
1235
        Client.__init__(self, *args, **kwargs)
328 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
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1238
        client_object_name = str(self.name).translate(
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1239
            {ord("."): ord("_"),
1240
             ord("-"): ord("_")})
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1241
        self.dbus_object_path = (dbus.ObjectPath
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1242
                                 ("/clients/" + client_object_name))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1243
        DBusObjectWithProperties.__init__(self, self.bus,
1244
                                          self.dbus_object_path)
573 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,
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1248
                             variant_level=1, invalidate_only=False,
1249
                             _interface=_interface):
505.1.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.
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1252
        
505.1.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.
505.1.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
505.1.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
        """
723.1.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"):
505.1.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)):
732 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())
505.1.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
        
505.1.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",
505.1.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")
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1297
    last_checker_status = notifychangeproperty(dbus.Int16,
1298
                                               "LastCheckerStatus")
505.1.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")
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1303
    approval_delay = notifychangeproperty(dbus.UInt64,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1304
                                          "ApprovalDelay",
1305
                                          type_func =
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1306
                                          lambda td: td.total_seconds()
1307
                                          * 1000)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1308
    approval_duration = notifychangeproperty(
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1309
        dbus.UInt64, "ApprovalDuration",
723.1.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")
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1312
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1313
                                   type_func = lambda td:
1314
                                       td.total_seconds() * 1000)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1315
    extended_timeout = notifychangeproperty(
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1316
        dbus.UInt64, "ExtendedTimeout",
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1317
        type_func = lambda td: td.total_seconds() * 1000)
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1318
    interval = notifychangeproperty(dbus.UInt64,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1319
                                    "Interval",
1320
                                    type_func =
723.1.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")
732 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
328 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()
329 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
1332
        except LookupError:
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1333
            pass
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1334
        if hasattr(DBusObjectWithProperties, "__del__"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1335
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
328 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
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1357
    def start_checker(self, *args, **kwargs):
658 by Teddy Hogeborn
* mandos (ClientDBus.start_checker): Removed unused variable
1358
        old_checker_pid = getattr(self.checker, "pid", None)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1359
        r = Client.start_checker(self, *args, **kwargs)
329 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
328 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):
518.2.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):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1372
        self.approved = value
723.1.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)
581 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
    
560.1.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
381 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1384
    @dbus.service.signal(_interface, signature="nxs")
280 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
463.1.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
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1395
    # PropertyChanged - signal
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
1396
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1397
    @dbus.service.signal(_interface, signature="sv")
238 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
    
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1402
    # GotSecret - signal
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1403
    @dbus.service.signal(_interface)
387 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
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1410
    
1411
    # Rejected - signal
463.1.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
463.1.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):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1420
        "D-Bus signal"
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1421
        return self.need_approval()
327 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
442 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
463.1.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)
442 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):
482 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
    
381 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
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1460
    # ApprovalPending - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1461
    @dbus_service_property(_interface, signature="b", access="read")
422 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
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1465
    # ApprovedByDefault - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1466
    @dbus_service_property(_interface, signature="b",
1467
                           access="readwrite")
422 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1474
    @dbus_service_property(_interface, signature="t",
1475
                           access="readwrite")
422 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
723.1.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)
422 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1483
    @dbus_service_property(_interface, signature="t",
1484
                           access="readwrite")
422 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
723.1.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)
422 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1492
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1493
    def Name_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1494
        return dbus.String(self.name)
1495
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1496
    # Fingerprint - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1497
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1498
    def Fingerprint_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1499
        return dbus.String(self.fingerprint)
1500
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1501
    # Host - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1502
    @dbus_service_property(_interface, signature="s",
1503
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1504
    def Host_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1505
        if value is None:       # get
1506
            return dbus.String(self.host)
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1507
        self.host = str(value)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1508
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1509
    # Created - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1510
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1511
    def Created_dbus_property(self):
518.2.3 by Teddy Hogeborn
Make "enabled" a client config option.
1512
        return datetime_to_dbus(self.created)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1513
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1514
    # LastEnabled - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1515
    @dbus_service_property(_interface, signature="s", access="read")
422 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)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1518
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1519
    # Enabled - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1520
    @dbus_service_property(_interface, signature="b",
1521
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1522
    def Enabled_dbus_property(self, value=None):
380 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
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1530
    # LastCheckedOK - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1531
    @dbus_service_property(_interface, signature="s",
1532
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1533
    def LastCheckedOK_dbus_property(self, value=None):
381 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
    
556 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)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1549
    
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1550
    # LastApprovalRequest - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1551
    @dbus_service_property(_interface, signature="s", access="read")
442 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)
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1554
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1555
    # Timeout - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1556
    @dbus_service_property(_interface, signature="t",
1557
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1558
    def Timeout_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1559
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1560
            return dbus.UInt64(self.timeout.total_seconds() * 1000)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1561
        old_timeout = self.timeout
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1562
        self.timeout = datetime.timedelta(0, 0, 0, value)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1563
        # Reschedule disabling
543 by Teddy Hogeborn
* mandos: Break some long lines.
1564
        if self.enabled:
1565
            now = datetime.datetime.utcnow()
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1566
            self.expires += self.timeout - old_timeout
1567
            if self.expires <= now:
543 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)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1575
                self.disable_initiator_tag = (
1576
                    gobject.timeout_add(
723.1.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
723.1.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
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1589
    # Interval - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1590
    @dbus_service_property(_interface, signature="t",
1591
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1592
    def Interval_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1593
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1594
            return dbus.UInt64(self.interval.total_seconds() * 1000)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1595
        self.interval = datetime.timedelta(0, 0, 0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1596
        if getattr(self, "checker_initiator_tag", None) is None:
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1597
            return
518.2.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
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1605
    # Checker - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1606
    @dbus_service_property(_interface, signature="s",
1607
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1608
    def Checker_dbus_property(self, value=None):
380 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)
730 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
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1613
    # CheckerRunning - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1614
    @dbus_service_property(_interface, signature="b",
1615
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1616
    def CheckerRunning_dbus_property(self, value=None):
380 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
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1624
    # ObjectPath - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1625
    @dbus_service_property(_interface, signature="o", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1626
    def ObjectPath_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1627
        return self.dbus_object_path # is already a dbus.ObjectPath
1628
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1629
    # Secret = property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1630
    @dbus_service_property(_interface, signature="ay",
1631
                           access="write", byte_arrays=True)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1632
    def Secret_dbus_property(self, value):
729 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
463.1.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):
518.2.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)
463.1.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()
463.1.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]
463.1.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):
463.1.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):
518.2.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)
463.1.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
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1663
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1664
class ClientHandler(socketserver.BaseRequestHandler, object):
331 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:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1672
            logger.info("TCP connection from: %s",
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1673
                        str(self.client_address))
463.1.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
            
288.1.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
            
288.1.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
            
463.1.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"))
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1691
            # Use a fallback default, since this MUST be set.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1692
            priority = self.server.gnutls_priority
1693
            if priority is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1694
                priority = "NORMAL"
288.1.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
            
416.1.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()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1702
            logger.debug("Protocol version: %r", line)
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1703
            try:
1704
                if int(line.strip().split()[0]) > 1:
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
1705
                    raise RuntimeError(line)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1706
            except (ValueError, IndexError, RuntimeError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1707
                logger.error("Unknown protocol version: %s", error)
416.1.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
            
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1710
            # Start GnuTLS connection
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1711
            try:
1712
                session.handshake()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1713
            except gnutls.errors.GNUTLSError as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1714
                logger.warning("Handshake failed: %s", error)
288.1.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
463.1.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
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1721
            try:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1722
                try:
1723
                    fpr = self.fingerprint(self.peer_certificate
1724
                                           (session))
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1725
                except (TypeError,
1726
                        gnutls.errors.GNUTLSError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1727
                    logger.warning("Bad certificate: %s", error)
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1728
                    return
463.1.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:
411 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
                
422 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
505.1.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
                    
518.2.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
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1754
                    elif client.approved is None:
463.1.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(
723.1.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:
463.1.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
463.1.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()
723.1.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:
463.1.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
463.1.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
                
411 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:])
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1794
                    except gnutls.errors.GNUTLSError as error:
574 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1798
                    logger.debug("Sent: %d, remaining: %d",
411 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
                
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1803
                logger.info("Sending secret to %s", client.name)
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
1804
                # bump the timeout using extended_timeout
556 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
            
411 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()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1815
                except gnutls.errors.GNUTLSError as error:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1816
                    logger.warning("GnuTLS bye failed",
1817
                                   exc_info=error)
330 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:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1833
            raise gnutls.errors.GNUTLSError("error getting peer"
1834
                                            " certificate")
330 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1866
                   ("Verify failed"))
330 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
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
1879
        hex_fpr = binascii.hexlify(fpr).upper()
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1880
        return hex_fpr
288.1.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)
518.1.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)
505.1.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."""
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1894
        proc = multiprocessing.Process(target = self.sub_process_main,
579 by Teddy Hogeborn
* mandos: Use all new builtins.
1895
                                       args = (request, address))
505.1.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 """
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1902
    def process_request(self, request, client_address):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1903
        """Overrides and wraps the original process_request().
1904
        
355 by Teddy Hogeborn
* mandos: White-space fixes only.
1905
        This function creates a new pipe in self.pipe
288.1.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
        
505.1.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()
505.1.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
    
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1914
    def add_pipe(self, parent_pipe, proc):
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1915
        """Dummy function; override as necessary"""
642 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,
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1920
                     socketserver.TCPServer, object):
237.2.13 by Teddy Hogeborn
Merge from trunk. Notable changes:
1921
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
331 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:
331 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
    """
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1928
    def __init__(self, server_address, RequestHandlerClass,
589.1.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
        """
331 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
589.1.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.
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1962
        socketserver.TCPServer.__init__(self, server_address,
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1963
                                        RequestHandlerClass)
589.1.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."""
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1969
        if self.interface is not None:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1970
            if SO_BINDTODEVICE is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1971
                logger.error("SO_BINDTODEVICE does not exist;"
1972
                             " cannot bind to interface %s",
338 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,
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
1978
                                           (self.interface + "\0")
1979
                                           .encode("utf-8"))
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1980
                except socket.error as error:
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1981
                    if error.errno == errno.EPERM:
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
1982
                        logger.error("No permission to bind to"
1983
                                     " interface %s", self.interface)
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1984
                    elif error.errno == errno.ENOPROTOOPT:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1985
                        logger.error("SO_BINDTODEVICE not available;"
1986
                                     " cannot bind to interface %s",
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1987
                                     self.interface)
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1988
                    elif error.errno == errno.ENODEV:
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
1989
                        logger.error("Interface %s does not exist,"
1990
                                     " cannot bind", self.interface)
338 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]:
314 by Teddy Hogeborn
Support not using IPv6 in server:
1996
                if self.address_family == socket.AF_INET6:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1997
                    any_address = "::" # in6addr_any
314 by Teddy Hogeborn
Support not using IPv6 in server:
1998
                else:
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
1999
                    any_address = "0.0.0.0" # INADDR_ANY
314 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)
331 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
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2010
#                                            (self.interface))
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2011
            return socketserver.TCPServer.server_bind(self)
339 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
340 by Teddy Hogeborn
Code cleanup.
2021
    
2022
    Assumes a gobject.MainLoop event loop.
339 by Teddy Hogeborn
Code cleanup.
2023
    """
2024
    def __init__(self, server_address, RequestHandlerClass,
2025
                 interface=None, use_ipv6=True, clients=None,
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2026
                 gnutls_priority=None, use_dbus=True, socketfd=None):
339 by Teddy Hogeborn
Code cleanup.
2027
        self.enabled = False
2028
        self.clients = clients
341 by Teddy Hogeborn
Code cleanup and one bug fix.
2029
        if self.clients is None:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2030
            self.clients = {}
339 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,
589.1.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:
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2040
            return socketserver.TCPServer.server_activate(self)
505.1.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
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2044
    
2045
    def add_pipe(self, parent_pipe, proc):
340 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(),
411 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,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2050
                                               parent_pipe =
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2051
                                               parent_pipe,
2052
                                               proc = proc))
505.1.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,
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2055
                   proc = None, client_object=None):
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2056
        # error, or the other end of multiprocessing.Pipe has closed
577 by Teddy Hogeborn
* mandos (Client.start_checker): Reworded comment.
2057
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2058
            # Wait for other process to exit
505.1.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
288.1.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
        
463.1.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
            
518.1.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
505.1.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,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2087
                                                   parent_pipe =
2088
                                                   parent_pipe,
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2089
                                                   proc = proc,
505.1.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)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2093
            # remove the old hook in favor of the new above hook on
2094
            # same fileno
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2095
            return False
463.1.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
            
505.1.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
        
463.1.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)):
463.1.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:
505.1.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
        
463.1.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
        
288.1.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
609 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,
721 by Teddy Hogeborn
Fix two mutually cancelling bugs.
2188
                                      token_week)))
609 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
721 by Teddy Hogeborn
Fix two mutually cancelling bugs.
2192
    followers = frozenset((token_duration,)) # Following valid tokens
609 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
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2222
    
463.1.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)
463.1.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)
463.1.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)
463.1.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)
463.1.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)
463.1.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
    """
609 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:
729 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])
463.1.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)
463.1.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)
463.1.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)
463.1.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)
463.1.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:
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2258
                raise ValueError("Unknown suffix {!r}"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2259
                                 .format(suffix))
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2260
        except IndexError as e:
464 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.
331 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:
463.1.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
560.1.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):
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2281
            raise OSError(errno.ENODEV, "{} not a character device"
568 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():
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2291
    
379 by Teddy Hogeborn
* mandos: Fix line lengths.
2292
    ##################################################################
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2293
    # Parsing of options, both command line and config file
2294
    
474 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",
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2297
                        version = "%(prog)s {}".format(version),
474 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"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2309
                        " to terminal", default=None)
474 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"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2322
                        " system bus interface", default=None)
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2323
    parser.add_argument("--no-ipv6", action="store_false",
618 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)
518.1.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",
518.1.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
2327
                        dest="restore", help="Do not restore stored"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2328
                        " state", default=None)
589.1.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")
518.2.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")
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2334
    parser.add_argument("--foreground", action="store_true",
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2335
                        help="Run in foreground", default=None)
707 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)
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2339
    
474 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
654 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
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2348
    server_defaults = { "interface": "",
2349
                        "address": "",
2350
                        "port": "",
2351
                        "debug": "False",
2352
                        "priority":
631 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",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2354
                        "servicename": "Mandos",
2355
                        "use_dbus": "True",
2356
                        "use_ipv6": "True",
2357
                        "debuglevel": "",
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2358
                        "restore": "True",
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2359
                        "socket": "",
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2360
                        "statedir": "/var/lib/mandos",
2361
                        "foreground": "False",
707 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
335 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
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2368
    server_config.read(os.path.join(options.configdir,
463.1.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()
282 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
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2373
    for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2374
        server_settings[option] = server_config.getboolean("DEFAULT",
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2375
                                                           option)
463.1.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")
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2379
    if server_settings["socket"]:
2380
        server_settings["socket"] = server_config.getint("DEFAULT",
2381
                                                         "socket")
591 by Teddy Hogeborn
Merge "--socket" option for server.
2382
        # Later, stdin will, and stdout and stderr might, be dup'ed
589.1.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.
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2392
    for option in ("interface", "address", "port", "debug",
2393
                   "priority", "servicename", "configdir",
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2394
                   "use_dbus", "use_ipv6", "debuglevel", "restore",
707 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
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2400
    # Force all strings to be unicode
2401
    for option in server_settings.keys():
729 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"))
618 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",
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2407
                   "foreground", "zeroconf"):
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2408
        server_settings[option] = bool(server_settings[option])
606 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
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2414
    ##################################################################
2415
    
707 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
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2422
    # For convenience
463.1.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"]
518.2.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)
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2429
    foreground = server_settings["foreground"]
707 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
    
518.1.4 by Björn Påhlsson
restructured logger
2432
    if debug:
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2433
        initlogger(debug, logging.DEBUG)
518.1.4 by Björn Påhlsson
restructured logger
2434
    else:
2435
        if not debuglevel:
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2436
            initlogger(debug)
518.1.4 by Björn Påhlsson
restructured logger
2437
        else:
2438
            level = getattr(logging, debuglevel.upper())
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2439
            initlogger(debug, level)
518.1.4 by Björn Påhlsson
restructured logger
2440
    
463.1.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
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2443
                               ('Mandos ({}) [%(process)d]:'
568 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
543 by Teddy Hogeborn
* mandos: Break some long lines.
2449
    client_config = configparser.SafeConfigParser(Client
2450
                                                  .client_defaults)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2451
    client_config.read(os.path.join(server_settings["configdir"],
2452
                                    "clients.conf"))
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2453
    
327 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
    
707 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"]
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2460
    tcp_server = MandosServer((server_settings["address"],
2461
                               server_settings["port"]),
339 by Teddy Hogeborn
Code cleanup.
2462
                              ClientHandler,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2463
                              interface=(server_settings["interface"]
237.2.35 by teddy at bsnet
* mandos (main): Bug fix: Don't try to bind to an empty string
2464
                                         or None),
339 by Teddy Hogeborn
Code cleanup.
2465
                              use_ipv6=use_ipv6,
2466
                              gnutls_priority=
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2467
                              server_settings["priority"],
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2468
                              use_dbus=use_dbus,
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2469
                              socketfd=socketfd)
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2470
    if not foreground:
626 by Teddy Hogeborn
* Makefile (CFLAGS, LDFLAGS): Keep default flags from environment.
2471
        pidfilename = "/run/mandos.pid"
636 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"
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2474
        pidfile = None
439 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
2475
        try:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2476
            pidfile = open(pidfilename, "w")
574 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
    
566 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2481
    for name in ("_mandos", "mandos", "nobody"):
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2482
        try:
566 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
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2486
        except KeyError:
566 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:
290 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)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2494
    except OSError as error:
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
2495
        if error.errno != errno.EPERM:
642 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
    
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2498
    if debug:
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2499
        # Enable all possible GnuTLS debugging
2500
        
290 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):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2507
            logger.debug("GnuTLS: %s", string[:-1])
290 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))
422 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
560.1.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)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2517
    
458 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2518
    # Need to fork before connecting to D-Bus
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2519
    if not foreground:
458 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()
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2522
    
590 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.
518.2.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
560.1.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
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2533
    if use_dbus:
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2534
        try:
505 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",
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2536
                                            bus, do_not_queue=True)
505.1.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))
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2540
        except dbus.exceptions.NameExistsException as e:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2541
            logger.error("Disabling D-Bus:", exc_info=e)
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2542
            use_dbus = False
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2543
            server_settings["use_dbus"] = False
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2544
            tcp_server.use_dbus = False
707 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
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2553
                                 (server_settings["interface"]
2554
                                  .encode("utf-8")))
458 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
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
2559
    client_class = Client
2560
    if use_dbus:
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
2561
        client_class = functools.partial(ClientDBus, bus = bus)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2562
    
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2563
    client_settings = Client.config_parser(client_config)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2564
    old_client_settings = {}
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2565
    clients_data = {}
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2566
    
617 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
    
518.1.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
2575
    # Get client data and settings from last running state.
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2576
    if server_settings["restore"]:
2577
        try:
2578
            with open(stored_state_path, "rb") as stored_state:
518.1.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))
518.1.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:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2583
            if e.errno == errno.ENOENT:
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2584
                logger.warning("Could not load persistent state: {}"
574 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)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2589
                raise
505.1.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: "
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2592
                           "EOFError:", exc_info=e)
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2593
    
518.1.9 by Björn Påhlsson
renamed variables
2594
    with PGPEngine() as pgp:
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
2595
        for client_name, client in clients_data.items():
617 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
            
518.2.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]):
518.2.10 by Teddy Hogeborn
* mandos (main): Bug fix: Syntax fix when restoring settings.
2613
                        client[name] = value
518.2.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
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2618
            # enabled if its last checker was successful.  Clients
552 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.
518.2.8 by Teddy Hogeborn
* mandos (ClientDBus.Host_dbus_property,
2622
            if client["enabled"]:
518.1.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(
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2626
                            "disabling client {} - Client never "
552 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2627
                            "performed a successful checker"
2628
                            .format(client_name))
518.1.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(
723.1.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 {}"
552 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2634
                            .format(client_name,
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2635
                                    client["last_checker_status"]))
518.2.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"])
542 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
2641
                        logger.debug("Last checker succeeded,"
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2642
                                     " keeping {} enabled"
552 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:
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2645
                client["secret"] = (
2646
                    pgp.decrypt(client["encrypted_secret"],
518.1.9 by Björn Påhlsson
renamed variables
2647
                                client_settings[client_name]
2648
                                ["secret"]))
2649
            except PGPError:
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2650
                # If decryption fails, we use secret from new settings
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2651
                logger.debug("Failed to decrypt {} old secret"
518.1.10 by Björn Påhlsson
added warning text when failing to decrypt old state
2652
                             .format(client_name))
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2653
                client["secret"] = (
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2654
                    client_settings[client_name]["secret"])
2655
    
518.1.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
543 by Teddy Hogeborn
* mandos: Break some long lines.
2657
    for client_name in (set(old_client_settings)
2658
                        - set(client_settings)):
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2659
        del clients_data[client_name]
543 by Teddy Hogeborn
* mandos: Break some long lines.
2660
    for client_name in (set(client_settings)
2661
                        - set(old_client_settings)):
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2662
        clients_data[client_name] = client_settings[client_name]
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2663
    
552 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2664
    # Create all client objects
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
2665
    for client_name, client in clients_data.items():
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2666
        tcp_server.clients[client_name] = client_class(
617 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
    
341 by Teddy Hogeborn
Code cleanup and one bug fix.
2670
    if not tcp_server.clients:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2671
        logger.warning("No clients defined")
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2672
    
606 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()
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2678
                    pidfile.write("{}\n".format(pid).encode("utf-8"))
606 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
439 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
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2688
    if use_dbus:
575 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"})
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
2691
        class MandosDBusService(DBusObjectWithProperties):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2692
            """A D-Bus proxy object"""
2693
            def __init__(self):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2694
                dbus.service.Object.__init__(self, bus, "/")
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
2695
            _interface = "se.recompile.Mandos"
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2696
            
560.1.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
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2703
            @dbus.service.signal(_interface, signature="o")
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2704
            def ClientAdded(self, objpath):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2705
                "D-Bus signal"
2706
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2707
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2708
            @dbus.service.signal(_interface, signature="ss")
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
2709
            def ClientNotFound(self, fingerprint, address):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2710
                "D-Bus signal"
2711
                pass
2712
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2713
            @dbus.service.signal(_interface, signature="os")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2714
            def ClientRemoved(self, objpath, name):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2715
                "D-Bus signal"
2716
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2717
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2718
            @dbus.service.method(_interface, out_signature="ao")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2719
            def GetAllClients(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
2720
                "D-Bus method"
341 by Teddy Hogeborn
Code cleanup and one bug fix.
2721
                return dbus.Array(c.dbus_object_path
518.1.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())
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2724
            
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2725
            @dbus.service.method(_interface,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2726
                                 out_signature="a{oa{sv}}")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2727
            def GetAllClientsWithProperties(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
2728
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2729
                return dbus.Dictionary(
732 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() },
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2732
                    signature="oa{sv}")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2733
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2734
            @dbus.service.method(_interface, in_signature="o")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2735
            def RemoveClient(self, object_path):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
2736
                "D-Bus method"
518.1.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():
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2738
                    if c.dbus_object_path == object_path:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2739
                        del tcp_server.clients[c.name]
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
2740
                        c.remove_from_connection()
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2741
                        # Don't signal anything except ClientRemoved
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2742
                        c.disable(quiet=True)
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2743
                        # Emit D-Bus signal
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2744
                        self.ClientRemoved(object_path, c.name)
2745
                        return
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2746
                raise KeyError(object_path)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2747
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2748
            del _interface
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2749
        
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
2750
        mandos_dbus_service = MandosDBusService()
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2751
    
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2752
    def cleanup():
2753
        "Cleanup function; run on exit"
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2754
        if zeroconf:
2755
            service.cleanup()
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2756
        
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2757
        multiprocessing.active_children()
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
2758
        wnull.close()
518.1.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
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2761
        
521 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.
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2765
        clients = {}
518.1.9 by Björn Påhlsson
renamed variables
2766
        with PGPEngine() as pgp:
518.2.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"]
518.1.9 by Björn Påhlsson
renamed variables
2769
                client.encrypted_secret = pgp.encrypt(client.secret,
2770
                                                      key)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2771
                client_dict = {}
2772
                
518.1.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.
723.1.2 by Teddy Hogeborn
Use set literals.
2775
                exclude = { "bus", "changedstate", "secret",
2776
                            "checker", "server_settings" }
518.2.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
                
518.1.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2787
                clients[client.name] = client_dict
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2788
                del client_settings[client.name]["secret"]
2789
        
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2790
        try:
578 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:
518.1.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)
578 by Teddy Hogeborn
* mandos (main.cleanup): Use tempfile.NamedTemporaryFile() instead of
2796
                tempname=stored_state.name
505.1.26 by teddy at bsnet
* mandos (main/cleanup): Write new file, then rename. Use
2797
            os.rename(tempname, stored_state_path)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2798
        except (IOError, OSError) as e:
530 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
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2804
            if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST):
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2805
                logger.warning("Could not save persistent state: {}"
574 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)
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2810
                raise
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2811
        
518.1.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
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2813
        while tcp_server.clients:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2814
            name, client = tcp_server.clients.popitem()
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2815
            if use_dbus:
2816
                client.remove_from_connection()
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2817
            # Don't signal anything except ClientRemoved
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2818
            client.disable(quiet=True)
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2819
            if use_dbus:
2820
                # Emit D-Bus signal
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2821
                mandos_dbus_service.ClientRemoved(client
2822
                                                  .dbus_object_path,
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2823
                                                  client.name)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2824
        client_settings.clear()
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2825
    
2826
    atexit.register(cleanup)
2827
    
518.1.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():
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2829
        if use_dbus:
2830
            # Emit D-Bus signal
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2831
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
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
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2840
    if zeroconf:
2841
        service.port = tcp_server.socket.getsockname()[1]
314 by Teddy Hogeborn
Support not using IPv6 in server:
2842
    if use_ipv6:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2843
        logger.info("Now listening on address %r, port %d,"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2844
                    " flowinfo %d, scope_id %d",
2845
                    *tcp_server.socket.getsockname())
314 by Teddy Hogeborn
Support not using IPv6 in server:
2846
    else:                       # IPv4
568 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:
707 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
        
463.1.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()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2870
    except AvahiError as error:
565 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
2871
        logger.critical("Avahi Error", exc_info=error)
401 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:
463.1.5 by teddy at bsnet
* mandos: Use unicode string literals.
2876
            print("", file=sys.stderr)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2877
        logger.debug("Server received KeyboardInterrupt")
2878
    logger.debug("Server exiting")
401 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
463.1.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()