/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:
731
                    logger.error("Child process vanished", exc_info=error)
732
                    return True
733
                raise
237.7.131 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
734
            if pid:
735
                gobject.source_remove(self.checker_callback_tag)
736
                self.checker_callback(pid, status, command)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
737
        # Re-run this periodically if run by gobject.timeout_add
738
        return True
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
739
    
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
740
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
741
        """Force the checker process, if any, to stop."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
742
        if self.checker_callback_tag:
743
            gobject.source_remove(self.checker_callback_tag)
744
            self.checker_callback_tag = None
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
745
        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.
746
            return
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
747
        logger.debug("Stopping checker for %(name)s", vars(self))
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
748
        try:
237.7.112 by Teddy Hogeborn
* mandos (Client.stop_checker): Use new Popen.terminate() method.
749
            self.checker.terminate()
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
750
            #time.sleep(0.5)
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
751
            #if self.checker.poll() is None:
237.7.112 by Teddy Hogeborn
* mandos (Client.stop_checker): Use new Popen.terminate() method.
752
            #    self.checker.kill()
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
753
        except OSError as error:
28 by Teddy Hogeborn
* server.conf: New file.
754
            if error.errno != errno.ESRCH: # No such process
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
755
                raise
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
756
        self.checker = None
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
757
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
758
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
759
def dbus_service_property(dbus_interface, signature="v",
760
                          access="readwrite", byte_arrays=False):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
761
    """Decorators for marking methods of a DBusObjectWithProperties to
762
    become properties on the D-Bus.
763
    
764
    The decorated method will be called with no arguments by "Get"
765
    and with one argument by "Set".
766
    
767
    The parameters, where they are supported, are the same as
768
    dbus.service.method, except there is only "signature", since the
769
    type from Get() and the type sent to Set() is the same.
770
    """
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
771
    # Encoding deeply encoded byte arrays is not supported yet by the
772
    # "Set" method, so we fail early here:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
773
    if byte_arrays and signature != "ay":
774
        raise ValueError("Byte arrays not supported for non-'ay'"
237.7.116 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
775
                         " signature {0!r}".format(signature))
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
776
    def decorator(func):
777
        func._dbus_is_property = True
778
        func._dbus_interface = dbus_interface
779
        func._dbus_signature = signature
780
        func._dbus_access = access
781
        func._dbus_name = func.__name__
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
782
        if func._dbus_name.endswith("_dbus_property"):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
783
            func._dbus_name = func._dbus_name[:-14]
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
784
        func._dbus_get_args_options = {'byte_arrays': byte_arrays }
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
785
        return func
786
    return decorator
787
788
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
789
def dbus_interface_annotations(dbus_interface):
237.7.121 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
790
    """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
791
    
792
    Usage:
793
    
794
    @dbus_interface_annotations("org.example.Interface")
795
    def _foo(self):  # Function name does not matter
796
        return {"org.freedesktop.DBus.Deprecated": "true",
797
                "org.freedesktop.DBus.Property.EmitsChangedSignal":
798
                    "false"}
799
    """
800
    def decorator(func):
801
        func._dbus_is_interface = True
802
        func._dbus_interface = dbus_interface
803
        func._dbus_name = dbus_interface
804
        return func
805
    return decorator
806
807
237.19.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
808
def dbus_annotations(annotations):
809
    """Decorator to annotate D-Bus methods, signals or properties
810
    Usage:
811
    
812
    @dbus_service_property("org.example.Interface", signature="b",
813
                           access="r")
814
    @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
815
                        "org.freedesktop.DBus.Property."
816
                        "EmitsChangedSignal": "false"})
817
    def Property_dbus_property(self):
818
        return dbus.Boolean(False)
819
    """
820
    def decorator(func):
821
        func._dbus_annotations = annotations
822
        return func
823
    return decorator
824
825
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
826
class DBusPropertyException(dbus.exceptions.DBusException):
827
    """A base class for D-Bus property-related exceptions
828
    """
829
    def __unicode__(self):
830
        return unicode(str(self))
831
832
833
class DBusPropertyAccessException(DBusPropertyException):
834
    """A property's access permissions disallows an operation.
835
    """
836
    pass
837
838
839
class DBusPropertyNotFound(DBusPropertyException):
840
    """An attempt was made to access a non-existing property.
841
    """
842
    pass
843
844
845
class DBusObjectWithProperties(dbus.service.Object):
846
    """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
847
    
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
848
    Classes inheriting from this can use the dbus_service_property
849
    decorator to expose methods as D-Bus properties.  It exposes the
850
    standard Get(), Set(), and GetAll() methods on the D-Bus.
851
    """
852
    
853
    @staticmethod
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
854
    def _is_dbus_thing(thing):
855
        """Returns a function testing if an attribute is a D-Bus thing
856
        
857
        If called like _is_dbus_thing("method") it returns a function
858
        suitable for use as predicate to inspect.getmembers().
859
        """
860
        return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
861
                                   False)
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
862
    
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
863
    def _get_all_dbus_things(self, thing):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
864
        """Returns a generator of (name, attribute) pairs
865
        """
237.19.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
866
        return ((getattr(athing.__get__(self), "_dbus_name",
867
                         name),
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
868
                 athing.__get__(self))
24.1.186 by Björn Påhlsson
transitional stuff actually working
869
                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
870
                for name, athing in
871
                inspect.getmembers(cls,
872
                                   self._is_dbus_thing(thing)))
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
873
    
874
    def _get_dbus_property(self, interface_name, property_name):
875
        """Returns a bound method if one exists which is a D-Bus
876
        property with the specified name and interface.
877
        """
24.1.186 by Björn Påhlsson
transitional stuff actually working
878
        for cls in  self.__class__.__mro__:
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
879
            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
880
                                (cls,
881
                                 self._is_dbus_thing("property"))):
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
882
                if (value._dbus_name == property_name
883
                    and value._dbus_interface == interface_name):
24.1.186 by Björn Påhlsson
transitional stuff actually working
884
                    return value.__get__(self)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
885
        
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
886
        # No such property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
887
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
888
                                   + interface_name + "."
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
889
                                   + property_name)
890
    
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
891
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
892
                         out_signature="v")
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
893
    def Get(self, interface_name, property_name):
894
        """Standard D-Bus property Get() method, see D-Bus standard.
895
        """
896
        prop = self._get_dbus_property(interface_name, property_name)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
897
        if prop._dbus_access == "write":
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
898
            raise DBusPropertyAccessException(property_name)
899
        value = prop()
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
900
        if not hasattr(value, "variant_level"):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
901
            return value
902
        return type(value)(value, variant_level=value.variant_level+1)
903
    
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
904
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
905
    def Set(self, interface_name, property_name, value):
906
        """Standard D-Bus property Set() method, see D-Bus standard.
907
        """
908
        prop = self._get_dbus_property(interface_name, property_name)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
909
        if prop._dbus_access == "read":
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
910
            raise DBusPropertyAccessException(property_name)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
911
        if prop._dbus_get_args_options["byte_arrays"]:
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
912
            # The byte_arrays option is not supported yet on
913
            # signatures other than "ay".
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
914
            if prop._dbus_signature != "ay":
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
915
                raise ValueError
237.7.94 by Teddy Hogeborn
* debian/rules (binary-common): Exclude network-hooks.d from
916
            value = dbus.ByteArray(b''.join(chr(byte)
917
                                            for byte in value))
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
918
        prop(value)
919
    
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
920
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
921
                         out_signature="a{sv}")
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
922
    def GetAll(self, interface_name):
923
        """Standard D-Bus property GetAll() method, see D-Bus
924
        standard.
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
925
        
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
926
        Note: Will not include properties with access="write".
927
        """
237.12.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
928
        properties = {}
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
929
        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.
930
            if (interface_name
931
                and interface_name != prop._dbus_interface):
932
                # Interface non-empty but did not match
933
                continue
934
            # Ignore write-only properties
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
935
            if prop._dbus_access == "write":
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
936
                continue
937
            value = prop()
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
938
            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
939
                properties[name] = value
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
940
                continue
237.12.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
941
            properties[name] = type(value)(value, variant_level=
942
                                           value.variant_level+1)
943
        return dbus.Dictionary(properties, signature="sv")
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
944
    
945
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
946
                         out_signature="s",
947
                         path_keyword='object_path',
948
                         connection_keyword='connection')
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
949
    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
950
        """Overloading of standard D-Bus method.
951
        
952
        Inserts property tags and interface annotation tags.
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
953
        """
954
        xmlstring = dbus.service.Object.Introspect(self, object_path,
237.2.149 by Teddy Hogeborn
* mandos (DBusObjectWithProperties.Introspect): Add the name
955
                                                   connection)
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
956
        try:
957
            document = xml.dom.minidom.parseString(xmlstring)
958
            def make_tag(document, name, prop):
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
959
                e = document.createElement("property")
960
                e.setAttribute("name", name)
961
                e.setAttribute("type", prop._dbus_signature)
962
                e.setAttribute("access", prop._dbus_access)
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
963
                return e
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
964
            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
965
                # Add property tags
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
966
                for tag in (make_tag(document, name, prop)
967
                            for name, prop
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
968
                            in self._get_all_dbus_things("property")
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
969
                            if prop._dbus_interface
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
970
                            == if_tag.getAttribute("name")):
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
971
                    if_tag.appendChild(tag)
237.19.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
972
                # Add annotation tags
973
                for typ in ("method", "signal", "property"):
974
                    for tag in if_tag.getElementsByTagName(typ):
975
                        annots = dict()
976
                        for name, prop in (self.
977
                                           _get_all_dbus_things(typ)):
978
                            if (name == tag.getAttribute("name")
979
                                and prop._dbus_interface
980
                                == if_tag.getAttribute("name")):
981
                                annots.update(getattr
982
                                              (prop,
983
                                               "_dbus_annotations",
984
                                               {}))
985
                        for name, value in annots.iteritems():
986
                            ann_tag = document.createElement(
987
                                "annotation")
988
                            ann_tag.setAttribute("name", name)
989
                            ann_tag.setAttribute("value", value)
990
                            tag.appendChild(ann_tag)
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
991
                # Add interface annotation tags
992
                for annotation, value in dict(
237.7.124 by Teddy Hogeborn
* mandos (DBusObjectWithProperties.Introspect): Use
993
                    itertools.chain.from_iterable(
994
                        annotations().iteritems()
995
                        for name, annotations in
996
                        self._get_all_dbus_things("interface")
997
                        if name == if_tag.getAttribute("name")
998
                        )).iteritems():
237.19.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
999
                    ann_tag = document.createElement("annotation")
1000
                    ann_tag.setAttribute("name", annotation)
1001
                    ann_tag.setAttribute("value", value)
1002
                    if_tag.appendChild(ann_tag)
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1003
                # Add the names to the return values for the
1004
                # "org.freedesktop.DBus.Properties" methods
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1005
                if (if_tag.getAttribute("name")
1006
                    == "org.freedesktop.DBus.Properties"):
1007
                    for cn in if_tag.getElementsByTagName("method"):
1008
                        if cn.getAttribute("name") == "Get":
1009
                            for arg in cn.getElementsByTagName("arg"):
1010
                                if (arg.getAttribute("direction")
1011
                                    == "out"):
1012
                                    arg.setAttribute("name", "value")
1013
                        elif cn.getAttribute("name") == "GetAll":
1014
                            for arg in cn.getElementsByTagName("arg"):
1015
                                if (arg.getAttribute("direction")
1016
                                    == "out"):
1017
                                    arg.setAttribute("name", "props")
1018
            xmlstring = document.toxml("utf-8")
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1019
            document.unlink()
1020
        except (AttributeError, xml.dom.DOMException,
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1021
                xml.parsers.expat.ExpatError) as error:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1022
            logger.error("Failed to override Introspection method",
237.7.113 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
1023
                         exc_info=error)
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1024
        return xmlstring
1025
1026
237.7.135 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
1027
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
1028
    """Convert a UTC datetime.datetime() to a D-Bus type."""
1029
    if dt is None:
1030
        return dbus.String("", variant_level = variant_level)
1031
    return dbus.String(dt.isoformat(),
1032
                       variant_level=variant_level)
1033
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1034
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1035
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
1036
    """A class decorator; applied to a subclass of
