/mandos/release

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