/mandos/trunk

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