1037
    dbus.service.Object, it will add alternate D-Bus attributes with
1038
    interface names according to the "alt_interface_names" mapping.
1039
    Usage:
1040
    
237.7.135 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
1041
    @alternate_dbus_interfaces({"org.example.Interface":
1042
                                    "net.example.AlternateInterface"})
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1043
    class SampleDBusObject(dbus.service.Object):
1044
        @dbus.service.method("org.example.Interface")
1045
        def SampleDBusMethod():
1046
            pass
1047
    
1048
    The above "SampleDBusMethod" on "SampleDBusObject" will be
1049
    reachable via two interfaces: "org.example.Interface" and
1050
    "net.example.AlternateInterface", the latter of which will have
1051
    its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to
1052
    "true", unless "deprecate" is passed with a False value.
1053
    
1054
    This works for methods and signals, and also for D-Bus properties
1055
    (from DBusObjectWithProperties) and interfaces (from the
1056
    dbus_interface_annotations decorator).
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1057
    """
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1058
    def wrapper(cls):
1059
        for orig_interface_name, alt_interface_name in (
1060
            alt_interface_names.iteritems()):
1061
            attr = {}
1062
            interface_names = set()
1063
            # Go though all attributes of the class
1064
            for attrname, attribute in inspect.getmembers(cls):
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1065
                # Ignore non-D-Bus attributes, and D-Bus attributes
1066
                # with the wrong interface name
1067
                if (not hasattr(attribute, "_dbus_interface")
1068
                    or not attribute._dbus_interface
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1069
                    .startswith(orig_interface_name)):
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1070
                    continue
1071
                # Create an alternate D-Bus interface name based on
1072
                # the current name
1073
                alt_interface = (attribute._dbus_interface
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1074
                                 .replace(orig_interface_name,
1075
                                          alt_interface_name))
1076
                interface_names.add(alt_interface)
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1077
                # Is this a D-Bus signal?
1078
                if getattr(attribute, "_dbus_is_signal", False):
1079
                    # Extract the original non-method function by
1080
                    # black magic
1081
                    nonmethod_func = (dict(
1082
                            zip(attribute.func_code.co_freevars,
1083
                                attribute.__closure__))["func"]
1084
                                      .cell_contents)
1085
                    # Create a new, but exactly alike, function
1086
                    # object, and decorate it to be a new D-Bus signal
1087
                    # with the alternate D-Bus interface name
1088
                    new_function = (dbus.service.signal
1089
                                    (alt_interface,
1090
                                     attribute._dbus_signature)
1091
                                    (types.FunctionType(
1092
                                nonmethod_func.func_code,
1093
                                nonmethod_func.func_globals,
1094
                                nonmethod_func.func_name,
1095
                                nonmethod_func.func_defaults,
1096
                                nonmethod_func.func_closure)))
237.19.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1097
                    # Copy annotations, if any
1098
                    try:
1099
                        new_function._dbus_annotations = (
1100
                            dict(attribute._dbus_annotations))
1101
                    except AttributeError:
1102
                        pass
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1103
                    # 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
1104
                    # original and alternate functions, so both the
1105
                    # original and alternate signals gets sent when
1106
                    # the function is called
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1107
                    def fixscope(func1, func2):
1108
                        """This function is a scope container to pass
1109
                        func1 and func2 to the "call_both" function
1110
                        outside of its arguments"""
1111
                        def call_both(*args, **kwargs):
1112
                            """This function will emit two D-Bus
1113
                            signals by calling func1 and func2"""
1114
                            func1(*args, **kwargs)
1115
                            func2(*args, **kwargs)
1116
                        return call_both
1117
                    # Create the "call_both" function and add it to
1118
                    # the class
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1119
                    attr[attrname] = fixscope(attribute, new_function)
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1120
                # Is this a D-Bus method?
1121
                elif getattr(attribute, "_dbus_is_method", False):
1122
                    # Create a new, but exactly alike, function
1123
                    # object.  Decorate it to be a new D-Bus method
1124
                    # with the alternate D-Bus interface name.  Add it
1125
                    # to the class.
1126
                    attr[attrname] = (dbus.service.method
1127
                                      (alt_interface,
1128
                                       attribute._dbus_in_signature,
1129
                                       attribute._dbus_out_signature)
1130
                                      (types.FunctionType
1131
                                       (attribute.func_code,
1132
                                        attribute.func_globals,
1133
                                        attribute.func_name,
1134
                                        attribute.func_defaults,
1135
                                        attribute.func_closure)))
237.19.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1136
                    # Copy annotations, if any
1137
                    try:
1138
                        attr[attrname]._dbus_annotations = (
1139
                            dict(attribute._dbus_annotations))
1140
                    except AttributeError:
1141
                        pass
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1142
                # Is this a D-Bus property?
1143
                elif getattr(attribute, "_dbus_is_property", False):
1144
                    # Create a new, but exactly alike, function
1145
                    # object, and decorate it to be a new D-Bus
1146
                    # property with the alternate D-Bus interface
1147
                    # name.  Add it to the class.
1148
                    attr[attrname] = (dbus_service_property
1149
                                      (alt_interface,
1150
                                       attribute._dbus_signature,
1151
                                       attribute._dbus_access,
1152
                                       attribute
1153
                                       ._dbus_get_args_options
1154
                                       ["byte_arrays"])
1155
                                      (types.FunctionType
1156
                                       (attribute.func_code,
1157
                                        attribute.func_globals,
1158
                                        attribute.func_name,
1159
                                        attribute.func_defaults,
1160
                                        attribute.func_closure)))
237.19.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1161
                    # Copy annotations, if any
1162
                    try:
1163
                        attr[attrname]._dbus_annotations = (
1164
                            dict(attribute._dbus_annotations))
1165
                    except AttributeError:
1166
                        pass
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1167
                # Is this a D-Bus interface?
1168
                elif getattr(attribute, "_dbus_is_interface", False):
1169
                    # Create a new, but exactly alike, function
1170
                    # object.  Decorate it to be a new D-Bus interface
1171
                    # with the alternate D-Bus interface name.  Add it
1172
                    # to the class.
1173
                    attr[attrname] = (dbus_interface_annotations
1174
                                      (alt_interface)
1175
                                      (types.FunctionType
1176
                                       (attribute.func_code,
1177
                                        attribute.func_globals,
1178
                                        attribute.func_name,
1179
                                        attribute.func_defaults,
1180
                                        attribute.func_closure)))
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1181
            if deprecate:
1182
                # Deprecate all alternate interfaces
1183
                iname="_AlternateDBusNames_interface_annotation{0}"
1184
                for interface_name in interface_names:
1185
                    @dbus_interface_annotations(interface_name)
1186
                    def func(self):
1187
                        return { "org.freedesktop.DBus.Deprecated":
1188
                                     "true" }
1189
                    # Find an unused name
1190
                    for aname in (iname.format(i)
1191
                                  for i in itertools.count()):
1192
                        if aname not in attr:
1193
                            attr[aname] = func
1194
                            break
1195
            if interface_names:
1196
                # Replace the class with a new subclass of it with
1197
                # methods, signals, etc. as created above.
1198
                cls = type(b"{0}Alternate".format(cls.__name__),
1199
                           (cls,), attr)
1200
        return cls
1201
    return wrapper
1202
1203
1204
@alternate_dbus_interfaces({"se.recompile.Mandos":
1205
                                "se.bsnet.fukt.Mandos"})
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1206
class ClientDBus(Client, DBusObjectWithProperties):
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1207
    """A Client class using D-Bus
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1208
    
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1209
    Attributes:
237.2.101 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1210
    dbus_object_path: dbus.ObjectPath
1211
    bus: dbus.SystemBus()
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1212
    """
237.2.201 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1213
    
1214
    runtime_expansions = (Client.runtime_expansions
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1215
                          + ("dbus_object_path",))
237.2.201 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1216
    
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1217
    # dbus.service.Object doesn't use super(), so we can't either.
1218
    
237.2.100 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
1219
    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
1220
        self.bus = bus
1221
        Client.__init__(self, *args, **kwargs)
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1222
        # Only now, when this client is initialized, can it show up on
1223
        # the D-Bus
237.2.204 by Teddy Hogeborn
* mandos (ClientDBus.__init__): Bug fix: Translate "-" in client names
1224
        client_object_name = unicode(self.name).translate(
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1225
            {ord("."): ord("_"),
1226
             ord("-"): ord("_")})
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1227
        self.dbus_object_path = (dbus.ObjectPath
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1228
                                 ("/clients/" + client_object_name))
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1229
        DBusObjectWithProperties.__init__(self, self.bus,
1230
                                          self.dbus_object_path)
237.7.121 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1231
    
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1232
    def notifychangeproperty(transform_func,
1233
                             dbus_name, type_func=lambda x: x,
1234
                             variant_level=1):
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1235
        """ Modify a variable so that it's a property which announces
1236
        its changes to DBus.
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1237
        
237.11.15 by Teddy Hogeborn
Bug fix: Make D-Bus properties settable again.
1238
        transform_fun: Function that takes a value and a variant_level
1239
                       and transforms it to a D-Bus type.
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1240
        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
1241
        type_func: Function that transform the value before sending it
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1242
                   to the D-Bus.  Default: no transform
1243
        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
