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