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