1244
        """
237.11.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1245
        attrname = "_{0}".format(dbus_name)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1246
        def setter(self, value):
1247
            if hasattr(self, "dbus_object_path"):
237.11.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1248
                if (not hasattr(self, attrname) or
1249
                    type_func(getattr(self, attrname, None))
1250
                    != type_func(value)):
1251
                    dbus_value = transform_func(type_func(value),
237.11.15 by Teddy Hogeborn
Bug fix: Make D-Bus properties settable again.
1252
                                                variant_level
1253
                                                =variant_level)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1254
                    self.PropertyChanged(dbus.String(dbus_name),
1255
                                         dbus_value)
237.11.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1256
            setattr(self, attrname, value)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1257
        
237.11.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1258
        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
1259
    
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1260
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
1261
    approvals_pending = notifychangeproperty(dbus.Boolean,
1262
                                             "ApprovalPending",
1263
                                             type_func = bool)
1264
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1265
    last_enabled = notifychangeproperty(datetime_to_dbus,
1266
                                        "LastEnabled")
1267
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1268
                                   type_func = lambda checker:
1269
                                       checker is not None)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1270
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1271
                                           "LastCheckedOK")
237.7.104 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1272
    last_checker_status = notifychangeproperty(dbus.Int16,
1273
                                               "LastCheckerStatus")
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1274
    last_approval_request = notifychangeproperty(
1275
        datetime_to_dbus, "LastApprovalRequest")
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1276
    approved_by_default = notifychangeproperty(dbus.Boolean,
1277
                                               "ApprovedByDefault")
237.14.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1278
    approval_delay = notifychangeproperty(dbus.UInt64,
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1279
                                          "ApprovalDelay",
1280
                                          type_func =
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1281
                                          timedelta_to_milliseconds)
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1282
    approval_duration = notifychangeproperty(
237.14.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1283
        dbus.UInt64, "ApprovalDuration",
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1284
        type_func = timedelta_to_milliseconds)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1285
    host = notifychangeproperty(dbus.String, "Host")
237.14.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1286
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1287
                                   type_func =
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1288
                                   timedelta_to_milliseconds)
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1289
    extended_timeout = notifychangeproperty(
237.14.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1290
        dbus.UInt64, "ExtendedTimeout",
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1291
        type_func = timedelta_to_milliseconds)
237.14.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1292
    interval = notifychangeproperty(dbus.UInt64,
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1293
                                    "Interval",
1294
                                    type_func =
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1295
                                    timedelta_to_milliseconds)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1296
    checker_command = notifychangeproperty(dbus.String, "Checker")
1297
    
1298
    del notifychangeproperty
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1299
    
1300
    def __del__(self, *args, **kwargs):
1301
        try:
1302
            self.remove_from_connection()
237.2.92 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
1303
        except LookupError:
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1304
            pass
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1305
        if hasattr(DBusObjectWithProperties, "__del__"):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1306
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1307
        Client.__del__(self, *args, **kwargs)
1308
    
1309
    def checker_callback(self, pid, condition, command,
1310
                         *args, **kwargs):
1311
        self.checker_callback_tag = None
1312
        self.checker = None
1313
        if os.WIFEXITED(condition):
1314
            exitstatus = os.WEXITSTATUS(condition)
1315
            # Emit D-Bus signal
1316
            self.CheckerCompleted(dbus.Int16(exitstatus),
1317
                                  dbus.Int64(condition),
1318
                                  dbus.String(command))
1319
        else:
1320
            # Emit D-Bus signal
1321
            self.CheckerCompleted(dbus.Int16(-1),
1322
                                  dbus.Int64(condition),
1323
                                  dbus.String(command))
1324
        
1325
        return Client.checker_callback(self, pid, condition, command,
1326
                                       *args, **kwargs)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1327
    
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1328
    def start_checker(self, *args, **kwargs):
1329
        old_checker = self.checker
1330
        if self.checker is not None:
1331
            old_checker_pid = self.checker.pid
1332
        else:
1333
            old_checker_pid = None
1334
        r = Client.start_checker(self, *args, **kwargs)
237.2.92 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
1335
        # Only if new checker process was started
1336
        if (self.checker is not None
1337
            and old_checker_pid != self.checker.pid):
1338
            # Emit D-Bus signal
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1339
            self.CheckerStarted(self.current_checker_command)
1340
        return r
1341
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1342
    def _reset_approved(self):
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1343
        self.approved = None
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1344
        return False
1345
    
1346
    def approve(self, value=True):
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1347
        self.approved = value
1348
        gobject.timeout_add(timedelta_to_milliseconds
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1349
                            (self.approval_duration),
24.1.154 by Björn Påhlsson
merge
1350
                            self._reset_approved)
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1351
        self.send_changedstate()
24.2.1 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-urwid".
1352
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1353
    ## D-Bus methods, signals & properties
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1354
    _interface = "se.recompile.Mandos.Client"
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1355
    
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1356
    ## Interfaces
1357
    
1358
    @dbus_interface_annotations(_interface)
1359
    def _foo(self):
1360
        return { "org.freedesktop.DBus.Property.EmitsChangedSignal":
1361
                     "false"}
1362
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1363
    ## Signals
237.2.144 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1364
    
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1365
    # CheckerCompleted - signal
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1366
    @dbus.service.signal(_interface, signature="nxs")
237.2.43 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
1367
    def CheckerCompleted(self, exitcode, waitstatus, command):
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1368
        "D-Bus signal"
1369
        pass
1370
    
1371
    # CheckerStarted - signal
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1372
    @dbus.service.signal(_interface, signature="s")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1373
    def CheckerStarted(self, command):
1374
        "D-Bus signal"
1375
        pass
1376
    
237.2.1 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1377
    # PropertyChanged - signal
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1378
    @dbus.service.signal(_interface, signature="sv")
237.2.1 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1379
    def PropertyChanged(self, property, value):
1380
        "D-Bus signal"
1381
        pass
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1382
    
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1383
    # GotSecret - signal
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
1384
    @dbus.service.signal(_interface)
237.2.150 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1385
    def GotSecret(self):
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1386
        """D-Bus signal
1387
        Is sent after a successful transfer of secret from the Mandos
1388
        server to mandos-client
1389
        """
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1390
        pass
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
1391
    
1392
    # Rejected - signal
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1393
    @dbus.service.signal(_interface, signature="s")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1394
    def Rejected(self, reason):
1395
        "D-Bus signal"
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1396
        pass
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1397
    
1398
    # NeedApproval - signal
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1399
    @dbus.service.signal(_interface, signature="tb")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1400
    def NeedApproval(self, timeout, default):
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
1401
        "D-Bus signal"
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1402
        return self.need_approval()
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
1403
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1404
    ## Methods
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1405
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1406
    # Approve - method
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1407
    @dbus.service.method(_interface, in_signature="b")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1408
    def Approve(self, value):
1409
        self.approve(value)
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1410
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1411
    # CheckedOK - method
1412
    @dbus.service.method(_interface)
1413
    def CheckedOK(self):
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1414
        self.checked_ok()
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1415
    
237.2.144 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1416
    # Enable - method
1417
    @dbus.service.method(_interface)
1418
    def Enable(self):
1419
        "D-Bus method"
1420
        self.enable()
1421
    
1422
    # StartChecker - method
1423
    @dbus.service.method(_interface)
1424
    def StartChecker(self):
1425
        "D-Bus method"
1426
        self.start_checker()
1427
    
1428
    # Disable - method
1429
    @dbus.service.method(_interface)
1430
    def Disable(self):
1431
        "D-Bus method"
1432
        self.disable()
1433
    
1434
    # StopChecker - method
1435
    @dbus.service.method(_interface)
1436
    def StopChecker(self):
1437
        self.stop_checker()
1438
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1439
    ## Properties
1440
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1441
    # ApprovalPending - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1442
    @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;
1443
    def ApprovalPending_dbus_property(self):
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1444
        return dbus.Boolean(bool(self.approvals_pending))
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1445
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1446
    # ApprovedByDefault - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1447
    @dbus_service_property(_interface, signature="b",
1448
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1449
    def ApprovedByDefault_dbus_property(self, value=None):
1450
        if value is None:       # get
1451
            return dbus.Boolean(self.approved_by_default)
1452
        self.approved_by_default = bool(value)
1453
    
1454
    # ApprovalDelay - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1455
    @dbus_service_property(_interface, signature="t",
1456
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1457
    def ApprovalDelay_dbus_property(self, value=None):
1458
        if value is None:       # get
1459
            return dbus.UInt64(self.approval_delay_milliseconds())
1460
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
1461
    
1462
    # ApprovalDuration - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1463
    @dbus_service_property(_interface, signature="t",
1464
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1465
    def ApprovalDuration_dbus_property(self, value=None):
1466
        if value is None:       # get
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1467
            return dbus.UInt64(timedelta_to_milliseconds(
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1468
                    self.approval_duration))
1469
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1470
    
1471
    # Name - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1472
    @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;
1473
    def Name_dbus_property(self):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1474
        return dbus.String(self.name)
1475
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1476
    # Fingerprint - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1477
    @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;
1478
    def Fingerprint_dbus_property(self):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1479
        return dbus.String(self.fingerprint)
1480
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1481
    # Host - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1482
    @dbus_service_property(_interface, signature="s",
1483
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1484
    def Host_dbus_property(self, value=None):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1485
        if value is None:       # get
1486
            return dbus.String(self.host)
237.14.8 by Teddy Hogeborn
* mandos (ClientDBus.Host_dbus_property,
1487
        self.host = unicode(value)
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1488
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1489
    # Created - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1490
    @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;
1491
    def Created_dbus_property(self):
237.14.3 by Teddy Hogeborn
Make "enabled" a client config option.
1492
        return datetime_to_dbus(self.created)
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1493
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1494
    # LastEnabled - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1495
    @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;
1496
    def LastEnabled_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1497
        return datetime_to_dbus(self.last_enabled)
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1498
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1499
    # Enabled - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1500
    @dbus_service_property(_interface, signature="b",
1501
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1502
    def Enabled_dbus_property(self, value=None):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1503
        if value is None:       # get
1504
            return dbus.Boolean(self.enabled)
1505
        if value:
1506
            self.enable()
1507
        else:
1508
            self.disable()
1509
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1510
    # LastCheckedOK - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1511
    @dbus_service_property(_interface, signature="s",
1512
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1513
    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
1514
        if value is not None:
1515
            self.checked_ok()
1516
            return
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1517
        return datetime_to_dbus(self.last_checked_ok)
24.1.179 by Björn Påhlsson
New feature:
1518
    
237.7.104 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1519
    # LastCheckerStatus - property
1520
    @dbus_service_property(_interface, signature="n",
1521
                           access="read")
1522
    def LastCheckerStatus_dbus_property(self):
1523
        return dbus.Int16(self.last_checker_status)
1524
    
24.1.179 by Björn Påhlsson
New feature:
1525
    # Expires - property
1526
    @dbus_service_property(_interface, signature="s", access="read")
1527
    def Expires_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1528
        return datetime_to_dbus(self.expires)
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1529
    
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1530
    # LastApprovalRequest - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1531
    @dbus_service_property(_interface, signature="s", access="read")
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1532
    def LastApprovalRequest_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1533
        return datetime_to_dbus(self.last_approval_request)
237.2.205 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1534
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1535
    # Timeout - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1536
    @dbus_service_property(_interface, signature="t",
1537
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1538
    def Timeout_dbus_property(self, value=None):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1539
        if value is None:       # get
1540
            return dbus.UInt64(self.timeout_milliseconds())
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1541
        old_timeout = self.timeout
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1542
        self.timeout = datetime.timedelta(0, 0, 0, value)
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1543
        # Reschedule disabling
237.7.91 by Teddy Hogeborn
* mandos: Break some long lines.
1544
        if self.enabled:
1545
            now = datetime.datetime.utcnow()
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1546
            self.expires += self.timeout - old_timeout
1547
            if self.expires <= now:
237.7.91 by Teddy Hogeborn
* mandos: Break some long lines.
1548
                # The timeout has passed
1549
                self.disable()
1550
            else:
1551
                if (getattr(self, "disable_initiator_tag", None)
1552
                    is None):
1553
                    return
1554
                gobject.source_remove(self.disable_initiator_tag)
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1555
                self.disable_initiator_tag = (
1556
                    gobject.timeout_add(
1557
                        timedelta_to_milliseconds(self.expires - now),
1558
                        self.disable))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1559
    
24.1.179 by Björn Påhlsson
New feature:
1560
    # ExtendedTimeout - property
1561
    @dbus_service_property(_interface, signature="t",
1562
                           access="readwrite")
1563
    def ExtendedTimeout_dbus_property(self, value=None):
1564
        if value is None:       # get
1565
            return dbus.UInt64(self.extended_timeout_milliseconds())
1566
        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
1567
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1568
    # Interval - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1569
    @dbus_service_property(_interface, signature="t",
1570
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1571
    def Interval_dbus_property(self, value=None):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1572
        if value is None:       # get
1573
            return dbus.UInt64(self.interval_milliseconds())
1574
        self.interval = datetime.timedelta(0, 0, 0, value)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1575
        if getattr(self, "checker_initiator_tag", None) is None:
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1576
            return
237.14.3 by Teddy Hogeborn
Make "enabled" a client config option.
1577
        if self.enabled:
1578
            # Reschedule checker run
1579
            gobject.source_remove(self.checker_initiator_tag)
1580
            self.checker_initiator_tag = (gobject.timeout_add
1581
                                          (value, self.start_checker))
1582
            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
1583
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1584
    # Checker - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1585
    @dbus_service_property(_interface, signature="s",
1586
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1587
    def Checker_dbus_property(self, value=None):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1588
        if value is None:       # get
1589
            return dbus.String(self.checker_command)
237.14.8 by Teddy Hogeborn
* mandos (ClientDBus.Host_dbus_property,
1590
        self.checker_command = unicode(value)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1591
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1592
    # CheckerRunning - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1593
    @dbus_service_property(_interface, signature="b",
1594
                           access="readwrite")
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1595
    def CheckerRunning_dbus_property(self, value=None):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1596
        if value is None:       # get
1597
            return dbus.Boolean(self.checker is not None)
1598
        if value:
1599
            self.start_checker()
1600
        else:
1601
            self.stop_checker()
1602
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1603
    # ObjectPath - property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1604
    @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;
1605
    def ObjectPath_dbus_property(self):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1606
        return self.dbus_object_path # is already a dbus.ObjectPath
1607
    
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1608
    # Secret = property
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1609
    @dbus_service_property(_interface, signature="ay",
1610
                           access="write", byte_arrays=True)
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1611
    def Secret_dbus_property(self, value):
237.2.143 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1612
        self.secret = str(value)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1613
    
1614
    del _interface
3 by Björn Påhlsson
Python based server
1615
1616
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1617
class ProxyClient(object):
1618
    def __init__(self, child_pipe, fpr, address):
1619
        self._pipe = child_pipe
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1620
        self._pipe.send(('init', fpr, address))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1621
        if not self._pipe.recv():
1622
            raise KeyError()
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1623
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1624
    def __getattribute__(self, name):
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1625
        if name == '_pipe':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1626
            return super(ProxyClient, self).__getattribute__(name)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1627
        self._pipe.send(('getattr', name))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1628
        data = self._pipe.recv()
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1629
        if data[0] == 'data':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1630
            return data[1]
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1631
        if data[0] == 'function':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1632
            def func(*args, **kwargs):
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1633
                self._pipe.send(('funcall', name, args, kwargs))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1634
                return self._pipe.recv()[1]
1635
            return func
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1636
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1637
    def __setattr__(self, name, value):
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1638
        if name == '_pipe':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1639
            return super(ProxyClient, self).__setattr__(name, value)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1640
        self._pipe.send(('setattr', name, value))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1641
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1642
237.2.98 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1643
class ClientHandler(socketserver.BaseRequestHandler, object):
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1644
    """A class to handle client connections.
