/mandos/trunk

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