1645
    
1646
    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
1647
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
1648
    
3 by Björn Påhlsson
Python based server
1649
    def handle(self):
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1650
        with contextlib.closing(self.server.child_pipe) as child_pipe:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1651
            logger.info("TCP connection from: %s",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1652
                        unicode(self.client_address))
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1653
            logger.debug("Pipe FD: %d",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1654
                         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
1655
            
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1656
            session = (gnutls.connection
1657
                       .ClientSession(self.request,
1658
                                      gnutls.connection
1659
                                      .X509Credentials()))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1660
            
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1661
            # Note: gnutls.connection.X509Credentials is really a
1662
            # generic GnuTLS certificate credentials object so long as
1663
            # no X.509 keys are added to it.  Therefore, we can use it
1664
            # 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
1665
            
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1666
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1667
            #                      "+AES-256-CBC", "+SHA1",
1668
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1669
            #                      "+DHE-DSS"))
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1670
            # Use a fallback default, since this MUST be set.
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1671
            priority = self.server.gnutls_priority
1672
            if priority is None:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1673
                priority = "NORMAL"
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1674
            (gnutls.library.functions
1675
             .gnutls_priority_set_direct(session._c_object,
1676
                                         priority, None))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1677
            
237.5.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1678
            # Start communication using the Mandos protocol
1679
            # Get protocol number
1680
            line = self.request.makefile().readline()
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1681
            logger.debug("Protocol version: %r", line)
237.5.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1682
            try:
1683
                if int(line.strip().split()[0]) > 1:
1684
                    raise RuntimeError
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1685
            except (ValueError, IndexError, RuntimeError) as error:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1686
                logger.error("Unknown protocol version: %s", error)
237.5.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1687
                return
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1688
            
237.5.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1689
            # Start GnuTLS connection
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1690
            try:
1691
                session.handshake()
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1692
            except gnutls.errors.GNUTLSError as error:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1693
                logger.warning("Handshake failed: %s", error)
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1694
                # Do not run session.bye() here: the session is not
1695
                # established.  Just abandon the request.
1696
                return
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1697
            logger.debug("Handshake succeeded")
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1698
            
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1699
            approval_required = False
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1700
            try:
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1701
                try:
1702
                    fpr = self.fingerprint(self.peer_certificate
1703
                                           (session))
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1704
                except (TypeError,
1705
                        gnutls.errors.GNUTLSError) as error:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1706
                    logger.warning("Bad certificate: %s", error)
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1707
                    return
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1708
                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
1709
                
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1710
                try:
1711
                    client = ProxyClient(child_pipe, fpr,
1712
                                         self.client_address)
1713
                except KeyError:
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1714
                    return
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1715
                
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1716
                if client.approval_delay:
1717
                    delay = client.approval_delay
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1718
                    client.approvals_pending += 1
1719
                    approval_required = True
1720
                
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
1721
                while True:
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1722
                    if not client.enabled:
24.1.174 by Björn Påhlsson
* Makefile (CFLAGS): Added "-lrt" to include real time library.
1723
                        logger.info("Client %s is disabled",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1724
                                       client.name)
1725
                        if self.server.use_dbus:
1726
                            # Emit D-Bus signal
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
1727
                            client.Rejected("Disabled")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1728
                        return
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1729
                    
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1730
                    if client.approved or not client.approval_delay:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1731
                        #We are approved or approval is disabled
1732
                        break
237.14.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1733
                    elif client.approved is None:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1734
                        logger.info("Client %s needs approval",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1735
                                    client.name)
1736
                        if self.server.use_dbus:
1737
                            # Emit D-Bus signal
1738
                            client.NeedApproval(
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1739
                                client.approval_delay_milliseconds(),
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1740
                                client.approved_by_default)
1741
                    else:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1742
                        logger.warning("Client %s was not approved",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1743
                                       client.name)
1744
                        if self.server.use_dbus:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1745
                            # Emit D-Bus signal
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1746
                            client.Rejected("Denied")
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
1747
                        return
1748
                    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1749
                    #wait until timeout or approved
1750
                    time = datetime.datetime.now()
1751
                    client.changedstate.acquire()
237.7.129 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1752
                    client.changedstate.wait(
1753
                        float(timedelta_to_milliseconds(delay)
1754
                              / 1000))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1755
                    client.changedstate.release()
1756
                    time2 = datetime.datetime.now()
1757
                    if (time2 - time) >= delay:
1758
                        if not client.approved_by_default:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1759
                            logger.warning("Client %s timed out while"
1760
                                           " waiting for approval",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1761
                                           client.name)
1762
                            if self.server.use_dbus:
1763
                                # Emit D-Bus signal
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1764
                                client.Rejected("Approval timed out")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1765
                            return
1766
                        else:
1767
                            break
1768
                    else:
1769
                        delay -= time2 - time
1770
                
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1771
                sent_size = 0
1772
                while sent_size < len(client.secret):
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1773
                    try:
1774
                        sent = session.send(client.secret[sent_size:])
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1775
                    except gnutls.errors.GNUTLSError as error:
237.7.122 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1776
                        logger.warning("gnutls send failed",
1777
                                       exc_info=error)
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1778
                        return
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1779
                    logger.debug("Sent: %d, remaining: %d",
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1780
                                 sent, len(client.secret)
1781
                                 - (sent_size + sent))
1782
                    sent_size += sent
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1783
                
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1784
                logger.info("Sending secret to %s", client.name)
237.11.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
1785
                # bump the timeout using extended_timeout
237.7.104 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1786
                client.bump_timeout(client.extended_timeout)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1787
                if self.server.use_dbus:
1788
                    # Emit D-Bus signal
1789
                    client.GotSecret()
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1790
            
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1791
            finally:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1792
                if approval_required:
1793
                    client.approvals_pending -= 1
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1794
                try:
1795
                    session.bye()
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1796
                except gnutls.errors.GNUTLSError as error:
237.7.122 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1797
                    logger.warning("GnuTLS bye failed",
1798
                                   exc_info=error)
237.2.93 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1799
    
1800
    @staticmethod
1801
    def peer_certificate(session):
1802
        "Return the peer's OpenPGP certificate as a bytestring"
1803
        # If not an OpenPGP certificate...
1804
        if (gnutls.library.functions
1805
            .gnutls_certificate_type_get(session._c_object)
1806
            != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1807
            # ...do the normal thing
1808
            return session.peer_certificate
1809
        list_size = ctypes.c_uint(1)
1810
        cert_list = (gnutls.library.functions
1811
                     .gnutls_certificate_get_peers
1812
                     (session._c_object, ctypes.byref(list_size)))
1813
        if not bool(cert_list) and list_size.value != 0:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1814
            raise gnutls.errors.GNUTLSError("error getting peer"
1815
                                            " certificate")
237.2.93 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1816
        if list_size.value == 0:
1817
            return None
1818
        cert = cert_list[0]
1819
        return ctypes.string_at(cert.data, cert.size)
1820
    
1821
    @staticmethod
1822
    def fingerprint(openpgp):
1823
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
1824
        # New GnuTLS "datum" with the OpenPGP public key
1825
        datum = (gnutls.library.types
1826
                 .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1827
                                             ctypes.POINTER
1828
                                             (ctypes.c_ubyte)),
1829
                                 ctypes.c_uint(len(openpgp))))
1830
        # New empty GnuTLS certificate
1831
        crt = gnutls.library.types.gnutls_openpgp_crt_t()
1832
        (gnutls.library.functions
1833
         .gnutls_openpgp_crt_init(ctypes.byref(crt)))
1834
        # Import the OpenPGP public key into the certificate
1835
        (gnutls.library.functions
1836
         .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1837
                                    gnutls.library.constants
1838
                                    .GNUTLS_OPENPGP_FMT_RAW))
1839
        # Verify the self signature in the key
1840
        crtverify = ctypes.c_uint()
1841
        (gnutls.library.functions
1842
         .gnutls_openpgp_crt_verify_self(crt, 0,
1843
                                         ctypes.byref(crtverify)))
1844
        if crtverify.value != 0:
1845
            gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1846
            raise (gnutls.errors.CertificateSecurityError
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1847
                   ("Verify failed"))
237.2.93 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1848
        # New buffer for the fingerprint
1849
        buf = ctypes.create_string_buffer(20)
1850
        buf_len = ctypes.c_size_t()
1851
        # Get the fingerprint from the certificate into the buffer
1852
        (gnutls.library.functions
1853
         .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1854
                                             ctypes.byref(buf_len)))
1855
        # Deinit the certificate
1856
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1857
        # Convert the buffer to a Python bytestring
1858
        fpr = ctypes.string_at(buf, buf_len.value)
1859
        # Convert the bytestring to hexadecimal notation
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
1860
        hex_fpr = binascii.hexlify(fpr).upper()
237.2.93 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1861
        return hex_fpr
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1862
1863
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1864
class MultiprocessingMixIn(object):
1865
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
1866
    def sub_process_main(self, request, address):
1867
        try:
1868
            self.finish_request(request, address)
237.12.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
1869
        except Exception:
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1870
            self.handle_error(request, address)
1871
        self.close_request(request)
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1872
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1873
    def process_request(self, request, address):
1874
        """Start a new process to process the request."""
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1875
        proc = multiprocessing.Process(target = self.sub_process_main,
237.7.127 by Teddy Hogeborn
* mandos: Use all new builtins.
1876
                                       args = (request, address))
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1877
        proc.start()
1878
        return proc
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1879
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1880
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1881
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1882
    """ adds a pipe to the MixIn """
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1883
    def process_request(self, request, client_address):
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1884
        """Overrides and wraps the original process_request().
1885
        
237.2.118 by Teddy Hogeborn
* mandos: White-space fixes only.
1886
        This function creates a new pipe in self.pipe
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1887
        """
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1888
        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
1889
        
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1890
        proc = MultiprocessingMixIn.process_request(self, request,
1891
                                                    client_address)
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
1892
        self.child_pipe.close()
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1893
        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
1894
    
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1895
    def add_pipe(self, parent_pipe, proc):
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1896
        """Dummy function; override as necessary"""
237.7.12 by Teddy Hogeborn
* debian/mandos-client.postrm (purge): Bug fix: update initramfs also
1897
        raise NotImplementedError
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1898
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1899
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1900
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
237.2.98 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1901
                     socketserver.TCPServer, object):
250 by Teddy Hogeborn
Merge from trunk. Notable changes:
1902
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1903
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1904
    Attributes:
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1905
        enabled:        Boolean; whether this server is activated yet
1906
        interface:      None or a network interface name (string)
1907
        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
1908
    """
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1909
    def __init__(self, server_address, RequestHandlerClass,
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
1910
                 interface=None, use_ipv6=True, socketfd=None):
1911
        """If socketfd is set, use that file descriptor instead of
1912
        creating a new one with socket.socket().
1913
        """
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1914
        self.interface = interface
1915
        if use_ipv6:
1916
            self.address_family = socket.AF_INET6
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
1917
        if socketfd is not None:
1918
            # Save the file descriptor
1919
            self.socketfd = socketfd
1920
            # Save the original socket.socket() function
1921
            self.socket_socket = socket.socket
1922
            # To implement --socket, we monkey patch socket.socket.
1923
            # 
1924
            # (When socketserver.TCPServer is a new-style class, we
1925
            # could make self.socket into a property instead of monkey
1926
            # patching socket.socket.)
1927
            # 
1928
            # Create a one-time-only replacement for socket.socket()
1929
            @functools.wraps(socket.socket)
1930
            def socket_wrapper(*args, **kwargs):
1931
                # Restore original function so subsequent calls are
1932
                # not affected.
1933
                socket.socket = self.socket_socket
1934
                del self.socket_socket
1935
                # This time only, return a new socket object from the
1936
                # saved file descriptor.
1937
                return socket.fromfd(self.socketfd, *args, **kwargs)
1938
            # Replace socket.socket() function with wrapper
1939
            socket.socket = socket_wrapper
1940
        # The socketserver.TCPServer.__init__ will call
1941
        # socket.socket(), which might be our replacement,
1942
        # socket_wrapper(), if socketfd was set.
237.2.98 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1943
        socketserver.TCPServer.__init__(self, server_address,
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1944
                                        RequestHandlerClass)
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
1945
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1946
    def server_bind(self):
1947
        """This overrides the normal server_bind() function
1948
        to bind to an interface if one was specified, and also NOT to
1949
        bind to an address or port if they were not specified."""
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1950
        if self.interface is not None:
237.2.101 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1951
            if SO_BINDTODEVICE is None:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1952
                logger.error("SO_BINDTODEVICE does not exist;"
1953
                             " cannot bind to interface %s",
237.2.101 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1954
                             self.interface)
1955
            else:
1956
                try:
1957
                    self.socket.setsockopt(socket.SOL_SOCKET,
1958
                                           SO_BINDTODEVICE,
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
1959
                                           str(self.interface + '\0'))
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1960
                except socket.error as error:
237.7.130 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1961
                    if error.errno == errno.EPERM:
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
1962
                        logger.error("No permission to bind to"
1963
                                     " interface %s", self.interface)
237.7.130 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1964
                    elif error.errno == errno.ENOPROTOOPT:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1965
                        logger.error("SO_BINDTODEVICE not available;"
1966
                                     " cannot bind to interface %s",
237.2.101 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1967
                                     self.interface)
237.7.130 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1968
                    elif error.errno == errno.ENODEV:
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
1969
                        logger.error("Interface %s does not exist,"
1970
                                     " cannot bind", self.interface)
237.2.101 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1971
                    else:
1972
                        raise
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1973
        # Only bind(2) the socket if we really need to.
1974
        if self.server_address[0] or self.server_address[1]:
1975
            if not self.server_address[0]:
237.2.77 by Teddy Hogeborn
Support not using IPv6 in server:
1976
                if self.address_family == socket.AF_INET6:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
1977
                    any_address = "::" # in6addr_any
237.2.77 by Teddy Hogeborn
Support not using IPv6 in server:
1978
                else:
1979
                    any_address = socket.INADDR_ANY
1980
                self.server_address = (any_address,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1981
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
1982
            elif not self.server_address[1]:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1983
                self.server_address = (self.server_address[0],
1984
                                       0)
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1985
#                 if self.interface:
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
1986
#                     self.server_address = (self.server_address[0],
1987
#                                            0, # port
1988
#                                            0, # flowinfo
1989
#                                            if_nametoindex
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1990
#                                            (self.interface))
237.2.98 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1991
            return socketserver.TCPServer.server_bind(self)
237.2.102 by Teddy Hogeborn
Code cleanup.
1992
1993
1994
class MandosServer(IPv6_TCPServer):
1995
    """Mandos server.
1996
    
1997
    Attributes:
1998
        clients:        set of Client objects
1999
        gnutls_priority GnuTLS priority string
2000
        use_dbus:       Boolean; to emit D-Bus signals or not
237.2.103 by Teddy Hogeborn
Code cleanup.
2001
    
2002
    Assumes a gobject.MainLoop event loop.
237.2.102 by Teddy Hogeborn
Code cleanup.
2003
    """
2004
    def __init__(self, server_address, RequestHandlerClass,
2005
                 interface=None, use_ipv6=True, clients=None,
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2006
                 gnutls_priority=None, use_dbus=True, socketfd=None):
237.2.102 by Teddy Hogeborn
Code cleanup.
2007
        self.enabled = False
2008
        self.clients = clients
237.2.104 by Teddy Hogeborn
Code cleanup and one bug fix.
2009
        if self.clients is None:
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2010
            self.clients = {}
237.2.102 by Teddy Hogeborn
Code cleanup.
2011
        self.use_dbus = use_dbus
2012
        self.gnutls_priority = gnutls_priority
2013
        IPv6_TCPServer.__init__(self, server_address,
2014
                                RequestHandlerClass,
2015
                                interface = interface,
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2016
                                use_ipv6 = use_ipv6,
2017
                                socketfd = socketfd)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2018
    def server_activate(self):
2019
        if self.enabled:
237.2.98 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2020
            return socketserver.TCPServer.server_activate(self)
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2021
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2022
    def enable(self):
2023
        self.enabled = True
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2024
    
2025
    def add_pipe(self, parent_pipe, proc):
237.2.103 by Teddy Hogeborn
Code cleanup.
2026
        # Call "handle_ipc" for both data and EOF events
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2027
        gobject.io_add_watch(parent_pipe.fileno(),
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2028
                             gobject.IO_IN | gobject.IO_HUP,
2029
                             functools.partial(self.handle_ipc,
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2030
                                               parent_pipe =
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2031
                                               parent_pipe,
2032
                                               proc = proc))
237.11.9 by Teddy Hogeborn
Whitespace
2033
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2034
    def handle_ipc(self, source, condition, parent_pipe=None,
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2035
                   proc = None, client_object=None):
237.11.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2036
        # error, or the other end of multiprocessing.Pipe has closed
237.7.125 by Teddy Hogeborn
* mandos (Client.start_checker): Reworded comment.
2037
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
237.11.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2038
            # Wait for other process to exit
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2039
            proc.join()
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
2040
            return False
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2041
        
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2042
        # Read a request from the child
2043
        request = parent_pipe.recv()
2044
        command = request[0]
2045
        
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2046
        if command == 'init':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2047
            fpr = request[1]
2048
            address = request[2]
2049
            
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2050
            for c in self.clients.itervalues():
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2051
                if c.fingerprint == fpr:
2052
                    client = c
2053
                    break
2054
            else:
24.1.174 by Björn Påhlsson
* Makefile (CFLAGS): Added "-lrt" to include real time library.
2055
                logger.info("Client not found for fingerprint: %s, ad"
2056
                            "dress: %s", fpr, address)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2057
                if self.use_dbus:
2058
                    # Emit D-Bus signal
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2059
                    mandos_dbus_service.ClientNotFound(fpr,
2060
                                                       address[0])
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2061
                parent_pipe.send(False)
2062
                return False
2063
            
2064
            gobject.io_add_watch(parent_pipe.fileno(),
2065
                                 gobject.IO_IN | gobject.IO_HUP,
2066
                                 functools.partial(self.handle_ipc,
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2067
                                                   parent_pipe =
2068
                                                   parent_pipe,
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2069
                                                   proc = proc,
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2070
                                                   client_object =
2071
                                                   client))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2072
            parent_pipe.send(True)
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2073
            # remove the old hook in favor of the new above hook on
2074
            # same fileno
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2075
            return False
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2076
        if command == 'funcall':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2077
            funcname = request[1]
2078
            args = request[2]
2079
            kwargs = request[3]
2080
            
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2081
            parent_pipe.send(('data', getattr(client_object,
2082
                                              funcname)(*args,
2083
                                                         **kwargs)))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2084
        
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2085
        if command == 'getattr':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2086
            attrname = request[1]
2087
            if callable(client_object.__getattribute__(attrname)):
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2088
                parent_pipe.send(('function',))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2089
            else:
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2090
                parent_pipe.send(('data', client_object
2091
                                  .__getattribute__(attrname)))
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
2092
        
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2093
        if command == 'setattr':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2094
            attrname = request[1]
2095
            value = request[2]
2096
            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
2097
        
237.4.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2098
        return True
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
2099
3 by Björn Påhlsson
Python based server
2100
237.7.157 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2101
def rfc3339_duration_to_delta(duration):
2102
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2103
    
2104
    >>> rfc3339_duration_to_delta("P7D")
2105
    datetime.timedelta(7)
2106
    >>> rfc3339_duration_to_delta("PT60S")
2107
    datetime.timedelta(0, 60)
2108
    >>> rfc3339_duration_to_delta("PT60M")
2109
    datetime.timedelta(0, 3600)
2110
    >>> rfc3339_duration_to_delta("PT24H")
2111
    datetime.timedelta(1)
2112
    >>> rfc3339_duration_to_delta("P1W")
2113
    datetime.timedelta(7)
2114
    >>> rfc3339_duration_to_delta("PT5M30S")
2115
    datetime.timedelta(0, 330)
2116
    >>> rfc3339_duration_to_delta("P1DT3M20S")
2117
    datetime.timedelta(1, 200)
2118
    """
2119
    
2120
    # Parsing an RFC 3339 duration with regular expressions is not
2121
    # possible - there would have to be multiple places for the same
2122
    # values, like seconds.  The current code, while more esoteric, is
2123
    # cleaner without depending on a parsing library.  If Python had a
2124
    # built-in library for parsing we would use it, but we'd like to
2125
    # avoid excessive use of external libraries.
2126
    
2127
    # New type for defining tokens, syntax, and semantics all-in-one
2128
    Token = collections.namedtuple("Token",
2129
                                   ("regexp", # To match token; if
2130
                                              # "value" is not None,
2131
                                              # must have a "group"
2132
                                              # containing digits
2133
                                    "value",  # datetime.timedelta or
2134
                                              # None
2135
                                    "followers")) # Tokens valid after
2136
                                                  # this token
2137
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
2138
    # the "duration" ABNF definition in RFC 3339, Appendix A.
2139
    token_end = Token(re.compile(r"$"), None, frozenset())
2140
    token_second = Token(re.compile(r"(\d+)S"),
2141
                         datetime.timedelta(seconds=1),
2142
                         frozenset((token_end,)))
2143
    token_minute = Token(re.compile(r"(\d+)M"),
2144
                         datetime.timedelta(minutes=1),
2145
                         frozenset((token_second, token_end)))
2146
    token_hour = Token(re.compile(r"(\d+)H"),
2147
                       datetime.timedelta(hours=1),
2148
                       frozenset((token_minute, token_end)))
2149
    token_time = Token(re.compile(r"T"),
2150
                       None,
2151
                       frozenset((token_hour, token_minute,
2152
                                  token_second)))
2153
    token_day = Token(re.compile(r"(\d+)D"),
2154
                      datetime.timedelta(days=1),
2155
                      frozenset((token_time, token_end)))
2156
    token_month = Token(re.compile(r"(\d+)M"),
2157
                        datetime.timedelta(weeks=4),
2158
                        frozenset((token_day, token_end)))
2159
    token_year = Token(re.compile(r"(\d+)Y"),
2160
                       datetime.timedelta(weeks=52),
2161
                       frozenset((token_month, token_end)))
2162
    token_week = Token(re.compile(r"(\d+)W"),
2163
                       datetime.timedelta(weeks=1),
2164
                       frozenset((token_end,)))
2165
    token_duration = Token(re.compile(r"P"), None,
2166
                           frozenset((token_year, token_month,
2167
                                      token_day, token_time,
2168
                                      token_week))),
2169
    # Define starting values
2170
    value = datetime.timedelta() # Value so far
2171
    found_token = None
2172
    followers = frozenset(token_duration,) # Following valid tokens
2173
    s = duration                # String left to parse
2174
    # Loop until end token is found
2175
    while found_token is not token_end:
2176
        # Search for any currently valid tokens
2177
        for token in followers:
2178
            match = token.regexp.match(s)
2179
            if match is not None:
2180
                # Token found
2181
                if token.value is not None:
2182
                    # Value found, parse digits
2183
                    factor = int(match.group(1), 10)
2184
                    # Add to value so far
2185
                    value += factor * token.value
2186
                # Strip token from string
2187
                s = token.regexp.sub("", s, 1)
2188
                # Go to found token
2189
                found_token = token
2190
                # Set valid next tokens
2191
                followers = found_token.followers
2192
                break
2193
        else:
2194
            # No currently valid tokens were found
2195
            raise ValueError("Invalid RFC 3339 duration")
2196
    # End token found
2197
    return value
2198
2199
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2200
def string_to_delta(interval):
2201
    """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
2202
    
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2203
    >>> string_to_delta('7d')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2204
    datetime.timedelta(7)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2205
    >>> string_to_delta('60s')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2206
    datetime.timedelta(0, 60)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2207
    >>> string_to_delta('60m')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2208
    datetime.timedelta(0, 3600)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2209
    >>> string_to_delta('24h')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2210
    datetime.timedelta(1)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2211
    >>> string_to_delta('1w')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2212
    datetime.timedelta(7)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2213
    >>> string_to_delta('5m 30s')
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2214
    datetime.timedelta(0, 330)
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2215
    """
237.7.157 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2216
    
2217
    try:
2218
        return rfc3339_duration_to_delta(interval)
2219
    except ValueError:
2220
        pass
2221
    
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2222
    timevalue = datetime.timedelta(0)
2223
    for s in interval.split():
2224
        try:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
2225
            suffix = unicode(s[-1])
2226
            value = int(s[:-1])
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2227
            if suffix == "d":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2228
                delta = datetime.timedelta(value)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2229
            elif suffix == "s":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2230
                delta = datetime.timedelta(0, value)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2231
            elif suffix == "m":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2232
                delta = datetime.timedelta(0, 0, 0, 0, value)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2233
            elif suffix == "h":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2234
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2235
            elif suffix == "w":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2236
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
2237
            else:
237.7.116 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2238
                raise ValueError("Unknown suffix {0!r}"
2239
                                 .format(suffix))
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2240
        except (ValueError, IndexError) as e:
237.7.12 by Teddy Hogeborn
* debian/mandos-client.postrm (purge): Bug fix: update initramfs also
2241
            raise ValueError(*(e.args))
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2242
        timevalue += delta
2243
    return timevalue
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2244
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
2245
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
2246
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2247
    """See daemon(3).  Standard BSD Unix function.
237.2.94 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2248
    
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2249
    This should really exist as os.daemon, but it doesn't (yet)."""
2250
    if os.fork():
2251
        sys.exit()
2252
    os.setsid()
2253
    if not nochdir:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2254
        os.chdir("/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
2255
    if os.fork():
2256
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2257
    if not noclose:
2258
        # 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
2259
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2260
        if not stat.S_ISCHR(os.fstat(null).st_mode):
2261
            raise OSError(errno.ENODEV,
237.7.116 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2262
                          "{0} not a character device"
2263
                          .format(os.devnull))
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2264
        os.dup2(null, sys.stdin.fileno())
2265
        os.dup2(null, sys.stdout.fileno())
2266
        os.dup2(null, sys.stderr.fileno())
2267
        if null > 2:
2268
            os.close(null)
2269
2270
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2271
def main():
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
2272
    
237.2.142 by Teddy Hogeborn
* mandos: Fix line lengths.
2273
    ##################################################################
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
2274
    # Parsing of options, both command line and config file
2275
    
237.7.22 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2276
    parser = argparse.ArgumentParser()
2277
    parser.add_argument("-v", "--version", action="version",
237.7.116 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2278
                        version = "%(prog)s {0}".format(version),
237.7.22 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2279
                        help="show version number and exit")
2280
    parser.add_argument("-i", "--interface", metavar="IF",
2281
                        help="Bind to interface IF")
2282
    parser.add_argument("-a", "--address",
2283
                        help="Address to listen for requests on")
2284
    parser.add_argument("-p", "--port", type=int,
2285
                        help="Port number to receive requests on")
2286
    parser.add_argument("--check", action="store_true",
2287
                        help="Run self-test")
2288
    parser.add_argument("--debug", action="store_true",
2289
                        help="Debug mode; run in foreground and log"
2290
                        " to terminal")
2291
    parser.add_argument("--debuglevel", metavar="LEVEL",
2292
                        help="Debug level for stdout output")
2293
    parser.add_argument("--priority", help="GnuTLS"
2294
                        " priority string (see GnuTLS documentation)")
2295
    parser.add_argument("--servicename",
2296
                        metavar="NAME", help="Zeroconf service name")
2297
    parser.add_argument("--configdir",
2298
                        default="/etc/mandos", metavar="DIR",
2299
                        help="Directory to search for configuration"
2300
                        " files")
2301
    parser.add_argument("--no-dbus", action="store_false",
2302
                        dest="use_dbus", help="Do not provide D-Bus"
2303
                        " system bus interface")
2304
    parser.add_argument("--no-ipv6", action="store_false",
2305
                        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
2306
    parser.add_argument("--no-restore", action="store_false",
237.12.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
2307
                        dest="restore", help="Do not restore stored"
237.14.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2308
                        " state")
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2309
    parser.add_argument("--socket", type=int,
2310
                        help="Specify a file descriptor to a network"
2311
                        " socket to use instead of creating one")
237.14.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2312
    parser.add_argument("--statedir", metavar="DIR",
2313
                        help="Directory to save/restore state in")
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2314
    parser.add_argument("--foreground", action="store_true",
2315
                        help="Run in foreground")
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2316
    
237.7.22 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2317
    options = parser.parse_args()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2318
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2319
    if options.check:
2320
        import doctest
2321
        doctest.testmod()
2322
        sys.exit()
3 by Björn Påhlsson
Python based server
2323
    
28 by Teddy Hogeborn
* server.conf: New file.
2324
    # Default values for config file for server-global settings
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2325
    server_defaults = { "interface": "",
2326
                        "address": "",
2327
                        "port": "",
2328
                        "debug": "False",
2329
                        "priority":
2330
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2331
                        "servicename": "Mandos",
2332
                        "use_dbus": "True",
2333
                        "use_ipv6": "True",
2334
                        "debuglevel": "",
237.14.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2335
                        "restore": "True",
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2336
                        "socket": "",
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2337
                        "statedir": "/var/lib/mandos",
2338
                        "foreground": "False",
28 by Teddy Hogeborn
* server.conf: New file.
2339
                        }
2340
    
2341
    # Parse config file for server-global settings
237.2.98 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2342
    server_config = configparser.SafeConfigParser(server_defaults)
28 by Teddy Hogeborn
* server.conf: New file.
2343
    del server_defaults
237.2.96 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2344
    server_config.read(os.path.join(options.configdir,
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2345
                                    "mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
2346
    # Convert the SafeConfigParser object to a dict
89 by Teddy Hogeborn
* Makefile: Bug fix: fixed creation of man pages for section 5 pages.
2347
    server_settings = server_config.defaults()
237.2.45 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
2348
    # Use the appropriate methods on the non-string config options
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2349
    for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2350
        server_settings[option] = server_config.getboolean("DEFAULT",
237.2.96 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2351
                                                           option)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2352
    if server_settings["port"]:
2353
        server_settings["port"] = server_config.getint("DEFAULT",
2354
                                                       "port")
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2355
    if server_settings["socket"]:
2356
        server_settings["socket"] = server_config.getint("DEFAULT",
2357
                                                         "socket")
237.7.139 by Teddy Hogeborn
Merge "--socket" option for server.
2358
        # Later, stdin will, and stdout and stderr might, be dup'ed
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2359
        # over with an opened os.devnull.  But we don't want this to
2360
        # happen with a supplied network socket.
2361
        if 0 <= server_settings["socket"] <= 2:
2362
            server_settings["socket"] = os.dup(server_settings
2363
                                               ["socket"])
28 by Teddy Hogeborn
* server.conf: New file.
2364
    del server_config
2365
    
2366
    # Override the settings from the config file with command line
2367
    # options, if set.
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2368
    for option in ("interface", "address", "port", "debug",
2369
                   "priority", "servicename", "configdir",
237.14.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2370
                   "use_dbus", "use_ipv6", "debuglevel", "restore",
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2371
                   "statedir", "socket", "foreground"):
28 by Teddy Hogeborn
* server.conf: New file.
2372
        value = getattr(options, option)
2373
        if value is not None:
2374
            server_settings[option] = value
2375
    del options
237.2.96 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2376
    # Force all strings to be unicode
2377
    for option in server_settings.keys():
2378
        if type(server_settings[option]) is str:
2379
            server_settings[option] = unicode(server_settings[option])
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2380
    # Debug implies foreground
2381
    if server_settings["debug"]:
2382
        server_settings["foreground"] = True
28 by Teddy Hogeborn
* server.conf: New file.
2383
    # Now we have our good server settings in "server_settings"
2384
    
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
2385
    ##################################################################
2386
    
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2387
    # For convenience
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2388
    debug = server_settings["debug"]
2389
    debuglevel = server_settings["debuglevel"]
2390
    use_dbus = server_settings["use_dbus"]
2391
    use_ipv6 = server_settings["use_ipv6"]
237.14.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2392
    stored_state_path = os.path.join(server_settings["statedir"],
2393
                                     stored_state_file)
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2394
    foreground = server_settings["foreground"]
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2395
    
237.12.4 by Björn Påhlsson
restructured logger
2396
    if debug:
237.7.78 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2397
        initlogger(debug, logging.DEBUG)
237.12.4 by Björn Påhlsson
restructured logger
2398
    else:
2399
        if not debuglevel:
237.7.78 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2400
            initlogger(debug)
237.12.4 by Björn Påhlsson
restructured logger
2401
        else:
2402
            level = getattr(logging, debuglevel.upper())
237.7.78 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2403
            initlogger(debug, level)
237.12.4 by Björn Påhlsson
restructured logger
2404
    
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2405
    if server_settings["servicename"] != "Mandos":
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
2406
        syslogger.setFormatter(logging.Formatter
237.7.116 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2407
                               ('Mandos ({0}) [%(process)d]:'
2408
                                ' %(levelname)s: %(message)s'
2409
                                .format(server_settings
2410
                                        ["servicename"])))
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
2411
    
28 by Teddy Hogeborn
* server.conf: New file.
2412
    # Parse config file with clients
237.7.91 by Teddy Hogeborn
* mandos: Break some long lines.
2413
    client_config = configparser.SafeConfigParser(Client
2414
                                                  .client_defaults)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2415
    client_config.read(os.path.join(server_settings["configdir"],
2416
                                    "clients.conf"))
237.2.96 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2417
    
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
2418
    global mandos_dbus_service
2419
    mandos_dbus_service = None
28 by Teddy Hogeborn
* server.conf: New file.
2420
    
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2421
    tcp_server = MandosServer((server_settings["address"],
2422
                               server_settings["port"]),
237.2.102 by Teddy Hogeborn
Code cleanup.
2423
                              ClientHandler,
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2424
                              interface=(server_settings["interface"]
269.1.3 by teddy at bsnet
* mandos (main): Bug fix: Don't try to bind to an empty string
2425
                                         or None),
237.2.102 by Teddy Hogeborn
Code cleanup.
2426
                              use_ipv6=use_ipv6,
2427
                              gnutls_priority=
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2428
                              server_settings["priority"],
237.21.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2429
                              use_dbus=use_dbus,
2430
                              socketfd=(server_settings["socket"]
2431
                                        or None))
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2432
    if not foreground:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2433
        pidfilename = "/var/run/mandos.pid"
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2434
        pidfile = None
237.2.202 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
2435
        try:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2436
            pidfile = open(pidfilename, "w")
237.7.122 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2437
        except IOError as e:
2438
            logger.error("Could not open file %r", pidfilename,
2439
                         exc_info=e)
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
2440
    
237.7.114 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2441
    for name in ("_mandos", "mandos", "nobody"):
237.2.1 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2442
        try:
237.7.114 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2443
            uid = pwd.getpwnam(name).pw_uid
2444
            gid = pwd.getpwnam(name).pw_gid
2445
            break
237.2.1 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2446
        except KeyError:
237.7.114 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2447
            continue
2448
    else:
2449
        uid = 65534
2450
        gid = 65534
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2451
    try:
237.2.53 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2452
        os.setgid(gid)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2453
        os.setuid(uid)
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2454
    except OSError as error:
237.7.131 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
2455
        if error.errno != errno.EPERM:
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2456
            raise error
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
2457
    
237.2.53 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2458
    if debug:
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2459
        # Enable all possible GnuTLS debugging
2460
        
237.2.53 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2461
        # "Use a log level over 10 to enable all debugging options."
2462
        # - GnuTLS manual
2463
        gnutls.library.functions.gnutls_global_set_log_level(11)
2464
        
2465
        @gnutls.library.types.gnutls_log_func
2466
        def debug_gnutls(level, string):
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2467
            logger.debug("GnuTLS: %s", string[:-1])
237.2.53 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2468
        
2469
        (gnutls.library.functions
2470
         .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;
2471
        
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
2472
        # 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
2473
        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
2474
        os.dup2(null, sys.stdin.fileno())
2475
        if null > 2:
2476
            os.close(null)
237.2.185 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2477
    
237.7.6 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2478
    # Need to fork before connecting to D-Bus
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2479
    if not foreground:
237.7.6 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2480
        # Close all input and output, do double fork, etc.
2481
        daemon()
237.2.53 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2482
    
237.7.138 by Teddy Hogeborn
* mandos: Change comment.
2483
    # multiprocessing will use threads, so before we use gobject we
2484
    # 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)
2485
    gobject.threads_init()
2486
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2487
    global main_loop
2488
    # From the Avahi example code
237.18.1 by teddy at recompile
* mandos: Use os.devnull instead of os.path.devnull. Fix some white
2489
    DBusGMainLoop(set_as_default=True)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2490
    main_loop = gobject.MainLoop()
2491
    bus = dbus.SystemBus()
2492
    # End of Avahi example code
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2493
    if use_dbus:
237.2.165 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2494
        try:
237.7.53 by Björn Påhlsson
The domain name has changed, so the D-Bus bus and interface names must
2495
            bus_name = dbus.service.BusName("se.recompile.Mandos",
237.2.165 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2496
                                            bus, do_not_queue=True)
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2497
            old_bus_name = (dbus.service.BusName
2498
                            ("se.bsnet.fukt.Mandos", bus,
2499
                             do_not_queue=True))
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2500
        except dbus.exceptions.NameExistsException as e:
237.7.122 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2501
            logger.error("Disabling D-Bus:", exc_info=e)
237.2.165 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2502
            use_dbus = False
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2503
            server_settings["use_dbus"] = False
237.2.165 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2504
            tcp_server.use_dbus = False
237.2.100 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
2505
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
237.11.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
2506
    service = AvahiServiceToSyslog(name =
2507
                                   server_settings["servicename"],
2508
                                   servicetype = "_mandos._tcp",
2509
                                   protocol = protocol, bus = bus)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2510
    if server_settings["interface"]:
237.2.100 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
2511
        service.interface = (if_nametoindex
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2512
                             (str(server_settings["interface"])))
237.7.6 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2513
    
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
2514
    global multiprocessing_manager
2515
    multiprocessing_manager = multiprocessing.Manager()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2516
    
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
2517
    client_class = Client
2518
    if use_dbus:
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
2519
        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
2520
    
237.12.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2521
    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
2522
    old_client_settings = {}
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2523
    clients_data = {}
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2524
    
237.12.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
2525
    # 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
2526
    if server_settings["restore"]:
2527
        try:
2528
            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.
2529
                clients_data, old_client_settings = (pickle.load
2530
                                                     (stored_state))
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2531
            os.remove(stored_state_path)
2532
        except IOError as e:
237.7.122 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2533
            if e.errno == errno.ENOENT:
2534
                logger.warning("Could not load persistent state: {0}"
2535
                                .format(os.strerror(e.errno)))
2536
            else:
2537
                logger.critical("Could not load persistent state:",
2538
                                exc_info=e)
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2539
                raise
237.11.23 by Teddy Hogeborn
* mandos (main): Handle EOFError when reading state file.
2540
        except EOFError as e:
2541
            logger.warning("Could not load persistent state: "
237.7.122 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2542
                           "EOFError:", exc_info=e)
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2543
    
237.12.9 by Björn Påhlsson
renamed variables
2544
    with PGPEngine() as pgp:
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2545
        for client_name, client in clients_data.iteritems():
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2546
            # Decide which value to use after restoring saved state.
2547
            # We have three different values: Old config file,
2548
            # new config file, and saved state.
2549
            # New config value takes precedence if it differs from old
2550
            # config value, otherwise use saved state.
2551
            for name, value in client_settings[client_name].items():
2552
                try:
2553
                    # For each value in new config, check if it
2554
                    # differs from the old config value (Except for
2555
                    # the "secret" attribute)
2556
                    if (name != "secret" and
2557
                        value != old_client_settings[client_name]
2558
                        [name]):
237.14.10 by Teddy Hogeborn
* mandos (main): Bug fix: Syntax fix when restoring settings.
2559
                        client[name] = value
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2560
                except KeyError:
2561
                    pass
2562
            
2563
            # 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.
2564
            # enabled if its last checker was successful.  Clients
237.7.100 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2565
            # whose checker succeeded before we stored its state is
2566
            # assumed to have successfully run all checkers during
2567
            # downtime.
237.14.8 by Teddy Hogeborn
* mandos (ClientDBus.Host_dbus_property,
2568
            if client["enabled"]:
237.12.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2569
                if datetime.datetime.utcnow() >= client["expires"]:
2570
                    if not client["last_checked_ok"]:
2571
                        logger.warning(
2572
                            "disabling client {0} - Client never "
237.7.100 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2573
                            "performed a successful checker"
2574
                            .format(client_name))
237.12.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2575
                        client["enabled"] = False
2576
                    elif client["last_checker_status"] != 0:
2577
                        logger.warning(
2578
                            "disabling client {0} - Client "
2579
                            "last checker failed with error code {1}"
237.7.100 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2580
                            .format(client_name,
237.12.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2581
                                    client["last_checker_status"]))
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2582
                        client["enabled"] = False
2583
                    else:
2584
                        client["expires"] = (datetime.datetime
2585
                                             .utcnow()
2586
                                             + client["timeout"])
237.7.90 by Björn Påhlsson
fixed bug with bad stored config state for expires and last_checked_ok.
2587
                        logger.debug("Last checker succeeded,"
2588
                                     " keeping {0} enabled"
237.7.100 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2589
                                     .format(client_name))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2590
            try:
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2591
                client["secret"] = (
2592
                    pgp.decrypt(client["encrypted_secret"],
237.12.9 by Björn Påhlsson
renamed variables
2593
                                client_settings[client_name]
2594
                                ["secret"]))
2595
            except PGPError:
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2596
                # 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
2597
                logger.debug("Failed to decrypt {0} old secret"
2598
                             .format(client_name))
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2599
                client["secret"] = (
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2600
                    client_settings[client_name]["secret"])
2601
    
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2602
    # Add/remove clients based on new changes made to config
237.7.91 by Teddy Hogeborn
* mandos: Break some long lines.
2603
    for client_name in (set(old_client_settings)
2604
                        - set(client_settings)):
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2605
        del clients_data[client_name]
237.7.91 by Teddy Hogeborn
* mandos: Break some long lines.
2606
    for client_name in (set(client_settings)
2607
                        - set(old_client_settings)):
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2608
        clients_data[client_name] = client_settings[client_name]
237.7.121 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2609
    
237.7.100 by teddy at recompile
* mandos: Consistent terminology; use the term "secret" for the
2610
    # Create all client objects
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2611
    for client_name, client in clients_data.iteritems():
2612
        tcp_server.clients[client_name] = client_class(
2613
            name = client_name, settings = client)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2614
    
237.2.104 by Teddy Hogeborn
Code cleanup and one bug fix.
2615
    if not tcp_server.clients:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2616
        logger.warning("No clients defined")
237.7.121 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2617
    
237.7.154 by Teddy Hogeborn
* mandos: New "--foreground" option.
2618
    if not foreground:
2619
        if pidfile is not None:
2620
            try:
2621
                with pidfile:
2622
                    pid = os.getpid()
2623
                    pidfile.write(str(pid) + "\n".encode("utf-8"))
2624
            except IOError:
2625
                logger.error("Could not write to file %r with PID %d",
2626
                             pidfilename, pid)
2627
        del pidfile
237.2.202 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
2628
        del pidfilename
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2629
    
28 by Teddy Hogeborn
* server.conf: New file.
2630
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2631
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2632
    
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2633
    if use_dbus:
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
2634
        @alternate_dbus_interfaces({"se.recompile.Mandos":
2635
                                        "se.bsnet.fukt.Mandos"})
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
2636
        class MandosDBusService(DBusObjectWithProperties):
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2637
            """A D-Bus proxy object"""
2638
            def __init__(self):
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2639
                dbus.service.Object.__init__(self, bus, "/")
237.11.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
2640
            _interface = "se.recompile.Mandos"
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2641
            
237.18.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
2642
            @dbus_interface_annotations(_interface)
2643
            def _foo(self):
2644
                return { "org.freedesktop.DBus.Property"
2645
                         ".EmitsChangedSignal":
2646
                             "false"}
2647
            
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2648
            @dbus.service.signal(_interface, signature="o")
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2649
            def ClientAdded(self, objpath):
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2650
                "D-Bus signal"
2651
                pass
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2652
            
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2653
            @dbus.service.signal(_interface, signature="ss")
237.2.172 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
2654
            def ClientNotFound(self, fingerprint, address):
237.2.90 by Teddy Hogeborn
Merge from pipe IPC branch.
2655
                "D-Bus signal"
2656
                pass
2657
            
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2658
            @dbus.service.signal(_interface, signature="os")
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2659
            def ClientRemoved(self, objpath, name):
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2660
                "D-Bus signal"
2661
                pass
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2662
            
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2663
            @dbus.service.method(_interface, out_signature="ao")
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2664
            def GetAllClients(self):
237.2.46 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
2665
                "D-Bus method"
237.2.104 by Teddy Hogeborn
Code cleanup and one bug fix.
2666
                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
2667
                                  for c in
2668
                                  tcp_server.clients.itervalues())
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2669
            
237.2.96 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2670
            @dbus.service.method(_interface,
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2671
                                 out_signature="a{oa{sv}}")
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2672
            def GetAllClientsWithProperties(self):
237.2.46 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
2673
                "D-Bus method"
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2674
                return dbus.Dictionary(
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2675
                    ((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
2676
                     for c in tcp_server.clients.itervalues()),
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2677
                    signature="oa{sv}")
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2678
            
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2679
            @dbus.service.method(_interface, in_signature="o")
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2680
            def RemoveClient(self, object_path):
237.2.46 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
2681
                "D-Bus method"
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2682
                for c in tcp_server.clients.itervalues():
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2683
                    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
2684
                        del tcp_server.clients[c.name]
237.2.91 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
2685
                        c.remove_from_connection()
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2686
                        # Don't signal anything except ClientRemoved
237.2.165 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2687
                        c.disable(quiet=True)
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2688
                        # Emit D-Bus signal
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2689
                        self.ClientRemoved(object_path, c.name)
2690
                        return
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2691
                raise KeyError(object_path)
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2692
            
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2693
            del _interface
237.2.41 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
2694
        
237.7.123 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
2695
        mandos_dbus_service = MandosDBusService()
237.2.1 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2696
    
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2697
    def cleanup():
2698
        "Cleanup function; run on exit"
2699
        service.cleanup()
2700
        
237.11.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2701
        multiprocessing.active_children()
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2702
        if not (tcp_server.clients or client_settings):
2703
            return
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2704
        
237.7.69 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-crypto".
2705
        # Store client before exiting. Secrets are encrypted with key
2706
        # based on what config file has. If config file is
2707
        # 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
2708
        clients = {}
237.12.9 by Björn Påhlsson
renamed variables
2709
        with PGPEngine() as pgp:
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2710
            for client in tcp_server.clients.itervalues():
2711
                key = client_settings[client.name]["secret"]
237.12.9 by Björn Påhlsson
renamed variables
2712
                client.encrypted_secret = pgp.encrypt(client.secret,
2713
                                                      key)
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2714
                client_dict = {}
2715
                
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2716
                # A list of attributes that can not be pickled
2717
                # + secret.
237.12.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
2718
                exclude = set(("bus", "changedstate", "secret",
2719
                               "checker"))
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2720
                for name, typ in (inspect.getmembers
2721
                                  (dbus.service.Object)):
2722
                    exclude.add(name)
2723
                
2724
                client_dict["encrypted_secret"] = (client
2725
                                                   .encrypted_secret)
2726
                for attr in client.client_structure:
2727
                    if attr not in exclude:
2728
                        client_dict[attr] = getattr(client, attr)
2729
                
237.12.15 by Björn Påhlsson
more refactoring in regards to how clients get created
2730
                clients[client.name] = client_dict
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2731
                del client_settings[client.name]["secret"]
2732
        
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2733
        try:
237.7.126 by Teddy Hogeborn
* mandos (main.cleanup): Use tempfile.NamedTemporaryFile() instead of
2734
            with (tempfile.NamedTemporaryFile
2735
                  (mode='wb', suffix=".pickle", prefix='clients-',
2736
                   dir=os.path.dirname(stored_state_path),
2737
                   delete=False)) as stored_state:
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2738
                pickle.dump((clients, client_settings), stored_state)
237.7.126 by Teddy Hogeborn
* mandos (main.cleanup): Use tempfile.NamedTemporaryFile() instead of
2739
                tempname=stored_state.name
237.11.26 by teddy at bsnet
* mandos (main/cleanup): Write new file, then rename. Use
2740
            os.rename(tempname, stored_state_path)
237.14.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
2741
        except (IOError, OSError) as e:
237.7.78 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2742
            if not debug:
2743
                try:
2744
                    os.remove(tempname)
2745
                except NameError:
2746
                    pass
237.7.122 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2747
            if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST):
2748
                logger.warning("Could not save persistent state: {0}"
2749
                               .format(os.strerror(e.errno)))
2750
            else:
2751
                logger.warning("Could not save persistent state:",
2752
                               exc_info=e)
237.7.78 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2753
                raise e
237.12.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2754
        
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2755
        # Delete all clients, and settings from config
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2756
        while tcp_server.clients:
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2757
            name, client = tcp_server.clients.popitem()
237.2.165 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2758
            if use_dbus:
2759
                client.remove_from_connection()
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2760
            # Don't signal anything except ClientRemoved
237.2.165 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2761
            client.disable(quiet=True)
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2762
            if use_dbus:
2763
                # Emit D-Bus signal
237.11.3 by Teddy Hogeborn
* mandos: Break long lines.
2764
                mandos_dbus_service.ClientRemoved(client
2765
                                                  .dbus_object_path,
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2766
                                                  client.name)
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2767
        client_settings.clear()
237.2.163 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
2768
    
2769
    atexit.register(cleanup)
2770
    
237.12.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2771
    for client in tcp_server.clients.itervalues():
237.2.6 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2772
        if use_dbus:
2773
            # Emit D-Bus signal
237.2.174 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2774
            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
2775
        # Need to initiate checking of clients
2776
        if client.enabled:
2777
            client.init_checker()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2778
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2779
    tcp_server.enable()
2780
    tcp_server.server_activate()
2781
    
28 by Teddy Hogeborn
* server.conf: New file.
2782
    # Find out what port we got
2783
    service.port = tcp_server.socket.getsockname()[1]
237.2.77 by Teddy Hogeborn
Support not using IPv6 in server:
2784
    if use_ipv6:
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2785
        logger.info("Now listening on address %r, port %d,"
237.7.116 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2786
                    " flowinfo %d, scope_id %d",
2787
                    *tcp_server.socket.getsockname())
237.2.77 by Teddy Hogeborn
Support not using IPv6 in server:
2788
    else:                       # IPv4
237.7.116 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2789
        logger.info("Now listening on address %r, port %d",
2790
                    *tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
2791
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
2792
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
2793
    
2794
    try:
2795
        # From the Avahi example code
2796
        try:
237.2.99 by Teddy Hogeborn
Code cleanup.
2797
            service.activate()
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2798
        except dbus.exceptions.DBusException as error:
237.7.113 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
2799
            logger.critical("D-Bus Exception", exc_info=error)
237.2.164 by Teddy Hogeborn
* README (FAQ): Fix typo.
2800
            cleanup()
28 by Teddy Hogeborn
* server.conf: New file.
2801
            sys.exit(1)
2802
        # End of Avahi example code
2803
        
2804
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
2805
                             lambda *args, **kwargs:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
2806
                             (tcp_server.handle_request
2807
                              (*args[2:], **kwargs) or True))
28 by Teddy Hogeborn
* server.conf: New file.
2808
        
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2809
        logger.debug("Starting main loop")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2810
        main_loop.run()
237.7.30 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2811
    except AvahiError as error:
237.7.113 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
2812
        logger.critical("Avahi Error", exc_info=error)
237.2.164 by Teddy Hogeborn
* README (FAQ): Fix typo.
2813
        cleanup()
28 by Teddy Hogeborn
* server.conf: New file.
2814
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2815
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2816
        if debug:
237.8.5 by teddy at bsnet
* mandos: Use unicode string literals.
2817
            print("", file=sys.stderr)
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2818
        logger.debug("Server received KeyboardInterrupt")
2819
    logger.debug("Server exiting")
237.2.164 by Teddy Hogeborn
* README (FAQ): Fix typo.
2820
    # Must run before the D-Bus bus name gets deregistered
2821
    cleanup()
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2822
237.8.4 by teddy at bsnet
* mandos: Use unicode string literals.
2823
if __name__ == '__main__':
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2824
    main()