/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
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
91
if sys.version_info.major == 2:
92
    str = unicode
93
237.4.53 by Teddy Hogeborn
* Makefile (version): Changed to "1.6.8".
94
version = "1.6.8"
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
95
stored_state_file = "clients.pickle"
13 by Björn Påhlsson
Added following support:
96
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
97
logger = logging.getLogger()
655 by Teddy Hogeborn
Allow self-tests to run without needing /dev/log.
98
syslogger = None
13 by Björn Påhlsson
Added following support:
99
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
100
try:
101
    if_nametoindex = (ctypes.cdll.LoadLibrary
102
                      (ctypes.util.find_library("c"))
103
                      .if_nametoindex)
104
except (OSError, AttributeError):
105
    def if_nametoindex(interface):
106
        "Get an interface index the hard way, i.e. using fcntl()"
107
        SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
108
        with contextlib.closing(socket.socket()) as s:
109
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
110
                                struct.pack(b"16s16x", interface))
111
        interface_index = struct.unpack("I", ifreq[16:20])[0]
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
112
        return interface_index
113
114
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
115
def initlogger(debug, level=logging.WARNING):
518.1.4 by Björn Påhlsson
restructured logger
116
    """init logger and add loglevel"""
117
    
698 by Teddy Hogeborn
Fix mandos server "--servicename" option, broken since 1.6.4.
118
    global syslogger
655 by Teddy Hogeborn
Allow self-tests to run without needing /dev/log.
119
    syslogger = (logging.handlers.SysLogHandler
120
                 (facility =
121
                  logging.handlers.SysLogHandler.LOG_DAEMON,
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
122
                  address = "/dev/log"))
518.1.4 by Björn Påhlsson
restructured logger
123
    syslogger.setFormatter(logging.Formatter
124
                           ('Mandos [%(process)d]: %(levelname)s:'
125
                            ' %(message)s'))
126
    logger.addHandler(syslogger)
127
    
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
128
    if debug:
129
        console = logging.StreamHandler()
130
        console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
131
                                               ' [%(process)d]:'
132
                                               ' %(levelname)s:'
133
                                               ' %(message)s'))
134
        logger.addHandler(console)
518.1.4 by Björn Påhlsson
restructured logger
135
    logger.setLevel(level)
28 by Teddy Hogeborn
* server.conf: New file.
136
505.1.20 by Teddy Hogeborn
* Makefile (run-server): Remove obsolete warning.
137
518.1.9 by Björn Påhlsson
renamed variables
138
class PGPError(Exception):
139
    """Exception if encryption/decryption fails"""
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
140
    pass
141
142
518.1.9 by Björn Påhlsson
renamed variables
143
class PGPEngine(object):
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
144
    """A simple class for OpenPGP symmetric encryption & decryption"""
145
    def __init__(self):
146
        self.tempdir = tempfile.mkdtemp(prefix="mandos-")
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
147
        self.gnupgargs = ['--batch',
148
                          '--home', self.tempdir,
149
                          '--force-mdc',
150
                          '--quiet',
151
                          '--no-use-agent']
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
152
    
153
    def __enter__(self):
154
        return self
155
    
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
156
    def __exit__(self, exc_type, exc_value, traceback):
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
157
        self._cleanup()
158
        return False
159
    
160
    def __del__(self):
161
        self._cleanup()
162
    
163
    def _cleanup(self):
164
        if self.tempdir is not None:
165
            # Delete contents of tempdir
166
            for root, dirs, files in os.walk(self.tempdir,
167
                                             topdown = False):
168
                for filename in files:
169
                    os.remove(os.path.join(root, filename))
170
                for dirname in dirs:
171
                    os.rmdir(os.path.join(root, dirname))
172
            # Remove tempdir
173
            os.rmdir(self.tempdir)
174
            self.tempdir = None
175
    
176
    def password_encode(self, password):
177
        # Passphrase can not be empty and can not contain newlines or
178
        # NUL bytes.  So we prefix it and hex encode it.
630 by Teddy Hogeborn
* mandos (PGPEngine.password_encode): Bug fix: GnuPG can't handle
179
        encoded = b"mandos" + binascii.hexlify(password)
180
        if len(encoded) > 2048:
181
            # GnuPG can't handle long passwords, so encode differently
182
            encoded = (b"mandos" + password.replace(b"\\", b"\\\\")
183
                       .replace(b"\n", b"\\n")
184
                       .replace(b"\0", b"\\x00"))
185
        return encoded
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
186
    
187
    def encrypt(self, data, password):
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
188
        passphrase = self.password_encode(password)
189
        with tempfile.NamedTemporaryFile(dir=self.tempdir
190
                                         ) as passfile:
191
            passfile.write(passphrase)
192
            passfile.flush()
193
            proc = subprocess.Popen(['gpg', '--symmetric',
194
                                     '--passphrase-file',
195
                                     passfile.name]
196
                                    + self.gnupgargs,
197
                                    stdin = subprocess.PIPE,
198
                                    stdout = subprocess.PIPE,
199
                                    stderr = subprocess.PIPE)
200
            ciphertext, err = proc.communicate(input = data)
201
        if proc.returncode != 0:
202
            raise PGPError(err)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
203
        return ciphertext
204
    
205
    def decrypt(self, data, password):
615 by Teddy Hogeborn
* mandos: Don't use the GnuPGInterface module.
206
        passphrase = self.password_encode(password)
207
        with tempfile.NamedTemporaryFile(dir = self.tempdir
208
                                         ) as passfile:
209
            passfile.write(passphrase)
210
            passfile.flush()
211
            proc = subprocess.Popen(['gpg', '--decrypt',
212
                                     '--passphrase-file',
213
                                     passfile.name]
214
                                    + self.gnupgargs,
215
                                    stdin = subprocess.PIPE,
216
                                    stdout = subprocess.PIPE,
217
                                    stderr = subprocess.PIPE)
218
            decrypted_plaintext, err = proc.communicate(input
219
                                                        = data)
220
        if proc.returncode != 0:
221
            raise PGPError(err)
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
222
        return decrypted_plaintext
223
224
28 by Teddy Hogeborn
* server.conf: New file.
225
class AvahiError(Exception):
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
226
    def __init__(self, value, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
227
        self.value = value
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
228
        return super(AvahiError, self).__init__(value, *args,
229
                                                **kwargs)
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")
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
285
        self.name = str(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",
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
340
                            str(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:
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
691
                                  re.escape(str(getattr(self, attr)))
723.1.3 by Teddy Hogeborn
Use dictionary comprehensions.
692
                              for attr in self.runtime_expansions }
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
693
            try:
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
694
                command = self.checker_command % escaped_attrs
695
            except TypeError as error:
696
                logger.error('Could not format string "%s"',
697
                             self.checker_command, exc_info=error)
698
                return True # Try again later
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
699
            self.current_checker_command = command
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
700
            try:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
701
                logger.info("Starting checker %r for %s",
44 by Teddy Hogeborn
* ca.pem: Removed.
702
                            command, self.name)
94 by Teddy Hogeborn
* clients.conf ([DEFAULT]/checker): Update to new default value.
703
                # We don't need to redirect stdout and stderr, since
704
                # in normal mode, that is already done by daemon(),
705
                # and in debug mode we don't want to.  (Stdin is
706
                # always replaced by /dev/null.)
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
707
                # The exception is when not debugging but nevertheless
708
                # running in the foreground; use the previously
709
                # created wnull.
710
                popen_args = {}
711
                if (not self.server_settings["debug"]
712
                    and self.server_settings["foreground"]):
713
                    popen_args.update({"stdout": wnull,
714
                                       "stderr": wnull })
28 by Teddy Hogeborn
* server.conf: New file.
715
                self.checker = subprocess.Popen(command,
716
                                                close_fds=True,
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
717
                                                shell=True, cwd="/",
718
                                                **popen_args)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
719
            except OSError as error:
565 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
720
                logger.error("Failed to start subprocess",
721
                             exc_info=error)
610 by Teddy Hogeborn
* mandos (Client.start_checker): Return True (retry later) if starting
722
                return True
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
723
            self.checker_callback_tag = (gobject.child_watch_add
724
                                         (self.checker.pid,
725
                                          self.checker_callback,
726
                                          data=command))
727
            # The checker may have completed before the gobject
728
            # watch was added.  Check for this.
610 by Teddy Hogeborn
* mandos (Client.start_checker): Return True (retry later) if starting
729
            try:
730
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
731
            except OSError as error:
732
                if error.errno == errno.ECHILD:
611 by Teddy Hogeborn
* mandos (Client.start_checker): Add comment. Break long line.
733
                    # This should never happen
734
                    logger.error("Child process vanished",
735
                                 exc_info=error)
610 by Teddy Hogeborn
* mandos (Client.start_checker): Return True (retry later) if starting
736
                    return True
737
                raise
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
738
            if pid:
739
                gobject.source_remove(self.checker_callback_tag)
740
                self.checker_callback(pid, status, command)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
741
        # Re-run this periodically if run by gobject.timeout_add
742
        return True
237.1.1 by Teddy Hogeborn
First steps of a D-Bus interface to the server.
743
    
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
744
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
745
        """Force the checker process, if any, to stop."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
746
        if self.checker_callback_tag:
747
            gobject.source_remove(self.checker_callback_tag)
748
            self.checker_callback_tag = None
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
749
        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.
750
            return
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
751
        logger.debug("Stopping checker for %(name)s", vars(self))
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
752
        try:
564 by Teddy Hogeborn
* mandos (Client.stop_checker): Use new Popen.terminate() method.
753
            self.checker.terminate()
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
754
            #time.sleep(0.5)
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
755
            #if self.checker.poll() is None:
564 by Teddy Hogeborn
* mandos (Client.stop_checker): Use new Popen.terminate() method.
756
            #    self.checker.kill()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
757
        except OSError as error:
28 by Teddy Hogeborn
* server.conf: New file.
758
            if error.errno != errno.ESRCH: # No such process
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
759
                raise
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
760
        self.checker = None
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
761
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
762
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
763
def dbus_service_property(dbus_interface, signature="v",
764
                          access="readwrite", byte_arrays=False):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
765
    """Decorators for marking methods of a DBusObjectWithProperties to
766
    become properties on the D-Bus.
767
    
768
    The decorated method will be called with no arguments by "Get"
769
    and with one argument by "Set".
770
    
771
    The parameters, where they are supported, are the same as
772
    dbus.service.method, except there is only "signature", since the
773
    type from Get() and the type sent to Set() is the same.
774
    """
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
775
    # Encoding deeply encoded byte arrays is not supported yet by the
776
    # "Set" method, so we fail early here:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
777
    if byte_arrays and signature != "ay":
778
        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.
779
                         " signature {!r}".format(signature))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
780
    def decorator(func):
781
        func._dbus_is_property = True
782
        func._dbus_interface = dbus_interface
783
        func._dbus_signature = signature
784
        func._dbus_access = access
785
        func._dbus_name = func.__name__
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
786
        if func._dbus_name.endswith("_dbus_property"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
787
            func._dbus_name = func._dbus_name[:-14]
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
788
        func._dbus_get_args_options = {'byte_arrays': byte_arrays }
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
789
        return func
790
    return decorator
791
792
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
793
def dbus_interface_annotations(dbus_interface):
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
794
    """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
795
    
796
    Usage:
797
    
798
    @dbus_interface_annotations("org.example.Interface")
799
    def _foo(self):  # Function name does not matter
800
        return {"org.freedesktop.DBus.Deprecated": "true",
801
                "org.freedesktop.DBus.Property.EmitsChangedSignal":
802
                    "false"}
803
    """
804
    def decorator(func):
805
        func._dbus_is_interface = True
806
        func._dbus_interface = dbus_interface
807
        func._dbus_name = dbus_interface
808
        return func
809
    return decorator
810
811
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
812
def dbus_annotations(annotations):
813
    """Decorator to annotate D-Bus methods, signals or properties
814
    Usage:
815
    
816
    @dbus_service_property("org.example.Interface", signature="b",
817
                           access="r")
818
    @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
819
                        "org.freedesktop.DBus.Property."
820
                        "EmitsChangedSignal": "false"})
821
    def Property_dbus_property(self):
822
        return dbus.Boolean(False)
823
    """
824
    def decorator(func):
825
        func._dbus_annotations = annotations
826
        return func
827
    return decorator
828
829
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
830
class DBusPropertyException(dbus.exceptions.DBusException):
831
    """A base class for D-Bus property-related exceptions
832
    """
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
833
    pass
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
834
835
class DBusPropertyAccessException(DBusPropertyException):
836
    """A property's access permissions disallows an operation.
837
    """
838
    pass
839
840
841
class DBusPropertyNotFound(DBusPropertyException):
842
    """An attempt was made to access a non-existing property.
843
    """
844
    pass
845
846
847
class DBusObjectWithProperties(dbus.service.Object):
848
    """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
849
    
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
850
    Classes inheriting from this can use the dbus_service_property
851
    decorator to expose methods as D-Bus properties.  It exposes the
852
    standard Get(), Set(), and GetAll() methods on the D-Bus.
853
    """
854
    
855
    @staticmethod
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
856
    def _is_dbus_thing(thing):
857
        """Returns a function testing if an attribute is a D-Bus thing
858
        
859
        If called like _is_dbus_thing("method") it returns a function
860
        suitable for use as predicate to inspect.getmembers().
861
        """
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
862
        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
863
                                   False)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
864
    
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
865
    def _get_all_dbus_things(self, thing):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
866
        """Returns a generator of (name, attribute) pairs
867
        """
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
868
        return ((getattr(athing.__get__(self), "_dbus_name",
869
                         name),
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
870
                 athing.__get__(self))
24.1.186 by Björn Påhlsson
transitional stuff actually working
871
                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
872
                for name, athing in
873
                inspect.getmembers(cls,
874
                                   self._is_dbus_thing(thing)))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
875
    
876
    def _get_dbus_property(self, interface_name, property_name):
877
        """Returns a bound method if one exists which is a D-Bus
878
        property with the specified name and interface.
879
        """
24.1.186 by Björn Påhlsson
transitional stuff actually working
880
        for cls in  self.__class__.__mro__:
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
881
            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
882
                                (cls,
883
                                 self._is_dbus_thing("property"))):
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
884
                if (value._dbus_name == property_name
885
                    and value._dbus_interface == interface_name):
24.1.186 by Björn Påhlsson
transitional stuff actually working
886
                    return value.__get__(self)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
887
        
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
888
        # No such property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
889
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
890
                                   + interface_name + "."
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
891
                                   + property_name)
892
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
893
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
894
                         out_signature="v")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
895
    def Get(self, interface_name, property_name):
896
        """Standard D-Bus property Get() method, see D-Bus standard.
897
        """
898
        prop = self._get_dbus_property(interface_name, property_name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
899
        if prop._dbus_access == "write":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
900
            raise DBusPropertyAccessException(property_name)
901
        value = prop()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
902
        if not hasattr(value, "variant_level"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
903
            return value
904
        return type(value)(value, variant_level=value.variant_level+1)
905
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
906
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
907
    def Set(self, interface_name, property_name, value):
908
        """Standard D-Bus property Set() method, see D-Bus standard.
909
        """
910
        prop = self._get_dbus_property(interface_name, property_name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
911
        if prop._dbus_access == "read":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
912
            raise DBusPropertyAccessException(property_name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
913
        if prop._dbus_get_args_options["byte_arrays"]:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
914
            # The byte_arrays option is not supported yet on
915
            # signatures other than "ay".
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
916
            if prop._dbus_signature != "ay":
649 by Teddy Hogeborn
Minor code fix; raise only exception instances, not classes.
917
                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.
918
                                 "'ay' signature {!r}"
649 by Teddy Hogeborn
Minor code fix; raise only exception instances, not classes.
919
                                 .format(prop._dbus_signature))
546 by Teddy Hogeborn
* debian/rules (binary-common): Exclude network-hooks.d from
920
            value = dbus.ByteArray(b''.join(chr(byte)
921
                                            for byte in value))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
922
        prop(value)
923
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
924
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
925
                         out_signature="a{sv}")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
926
    def GetAll(self, interface_name):
927
        """Standard D-Bus property GetAll() method, see D-Bus
928
        standard.
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
929
        
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
930
        Note: Will not include properties with access="write".
931
        """
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
932
        properties = {}
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
933
        for name, prop in self._get_all_dbus_things("property"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
934
            if (interface_name
935
                and interface_name != prop._dbus_interface):
936
                # Interface non-empty but did not match
937
                continue
938
            # Ignore write-only properties
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
939
            if prop._dbus_access == "write":
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
940
                continue
941
            value = prop()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
942
            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
943
                properties[name] = value
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
944
                continue
518.1.5 by Björn Påhlsson
First run of python-lint. Fixed some *obviously* bad code and turned
945
            properties[name] = type(value)(value, variant_level=
946
                                           value.variant_level+1)
947
        return dbus.Dictionary(properties, signature="sv")
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
948
    
949
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
950
                         out_signature="s",
951
                         path_keyword='object_path',
952
                         connection_keyword='connection')
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
953
    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
954
        """Overloading of standard D-Bus method.
955
        
956
        Inserts property tags and interface annotation tags.
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
957
        """
958
        xmlstring = dbus.service.Object.Introspect(self, object_path,
386 by Teddy Hogeborn
* mandos (DBusObjectWithProperties.Introspect): Add the name
959
                                                   connection)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
960
        try:
961
            document = xml.dom.minidom.parseString(xmlstring)
962
            def make_tag(document, name, prop):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
963
                e = document.createElement("property")
964
                e.setAttribute("name", name)
965
                e.setAttribute("type", prop._dbus_signature)
966
                e.setAttribute("access", prop._dbus_access)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
967
                return e
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
968
            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
969
                # Add property tags
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
970
                for tag in (make_tag(document, name, prop)
971
                            for name, prop
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
972
                            in self._get_all_dbus_things("property")
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
973
                            if prop._dbus_interface
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
974
                            == if_tag.getAttribute("name")):
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
975
                    if_tag.appendChild(tag)
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
976
                # Add annotation tags
977
                for typ in ("method", "signal", "property"):
978
                    for tag in if_tag.getElementsByTagName(typ):
979
                        annots = dict()
980
                        for name, prop in (self.
981
                                           _get_all_dbus_things(typ)):
982
                            if (name == tag.getAttribute("name")
983
                                and prop._dbus_interface
984
                                == if_tag.getAttribute("name")):
985
                                annots.update(getattr
986
                                              (prop,
987
                                               "_dbus_annotations",
988
                                               {}))
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
989
                        for name, value in annots.items():
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
990
                            ann_tag = document.createElement(
991
                                "annotation")
992
                            ann_tag.setAttribute("name", name)
993
                            ann_tag.setAttribute("value", value)
994
                            tag.appendChild(ann_tag)
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
995
                # Add interface annotation tags
996
                for annotation, value in dict(
576 by Teddy Hogeborn
* mandos (DBusObjectWithProperties.Introspect): Use
997
                    itertools.chain.from_iterable(
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
998
                        annotations().items()
576 by Teddy Hogeborn
* mandos (DBusObjectWithProperties.Introspect): Use
999
                        for name, annotations in
1000
                        self._get_all_dbus_things("interface")
1001
                        if name == if_tag.getAttribute("name")
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
1002
                        )).items():
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1003
                    ann_tag = document.createElement("annotation")
1004
                    ann_tag.setAttribute("name", annotation)
1005
                    ann_tag.setAttribute("value", value)
1006
                    if_tag.appendChild(ann_tag)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1007
                # Add the names to the return values for the
1008
                # "org.freedesktop.DBus.Properties" methods
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1009
                if (if_tag.getAttribute("name")
1010
                    == "org.freedesktop.DBus.Properties"):
1011
                    for cn in if_tag.getElementsByTagName("method"):
1012
                        if cn.getAttribute("name") == "Get":
1013
                            for arg in cn.getElementsByTagName("arg"):
1014
                                if (arg.getAttribute("direction")
1015
                                    == "out"):
1016
                                    arg.setAttribute("name", "value")
1017
                        elif cn.getAttribute("name") == "GetAll":
1018
                            for arg in cn.getElementsByTagName("arg"):
1019
                                if (arg.getAttribute("direction")
1020
                                    == "out"):
1021
                                    arg.setAttribute("name", "props")
1022
            xmlstring = document.toxml("utf-8")
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1023
            document.unlink()
1024
        except (AttributeError, xml.dom.DOMException,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1025
                xml.parsers.expat.ExpatError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1026
            logger.error("Failed to override Introspection method",
565 by Teddy Hogeborn
* mandos (AvahiService.rename, Client.start_checker,
1027
                         exc_info=error)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1028
        return xmlstring
1029
1030
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
1031
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
1032
    """Convert a UTC datetime.datetime() to a D-Bus type."""
1033
    if dt is None:
1034
        return dbus.String("", variant_level = variant_level)
1035
    return dbus.String(dt.isoformat(),
1036
                       variant_level=variant_level)
1037
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1038
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1039
def alternate_dbus_interfaces(alt_interface_names, deprecate=True):
1040
    """A class decorator; applied to a subclass of
1041
    dbus.service.Object, it will add alternate D-Bus attributes with
1042
    interface names according to the "alt_interface_names" mapping.
1043
    Usage:
1044
    
587 by Teddy Hogeborn
* Makefile (DOCBOOKTOMAN): Only run man --warnings if both "man" and
1045
    @alternate_dbus_interfaces({"org.example.Interface":
1046
                                    "net.example.AlternateInterface"})
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1047
    class SampleDBusObject(dbus.service.Object):
1048
        @dbus.service.method("org.example.Interface")
1049
        def SampleDBusMethod():
1050
            pass
1051
    
1052
    The above "SampleDBusMethod" on "SampleDBusObject" will be
1053
    reachable via two interfaces: "org.example.Interface" and
1054
    "net.example.AlternateInterface", the latter of which will have
1055
    its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to
1056
    "true", unless "deprecate" is passed with a False value.
1057
    
1058
    This works for methods and signals, and also for D-Bus properties
1059
    (from DBusObjectWithProperties) and interfaces (from the
1060
    dbus_interface_annotations decorator).
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1061
    """
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1062
    def wrapper(cls):
1063
        for orig_interface_name, alt_interface_name in (
723.1.4 by Teddy Hogeborn
Use the .items() method instead of .iteritems().
1064
            alt_interface_names.items()):
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1065
            attr = {}
1066
            interface_names = set()
1067
            # Go though all attributes of the class
1068
            for attrname, attribute in inspect.getmembers(cls):
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1069
                # Ignore non-D-Bus attributes, and D-Bus attributes
1070
                # with the wrong interface name
1071
                if (not hasattr(attribute, "_dbus_interface")
1072
                    or not attribute._dbus_interface
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1073
                    .startswith(orig_interface_name)):
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1074
                    continue
1075
                # Create an alternate D-Bus interface name based on
1076
                # the current name
1077
                alt_interface = (attribute._dbus_interface
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1078
                                 .replace(orig_interface_name,
1079
                                          alt_interface_name))
1080
                interface_names.add(alt_interface)
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1081
                # Is this a D-Bus signal?
1082
                if getattr(attribute, "_dbus_is_signal", False):
614 by Teddy Hogeborn
* mandos: Comment changes.
1083
                    # Extract the original non-method undecorated
1084
                    # function by black magic
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1085
                    nonmethod_func = (dict(
1086
                            zip(attribute.func_code.co_freevars,
1087
                                attribute.__closure__))["func"]
1088
                                      .cell_contents)
1089
                    # Create a new, but exactly alike, function
1090
                    # object, and decorate it to be a new D-Bus signal
1091
                    # with the alternate D-Bus interface name
1092
                    new_function = (dbus.service.signal
1093
                                    (alt_interface,
1094
                                     attribute._dbus_signature)
1095
                                    (types.FunctionType(
1096
                                nonmethod_func.func_code,
1097
                                nonmethod_func.func_globals,
1098
                                nonmethod_func.func_name,
1099
                                nonmethod_func.func_defaults,
1100
                                nonmethod_func.func_closure)))
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1101
                    # Copy annotations, if any
1102
                    try:
1103
                        new_function._dbus_annotations = (
1104
                            dict(attribute._dbus_annotations))
1105
                    except AttributeError:
1106
                        pass
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1107
                    # 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
1108
                    # original and alternate functions, so both the
1109
                    # original and alternate signals gets sent when
1110
                    # the function is called
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1111
                    def fixscope(func1, func2):
1112
                        """This function is a scope container to pass
1113
                        func1 and func2 to the "call_both" function
1114
                        outside of its arguments"""
1115
                        def call_both(*args, **kwargs):
1116
                            """This function will emit two D-Bus
1117
                            signals by calling func1 and func2"""
1118
                            func1(*args, **kwargs)
1119
                            func2(*args, **kwargs)
1120
                        return call_both
1121
                    # Create the "call_both" function and add it to
1122
                    # the class
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1123
                    attr[attrname] = fixscope(attribute, new_function)
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1124
                # Is this a D-Bus method?
1125
                elif getattr(attribute, "_dbus_is_method", False):
1126
                    # Create a new, but exactly alike, function
1127
                    # object.  Decorate it to be a new D-Bus method
1128
                    # with the alternate D-Bus interface name.  Add it
1129
                    # to the class.
1130
                    attr[attrname] = (dbus.service.method
1131
                                      (alt_interface,
1132
                                       attribute._dbus_in_signature,
1133
                                       attribute._dbus_out_signature)
1134
                                      (types.FunctionType
1135
                                       (attribute.func_code,
1136
                                        attribute.func_globals,
1137
                                        attribute.func_name,
1138
                                        attribute.func_defaults,
1139
                                        attribute.func_closure)))
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1140
                    # Copy annotations, if any
1141
                    try:
1142
                        attr[attrname]._dbus_annotations = (
1143
                            dict(attribute._dbus_annotations))
1144
                    except AttributeError:
1145
                        pass
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1146
                # Is this a D-Bus property?
1147
                elif getattr(attribute, "_dbus_is_property", False):
1148
                    # Create a new, but exactly alike, function
1149
                    # object, and decorate it to be a new D-Bus
1150
                    # property with the alternate D-Bus interface
1151
                    # name.  Add it to the class.
1152
                    attr[attrname] = (dbus_service_property
1153
                                      (alt_interface,
1154
                                       attribute._dbus_signature,
1155
                                       attribute._dbus_access,
1156
                                       attribute
1157
                                       ._dbus_get_args_options
1158
                                       ["byte_arrays"])
1159
                                      (types.FunctionType
1160
                                       (attribute.func_code,
1161
                                        attribute.func_globals,
1162
                                        attribute.func_name,
1163
                                        attribute.func_defaults,
1164
                                        attribute.func_closure)))
561.1.1 by Teddy Hogeborn
Add D-Bus annotations support to methods, signals and properties.
1165
                    # Copy annotations, if any
1166
                    try:
1167
                        attr[attrname]._dbus_annotations = (
1168
                            dict(attribute._dbus_annotations))
1169
                    except AttributeError:
1170
                        pass
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1171
                # Is this a D-Bus interface?
1172
                elif getattr(attribute, "_dbus_is_interface", False):
1173
                    # Create a new, but exactly alike, function
1174
                    # object.  Decorate it to be a new D-Bus interface
1175
                    # with the alternate D-Bus interface name.  Add it
1176
                    # to the class.
1177
                    attr[attrname] = (dbus_interface_annotations
1178
                                      (alt_interface)
1179
                                      (types.FunctionType
1180
                                       (attribute.func_code,
1181
                                        attribute.func_globals,
1182
                                        attribute.func_name,
1183
                                        attribute.func_defaults,
1184
                                        attribute.func_closure)))
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1185
            if deprecate:
1186
                # Deprecate all alternate interfaces
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1187
                iname="_AlternateDBusNames_interface_annotation{}"
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1188
                for interface_name in interface_names:
1189
                    @dbus_interface_annotations(interface_name)
1190
                    def func(self):
1191
                        return { "org.freedesktop.DBus.Deprecated":
1192
                                     "true" }
1193
                    # Find an unused name
1194
                    for aname in (iname.format(i)
1195
                                  for i in itertools.count()):
1196
                        if aname not in attr:
1197
                            attr[aname] = func
1198
                            break
1199
            if interface_names:
1200
                # Replace the class with a new subclass of it with
1201
                # methods, signals, etc. as created above.
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1202
                cls = type(b"{}Alternate".format(cls.__name__),
575 by Teddy Hogeborn
* mandos: Use a class decorator instead of a metaclass to provide
1203
                           (cls,), attr)
1204
        return cls
1205
    return wrapper
1206
1207
1208
@alternate_dbus_interfaces({"se.recompile.Mandos":
1209
                                "se.bsnet.fukt.Mandos"})
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1210
class ClientDBus(Client, DBusObjectWithProperties):
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1211
    """A Client class using D-Bus
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1212
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1213
    Attributes:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1214
    dbus_object_path: dbus.ObjectPath
1215
    bus: dbus.SystemBus()
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1216
    """
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1217
    
1218
    runtime_expansions = (Client.runtime_expansions
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1219
                          + ("dbus_object_path",))
438 by Teddy Hogeborn
* mandos (Client.runtime_expansions): New attribute containing the
1220
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1221
    # dbus.service.Object doesn't use super(), so we can't either.
1222
    
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
1223
    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
1224
        self.bus = bus
1225
        Client.__init__(self, *args, **kwargs)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1226
        # Only now, when this client is initialized, can it show up on
1227
        # the D-Bus
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1228
        client_object_name = str(self.name).translate(
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1229
            {ord("."): ord("_"),
1230
             ord("-"): ord("_")})
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1231
        self.dbus_object_path = (dbus.ObjectPath
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1232
                                 ("/clients/" + client_object_name))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1233
        DBusObjectWithProperties.__init__(self, self.bus,
1234
                                          self.dbus_object_path)
573 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1235
    
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1236
    def notifychangeproperty(transform_func,
1237
                             dbus_name, type_func=lambda x: x,
1238
                             variant_level=1):
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1239
        """ Modify a variable so that it's a property which announces
1240
        its changes to DBus.
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1241
        
505.1.15 by Teddy Hogeborn
Bug fix: Make D-Bus properties settable again.
1242
        transform_fun: Function that takes a value and a variant_level
1243
                       and transforms it to a D-Bus type.
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1244
        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
1245
        type_func: Function that transform the value before sending it
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1246
                   to the D-Bus.  Default: no transform
1247
        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
1248
        """
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
1249
        attrname = "_{}".format(dbus_name)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1250
        def setter(self, value):
1251
            if hasattr(self, "dbus_object_path"):
505.1.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1252
                if (not hasattr(self, attrname) or
1253
                    type_func(getattr(self, attrname, None))
1254
                    != type_func(value)):
1255
                    dbus_value = transform_func(type_func(value),
505.1.15 by Teddy Hogeborn
Bug fix: Make D-Bus properties settable again.
1256
                                                variant_level
1257
                                                =variant_level)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1258
                    self.PropertyChanged(dbus.String(dbus_name),
1259
                                         dbus_value)
505.1.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1260
            setattr(self, attrname, value)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1261
        
505.1.6 by Teddy Hogeborn
* mandos (ClientDBus.notifychangeproperty): Bug fix: Use instance
1262
        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
1263
    
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1264
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
1265
    approvals_pending = notifychangeproperty(dbus.Boolean,
1266
                                             "ApprovalPending",
1267
                                             type_func = bool)
1268
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1269
    last_enabled = notifychangeproperty(datetime_to_dbus,
1270
                                        "LastEnabled")
1271
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1272
                                   type_func = lambda checker:
1273
                                       checker is not None)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1274
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1275
                                           "LastCheckedOK")
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1276
    last_checker_status = notifychangeproperty(dbus.Int16,
1277
                                               "LastCheckerStatus")
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1278
    last_approval_request = notifychangeproperty(
1279
        datetime_to_dbus, "LastApprovalRequest")
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1280
    approved_by_default = notifychangeproperty(dbus.Boolean,
1281
                                               "ApprovedByDefault")
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1282
    approval_delay = notifychangeproperty(dbus.UInt64,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1283
                                          "ApprovalDelay",
1284
                                          type_func =
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1285
                                          lambda td: td.total_seconds()
1286
                                          * 1000)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1287
    approval_duration = notifychangeproperty(
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1288
        dbus.UInt64, "ApprovalDuration",
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1289
        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
1290
    host = notifychangeproperty(dbus.String, "Host")
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1291
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1292
                                   type_func = lambda td:
1293
                                       td.total_seconds() * 1000)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1294
    extended_timeout = notifychangeproperty(
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1295
        dbus.UInt64, "ExtendedTimeout",
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1296
        type_func = lambda td: td.total_seconds() * 1000)
518.2.9 by Teddy Hogeborn
* mandos (ClientDBus.approval_delay, ClientDBus.approval_duration,
1297
    interval = notifychangeproperty(dbus.UInt64,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1298
                                    "Interval",
1299
                                    type_func =
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1300
                                    lambda td: td.total_seconds()
1301
                                    * 1000)
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1302
    checker_command = notifychangeproperty(dbus.String, "Checker")
1303
    
1304
    del notifychangeproperty
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1305
    
1306
    def __del__(self, *args, **kwargs):
1307
        try:
1308
            self.remove_from_connection()
329 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
1309
        except LookupError:
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1310
            pass
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1311
        if hasattr(DBusObjectWithProperties, "__del__"):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1312
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1313
        Client.__del__(self, *args, **kwargs)
1314
    
1315
    def checker_callback(self, pid, condition, command,
1316
                         *args, **kwargs):
1317
        self.checker_callback_tag = None
1318
        self.checker = None
1319
        if os.WIFEXITED(condition):
1320
            exitstatus = os.WEXITSTATUS(condition)
1321
            # Emit D-Bus signal
1322
            self.CheckerCompleted(dbus.Int16(exitstatus),
1323
                                  dbus.Int64(condition),
1324
                                  dbus.String(command))
1325
        else:
1326
            # Emit D-Bus signal
1327
            self.CheckerCompleted(dbus.Int16(-1),
1328
                                  dbus.Int64(condition),
1329
                                  dbus.String(command))
1330
        
1331
        return Client.checker_callback(self, pid, condition, command,
1332
                                       *args, **kwargs)
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1333
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1334
    def start_checker(self, *args, **kwargs):
658 by Teddy Hogeborn
* mandos (ClientDBus.start_checker): Removed unused variable
1335
        old_checker_pid = getattr(self.checker, "pid", None)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1336
        r = Client.start_checker(self, *args, **kwargs)
329 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
1337
        # Only if new checker process was started
1338
        if (self.checker is not None
1339
            and old_checker_pid != self.checker.pid):
1340
            # Emit D-Bus signal
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1341
            self.CheckerStarted(self.current_checker_command)
1342
        return r
1343
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1344
    def _reset_approved(self):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1345
        self.approved = None
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1346
        return False
1347
    
1348
    def approve(self, value=True):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1349
        self.approved = value
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1350
        gobject.timeout_add(int(self.approval_duration.total_seconds()
1351
                                * 1000), self._reset_approved)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1352
        self.send_changedstate()
24.2.1 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-urwid".
1353
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1354
    ## D-Bus methods, signals & properties
505.1.1 by Teddy Hogeborn
* mandos (transitional_dbus_metaclass): Renamed to
1355
    _interface = "se.recompile.Mandos.Client"
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1356
    
560.1.2 by teddy at recompile
Add facilities for D-Bus interface annotations and use them to mark
1357
    ## Interfaces
1358
    
1359
    @dbus_interface_annotations(_interface)
1360
    def _foo(self):
1361
        return { "org.freedesktop.DBus.Property.EmitsChangedSignal":
1362
                     "false"}
1363
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1364
    ## Signals
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1365
    
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1366
    # CheckerCompleted - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1367
    @dbus.service.signal(_interface, signature="nxs")
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
1368
    def CheckerCompleted(self, exitcode, waitstatus, command):
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1369
        "D-Bus signal"
1370
        pass
1371
    
1372
    # CheckerStarted - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1373
    @dbus.service.signal(_interface, signature="s")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1374
    def CheckerStarted(self, command):
1375
        "D-Bus signal"
1376
        pass
1377
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1378
    # PropertyChanged - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1379
    @dbus.service.signal(_interface, signature="sv")
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1380
    def PropertyChanged(self, property, value):
1381
        "D-Bus signal"
1382
        pass
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1383
    
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1384
    # GotSecret - signal
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1385
    @dbus.service.signal(_interface)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
1386
    def GotSecret(self):
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1387
        """D-Bus signal
1388
        Is sent after a successful transfer of secret from the Mandos
1389
        server to mandos-client
1390
        """
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1391
        pass
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1392
    
1393
    # Rejected - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1394
    @dbus.service.signal(_interface, signature="s")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1395
    def Rejected(self, reason):
1396
        "D-Bus signal"
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1397
        pass
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1398
    
1399
    # NeedApproval - signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1400
    @dbus.service.signal(_interface, signature="tb")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1401
    def NeedApproval(self, timeout, default):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1402
        "D-Bus signal"
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1403
        return self.need_approval()
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1404
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1405
    ## Methods
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1406
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1407
    # Approve - method
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1408
    @dbus.service.method(_interface, in_signature="b")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1409
    def Approve(self, value):
1410
        self.approve(value)
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1411
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1412
    # CheckedOK - method
1413
    @dbus.service.method(_interface)
1414
    def CheckedOK(self):
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1415
        self.checked_ok()
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1416
    
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1417
    # Enable - method
1418
    @dbus.service.method(_interface)
1419
    def Enable(self):
1420
        "D-Bus method"
1421
        self.enable()
1422
    
1423
    # StartChecker - method
1424
    @dbus.service.method(_interface)
1425
    def StartChecker(self):
1426
        "D-Bus method"
1427
        self.start_checker()
1428
    
1429
    # Disable - method
1430
    @dbus.service.method(_interface)
1431
    def Disable(self):
1432
        "D-Bus method"
1433
        self.disable()
1434
    
1435
    # StopChecker - method
1436
    @dbus.service.method(_interface)
1437
    def StopChecker(self):
1438
        self.stop_checker()
1439
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
1440
    ## Properties
1441
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1442
    # ApprovalPending - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1443
    @dbus_service_property(_interface, signature="b", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1444
    def ApprovalPending_dbus_property(self):
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1445
        return dbus.Boolean(bool(self.approvals_pending))
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1446
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1447
    # ApprovedByDefault - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1448
    @dbus_service_property(_interface, signature="b",
1449
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1450
    def ApprovedByDefault_dbus_property(self, value=None):
1451
        if value is None:       # get
1452
            return dbus.Boolean(self.approved_by_default)
1453
        self.approved_by_default = bool(value)
1454
    
1455
    # ApprovalDelay - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1456
    @dbus_service_property(_interface, signature="t",
1457
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1458
    def ApprovalDelay_dbus_property(self, value=None):
1459
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1460
            return dbus.UInt64(self.approval_delay.total_seconds()
1461
                               * 1000)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1462
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
1463
    
1464
    # ApprovalDuration - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1465
    @dbus_service_property(_interface, signature="t",
1466
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1467
    def ApprovalDuration_dbus_property(self, value=None):
1468
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1469
            return dbus.UInt64(self.approval_duration.total_seconds()
1470
                               * 1000)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1471
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1472
    
1473
    # Name - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1474
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1475
    def Name_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1476
        return dbus.String(self.name)
1477
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1478
    # Fingerprint - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1479
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1480
    def Fingerprint_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1481
        return dbus.String(self.fingerprint)
1482
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1483
    # Host - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1484
    @dbus_service_property(_interface, signature="s",
1485
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1486
    def Host_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1487
        if value is None:       # get
1488
            return dbus.String(self.host)
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1489
        self.host = str(value)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1490
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1491
    # Created - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1492
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1493
    def Created_dbus_property(self):
518.2.3 by Teddy Hogeborn
Make "enabled" a client config option.
1494
        return datetime_to_dbus(self.created)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1495
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1496
    # LastEnabled - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1497
    @dbus_service_property(_interface, signature="s", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1498
    def LastEnabled_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1499
        return datetime_to_dbus(self.last_enabled)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1500
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1501
    # Enabled - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1502
    @dbus_service_property(_interface, signature="b",
1503
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1504
    def Enabled_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1505
        if value is None:       # get
1506
            return dbus.Boolean(self.enabled)
1507
        if value:
1508
            self.enable()
1509
        else:
1510
            self.disable()
1511
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1512
    # LastCheckedOK - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1513
    @dbus_service_property(_interface, signature="s",
1514
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1515
    def LastCheckedOK_dbus_property(self, value=None):
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1516
        if value is not None:
1517
            self.checked_ok()
1518
            return
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1519
        return datetime_to_dbus(self.last_checked_ok)
24.1.179 by Björn Påhlsson
New feature:
1520
    
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1521
    # LastCheckerStatus - property
1522
    @dbus_service_property(_interface, signature="n",
1523
                           access="read")
1524
    def LastCheckerStatus_dbus_property(self):
1525
        return dbus.Int16(self.last_checker_status)
1526
    
24.1.179 by Björn Påhlsson
New feature:
1527
    # Expires - property
1528
    @dbus_service_property(_interface, signature="s", access="read")
1529
    def Expires_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1530
        return datetime_to_dbus(self.expires)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1531
    
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1532
    # LastApprovalRequest - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1533
    @dbus_service_property(_interface, signature="s", access="read")
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1534
    def LastApprovalRequest_dbus_property(self):
24.1.182 by Björn Påhlsson
Refactoring code related to PropertyChanged dbus signal. Made sending
1535
        return datetime_to_dbus(self.last_approval_request)
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
1536
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1537
    # Timeout - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1538
    @dbus_service_property(_interface, signature="t",
1539
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1540
    def Timeout_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1541
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1542
            return dbus.UInt64(self.timeout.total_seconds() * 1000)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1543
        old_timeout = self.timeout
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1544
        self.timeout = datetime.timedelta(0, 0, 0, value)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1545
        # Reschedule disabling
543 by Teddy Hogeborn
* mandos: Break some long lines.
1546
        if self.enabled:
1547
            now = datetime.datetime.utcnow()
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1548
            self.expires += self.timeout - old_timeout
1549
            if self.expires <= now:
543 by Teddy Hogeborn
* mandos: Break some long lines.
1550
                # The timeout has passed
1551
                self.disable()
1552
            else:
1553
                if (getattr(self, "disable_initiator_tag", None)
1554
                    is None):
1555
                    return
1556
                gobject.source_remove(self.disable_initiator_tag)
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
1557
                self.disable_initiator_tag = (
1558
                    gobject.timeout_add(
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1559
                        int((self.expires - now).total_seconds()
1560
                            * 1000), self.disable))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1561
    
24.1.179 by Björn Påhlsson
New feature:
1562
    # ExtendedTimeout - property
1563
    @dbus_service_property(_interface, signature="t",
1564
                           access="readwrite")
1565
    def ExtendedTimeout_dbus_property(self, value=None):
1566
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1567
            return dbus.UInt64(self.extended_timeout.total_seconds()
1568
                               * 1000)
24.1.179 by Björn Påhlsson
New feature:
1569
        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
1570
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1571
    # Interval - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1572
    @dbus_service_property(_interface, signature="t",
1573
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1574
    def Interval_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1575
        if value is None:       # get
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1576
            return dbus.UInt64(self.interval.total_seconds() * 1000)
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1577
        self.interval = datetime.timedelta(0, 0, 0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1578
        if getattr(self, "checker_initiator_tag", None) is None:
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1579
            return
518.2.3 by Teddy Hogeborn
Make "enabled" a client config option.
1580
        if self.enabled:
1581
            # Reschedule checker run
1582
            gobject.source_remove(self.checker_initiator_tag)
1583
            self.checker_initiator_tag = (gobject.timeout_add
1584
                                          (value, self.start_checker))
1585
            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
1586
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1587
    # Checker - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1588
    @dbus_service_property(_interface, signature="s",
1589
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1590
    def Checker_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1591
        if value is None:       # get
1592
            return dbus.String(self.checker_command)
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1593
        self.checker_command = str(value)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1594
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1595
    # CheckerRunning - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1596
    @dbus_service_property(_interface, signature="b",
1597
                           access="readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1598
    def CheckerRunning_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1599
        if value is None:       # get
1600
            return dbus.Boolean(self.checker is not None)
1601
        if value:
1602
            self.start_checker()
1603
        else:
1604
            self.stop_checker()
1605
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1606
    # ObjectPath - property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1607
    @dbus_service_property(_interface, signature="o", access="read")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1608
    def ObjectPath_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1609
        return self.dbus_object_path # is already a dbus.ObjectPath
1610
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1611
    # Secret = property
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1612
    @dbus_service_property(_interface, signature="ay",
1613
                           access="write", byte_arrays=True)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1614
    def Secret_dbus_property(self, value):
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
1615
        self.secret = bytes(value)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1616
    
1617
    del _interface
3 by Björn Påhlsson
Python based server
1618
1619
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1620
class ProxyClient(object):
1621
    def __init__(self, child_pipe, fpr, address):
1622
        self._pipe = child_pipe
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1623
        self._pipe.send(('init', fpr, address))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1624
        if not self._pipe.recv():
1625
            raise KeyError()
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1626
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1627
    def __getattribute__(self, name):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1628
        if name == '_pipe':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1629
            return super(ProxyClient, self).__getattribute__(name)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1630
        self._pipe.send(('getattr', name))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1631
        data = self._pipe.recv()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1632
        if data[0] == 'data':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1633
            return data[1]
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1634
        if data[0] == 'function':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1635
            def func(*args, **kwargs):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1636
                self._pipe.send(('funcall', name, args, kwargs))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1637
                return self._pipe.recv()[1]
1638
            return func
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1639
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1640
    def __setattr__(self, name, value):
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1641
        if name == '_pipe':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1642
            return super(ProxyClient, self).__setattr__(name, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1643
        self._pipe.send(('setattr', name, value))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1644
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
1645
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1646
class ClientHandler(socketserver.BaseRequestHandler, object):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1647
    """A class to handle client connections.
1648
    
1649
    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
1650
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
1651
    
3 by Björn Påhlsson
Python based server
1652
    def handle(self):
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1653
        with contextlib.closing(self.server.child_pipe) as child_pipe:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1654
            logger.info("TCP connection from: %s",
730 by Teddy Hogeborn
mandos: Replace unicode() with str().
1655
                        str(self.client_address))
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1656
            logger.debug("Pipe FD: %d",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1657
                         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
1658
            
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1659
            session = (gnutls.connection
1660
                       .ClientSession(self.request,
1661
                                      gnutls.connection
1662
                                      .X509Credentials()))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1663
            
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1664
            # Note: gnutls.connection.X509Credentials is really a
1665
            # generic GnuTLS certificate credentials object so long as
1666
            # no X.509 keys are added to it.  Therefore, we can use it
1667
            # 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
1668
            
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1669
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1670
            #                      "+AES-256-CBC", "+SHA1",
1671
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1672
            #                      "+DHE-DSS"))
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1673
            # Use a fallback default, since this MUST be set.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1674
            priority = self.server.gnutls_priority
1675
            if priority is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1676
                priority = "NORMAL"
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1677
            (gnutls.library.functions
1678
             .gnutls_priority_set_direct(session._c_object,
1679
                                         priority, None))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1680
            
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1681
            # Start communication using the Mandos protocol
1682
            # Get protocol number
1683
            line = self.request.makefile().readline()
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1684
            logger.debug("Protocol version: %r", line)
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1685
            try:
1686
                if int(line.strip().split()[0]) > 1:
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
1687
                    raise RuntimeError(line)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1688
            except (ValueError, IndexError, RuntimeError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1689
                logger.error("Unknown protocol version: %s", error)
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1690
                return
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1691
            
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1692
            # Start GnuTLS connection
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1693
            try:
1694
                session.handshake()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1695
            except gnutls.errors.GNUTLSError as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1696
                logger.warning("Handshake failed: %s", error)
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1697
                # Do not run session.bye() here: the session is not
1698
                # established.  Just abandon the request.
1699
                return
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1700
            logger.debug("Handshake succeeded")
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1701
            
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1702
            approval_required = False
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1703
            try:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1704
                try:
1705
                    fpr = self.fingerprint(self.peer_certificate
1706
                                           (session))
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1707
                except (TypeError,
1708
                        gnutls.errors.GNUTLSError) as error:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1709
                    logger.warning("Bad certificate: %s", error)
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1710
                    return
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1711
                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
1712
                
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1713
                try:
1714
                    client = ProxyClient(child_pipe, fpr,
1715
                                         self.client_address)
1716
                except KeyError:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1717
                    return
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1718
                
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1719
                if client.approval_delay:
1720
                    delay = client.approval_delay
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1721
                    client.approvals_pending += 1
1722
                    approval_required = True
1723
                
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
1724
                while True:
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1725
                    if not client.enabled:
24.1.174 by Björn Påhlsson
* Makefile (CFLAGS): Added "-lrt" to include real time library.
1726
                        logger.info("Client %s is disabled",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1727
                                       client.name)
1728
                        if self.server.use_dbus:
1729
                            # Emit D-Bus signal
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
1730
                            client.Rejected("Disabled")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1731
                        return
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1732
                    
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1733
                    if client.approved or not client.approval_delay:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1734
                        #We are approved or approval is disabled
1735
                        break
518.2.4 by Teddy Hogeborn
* mandos (_timedelta_to_milliseconds): Renamed to
1736
                    elif client.approved is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1737
                        logger.info("Client %s needs approval",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1738
                                    client.name)
1739
                        if self.server.use_dbus:
1740
                            # Emit D-Bus signal
1741
                            client.NeedApproval(
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1742
                                client.approval_delay.total_seconds()
1743
                                * 1000, client.approved_by_default)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1744
                    else:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1745
                        logger.warning("Client %s was not approved",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1746
                                       client.name)
1747
                        if self.server.use_dbus:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1748
                            # Emit D-Bus signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1749
                            client.Rejected("Denied")
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
1750
                        return
1751
                    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1752
                    #wait until timeout or approved
1753
                    time = datetime.datetime.now()
1754
                    client.changedstate.acquire()
723.1.6 by Teddy Hogeborn
Use the new .total_seconds() method on datetime.timedelta objects.
1755
                    client.changedstate.wait(delay.total_seconds())
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1756
                    client.changedstate.release()
1757
                    time2 = datetime.datetime.now()
1758
                    if (time2 - time) >= delay:
1759
                        if not client.approved_by_default:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1760
                            logger.warning("Client %s timed out while"
1761
                                           " waiting for approval",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1762
                                           client.name)
1763
                            if self.server.use_dbus:
1764
                                # Emit D-Bus signal
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1765
                                client.Rejected("Approval timed out")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1766
                            return
1767
                        else:
1768
                            break
1769
                    else:
1770
                        delay -= time2 - time
1771
                
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1772
                sent_size = 0
1773
                while sent_size < len(client.secret):
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1774
                    try:
1775
                        sent = session.send(client.secret[sent_size:])
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1776
                    except gnutls.errors.GNUTLSError as error:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1777
                        logger.warning("gnutls send failed",
1778
                                       exc_info=error)
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1779
                        return
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1780
                    logger.debug("Sent: %d, remaining: %d",
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1781
                                 sent, len(client.secret)
1782
                                 - (sent_size + sent))
1783
                    sent_size += sent
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1784
                
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1785
                logger.info("Sending secret to %s", client.name)
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
1786
                # bump the timeout using extended_timeout
556 by Teddy Hogeborn
* DBUS-API (se.recompile.Mandos.Client.LastCheckerStatus): New
1787
                client.bump_timeout(client.extended_timeout)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1788
                if self.server.use_dbus:
1789
                    # Emit D-Bus signal
1790
                    client.GotSecret()
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1791
            
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1792
            finally:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1793
                if approval_required:
1794
                    client.approvals_pending -= 1
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1795
                try:
1796
                    session.bye()
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1797
                except gnutls.errors.GNUTLSError as error:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
1798
                    logger.warning("GnuTLS bye failed",
1799
                                   exc_info=error)
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1800
    
1801
    @staticmethod
1802
    def peer_certificate(session):
1803
        "Return the peer's OpenPGP certificate as a bytestring"
1804
        # If not an OpenPGP certificate...
1805
        if (gnutls.library.functions
1806
            .gnutls_certificate_type_get(session._c_object)
1807
            != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1808
            # ...do the normal thing
1809
            return session.peer_certificate
1810
        list_size = ctypes.c_uint(1)
1811
        cert_list = (gnutls.library.functions
1812
                     .gnutls_certificate_get_peers
1813
                     (session._c_object, ctypes.byref(list_size)))
1814
        if not bool(cert_list) and list_size.value != 0:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1815
            raise gnutls.errors.GNUTLSError("error getting peer"
1816
                                            " certificate")
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1817
        if list_size.value == 0:
1818
            return None
1819
        cert = cert_list[0]
1820
        return ctypes.string_at(cert.data, cert.size)
1821
    
1822
    @staticmethod
1823
    def fingerprint(openpgp):
1824
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
1825
        # New GnuTLS "datum" with the OpenPGP public key
1826
        datum = (gnutls.library.types
1827
                 .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1828
                                             ctypes.POINTER
1829
                                             (ctypes.c_ubyte)),
1830
                                 ctypes.c_uint(len(openpgp))))
1831
        # New empty GnuTLS certificate
1832
        crt = gnutls.library.types.gnutls_openpgp_crt_t()
1833
        (gnutls.library.functions
1834
         .gnutls_openpgp_crt_init(ctypes.byref(crt)))
1835
        # Import the OpenPGP public key into the certificate
1836
        (gnutls.library.functions
1837
         .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1838
                                    gnutls.library.constants
1839
                                    .GNUTLS_OPENPGP_FMT_RAW))
1840
        # Verify the self signature in the key
1841
        crtverify = ctypes.c_uint()
1842
        (gnutls.library.functions
1843
         .gnutls_openpgp_crt_verify_self(crt, 0,
1844
                                         ctypes.byref(crtverify)))
1845
        if crtverify.value != 0:
1846
            gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1847
            raise (gnutls.errors.CertificateSecurityError
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1848
                   ("Verify failed"))
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1849
        # New buffer for the fingerprint
1850
        buf = ctypes.create_string_buffer(20)
1851
        buf_len = ctypes.c_size_t()
1852
        # Get the fingerprint from the certificate into the buffer
1853
        (gnutls.library.functions
1854
         .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1855
                                             ctypes.byref(buf_len)))
1856
        # Deinit the certificate
1857
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1858
        # Convert the buffer to a Python bytestring
1859
        fpr = ctypes.string_at(buf, buf_len.value)
1860
        # Convert the bytestring to hexadecimal notation
518.2.1 by Teddy Hogeborn
Use GPG to encrypt instead of AES.
1861
        hex_fpr = binascii.hexlify(fpr).upper()
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1862
        return hex_fpr
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1863
1864
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1865
class MultiprocessingMixIn(object):
1866
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
1867
    def sub_process_main(self, request, address):
1868
        try:
1869
            self.finish_request(request, address)
518.1.14 by Björn Påhlsson
Mandos: refactoring the code handling settings from config files.
1870
        except Exception:
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1871
            self.handle_error(request, address)
1872
        self.close_request(request)
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1873
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1874
    def process_request(self, request, address):
1875
        """Start a new process to process the request."""
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1876
        proc = multiprocessing.Process(target = self.sub_process_main,
579 by Teddy Hogeborn
* mandos: Use all new builtins.
1877
                                       args = (request, address))
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1878
        proc.start()
1879
        return proc
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1880
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1881
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1882
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1883
    """ adds a pipe to the MixIn """
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1884
    def process_request(self, request, client_address):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1885
        """Overrides and wraps the original process_request().
1886
        
355 by Teddy Hogeborn
* mandos: White-space fixes only.
1887
        This function creates a new pipe in self.pipe
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1888
        """
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1889
        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
1890
        
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1891
        proc = MultiprocessingMixIn.process_request(self, request,
1892
                                                    client_address)
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
1893
        self.child_pipe.close()
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1894
        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
1895
    
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
1896
    def add_pipe(self, parent_pipe, proc):
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1897
        """Dummy function; override as necessary"""
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
1898
        raise NotImplementedError()
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1899
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
1900
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1901
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1902
                     socketserver.TCPServer, object):
237.2.13 by Teddy Hogeborn
Merge from trunk. Notable changes:
1903
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1904
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1905
    Attributes:
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1906
        enabled:        Boolean; whether this server is activated yet
1907
        interface:      None or a network interface name (string)
1908
        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
1909
    """
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1910
    def __init__(self, server_address, RequestHandlerClass,
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
1911
                 interface=None, use_ipv6=True, socketfd=None):
1912
        """If socketfd is set, use that file descriptor instead of
1913
        creating a new one with socket.socket().
1914
        """
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1915
        self.interface = interface
1916
        if use_ipv6:
1917
            self.address_family = socket.AF_INET6
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
1918
        if socketfd is not None:
1919
            # Save the file descriptor
1920
            self.socketfd = socketfd
1921
            # Save the original socket.socket() function
1922
            self.socket_socket = socket.socket
1923
            # To implement --socket, we monkey patch socket.socket.
1924
            # 
1925
            # (When socketserver.TCPServer is a new-style class, we
1926
            # could make self.socket into a property instead of monkey
1927
            # patching socket.socket.)
1928
            # 
1929
            # Create a one-time-only replacement for socket.socket()
1930
            @functools.wraps(socket.socket)
1931
            def socket_wrapper(*args, **kwargs):
1932
                # Restore original function so subsequent calls are
1933
                # not affected.
1934
                socket.socket = self.socket_socket
1935
                del self.socket_socket
1936
                # This time only, return a new socket object from the
1937
                # saved file descriptor.
1938
                return socket.fromfd(self.socketfd, *args, **kwargs)
1939
            # Replace socket.socket() function with wrapper
1940
            socket.socket = socket_wrapper
1941
        # The socketserver.TCPServer.__init__ will call
1942
        # socket.socket(), which might be our replacement,
1943
        # socket_wrapper(), if socketfd was set.
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1944
        socketserver.TCPServer.__init__(self, server_address,
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1945
                                        RequestHandlerClass)
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
1946
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1947
    def server_bind(self):
1948
        """This overrides the normal server_bind() function
1949
        to bind to an interface if one was specified, and also NOT to
1950
        bind to an address or port if they were not specified."""
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1951
        if self.interface is not None:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1952
            if SO_BINDTODEVICE is None:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1953
                logger.error("SO_BINDTODEVICE does not exist;"
1954
                             " cannot bind to interface %s",
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1955
                             self.interface)
1956
            else:
1957
                try:
1958
                    self.socket.setsockopt(socket.SOL_SOCKET,
1959
                                           SO_BINDTODEVICE,
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
1960
                                           (self.interface + "\0")
1961
                                           .encode("utf-8"))
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
1962
                except socket.error as error:
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1963
                    if error.errno == errno.EPERM:
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
1964
                        logger.error("No permission to bind to"
1965
                                     " interface %s", self.interface)
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1966
                    elif error.errno == errno.ENOPROTOOPT:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1967
                        logger.error("SO_BINDTODEVICE not available;"
1968
                                     " cannot bind to interface %s",
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1969
                                     self.interface)
582 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: handle non-existing
1970
                    elif error.errno == errno.ENODEV:
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
1971
                        logger.error("Interface %s does not exist,"
1972
                                     " cannot bind", self.interface)
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1973
                    else:
1974
                        raise
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1975
        # Only bind(2) the socket if we really need to.
1976
        if self.server_address[0] or self.server_address[1]:
1977
            if not self.server_address[0]:
314 by Teddy Hogeborn
Support not using IPv6 in server:
1978
                if self.address_family == socket.AF_INET6:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
1979
                    any_address = "::" # in6addr_any
314 by Teddy Hogeborn
Support not using IPv6 in server:
1980
                else:
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
1981
                    any_address = "0.0.0.0" # INADDR_ANY
314 by Teddy Hogeborn
Support not using IPv6 in server:
1982
                self.server_address = (any_address,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1983
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
1984
            elif not self.server_address[1]:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1985
                self.server_address = (self.server_address[0],
1986
                                       0)
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1987
#                 if self.interface:
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
1988
#                     self.server_address = (self.server_address[0],
1989
#                                            0, # port
1990
#                                            0, # flowinfo
1991
#                                            if_nametoindex
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1992
#                                            (self.interface))
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1993
            return socketserver.TCPServer.server_bind(self)
339 by Teddy Hogeborn
Code cleanup.
1994
1995
1996
class MandosServer(IPv6_TCPServer):
1997
    """Mandos server.
1998
    
1999
    Attributes:
2000
        clients:        set of Client objects
2001
        gnutls_priority GnuTLS priority string
2002
        use_dbus:       Boolean; to emit D-Bus signals or not
340 by Teddy Hogeborn
Code cleanup.
2003
    
2004
    Assumes a gobject.MainLoop event loop.
339 by Teddy Hogeborn
Code cleanup.
2005
    """
2006
    def __init__(self, server_address, RequestHandlerClass,
2007
                 interface=None, use_ipv6=True, clients=None,
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2008
                 gnutls_priority=None, use_dbus=True, socketfd=None):
339 by Teddy Hogeborn
Code cleanup.
2009
        self.enabled = False
2010
        self.clients = clients
341 by Teddy Hogeborn
Code cleanup and one bug fix.
2011
        if self.clients is None:
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2012
            self.clients = {}
339 by Teddy Hogeborn
Code cleanup.
2013
        self.use_dbus = use_dbus
2014
        self.gnutls_priority = gnutls_priority
2015
        IPv6_TCPServer.__init__(self, server_address,
2016
                                RequestHandlerClass,
2017
                                interface = interface,
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2018
                                use_ipv6 = use_ipv6,
2019
                                socketfd = socketfd)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2020
    def server_activate(self):
2021
        if self.enabled:
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2022
            return socketserver.TCPServer.server_activate(self)
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2023
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2024
    def enable(self):
2025
        self.enabled = True
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2026
    
2027
    def add_pipe(self, parent_pipe, proc):
340 by Teddy Hogeborn
Code cleanup.
2028
        # Call "handle_ipc" for both data and EOF events
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2029
        gobject.io_add_watch(parent_pipe.fileno(),
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
2030
                             gobject.IO_IN | gobject.IO_HUP,
2031
                             functools.partial(self.handle_ipc,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2032
                                               parent_pipe =
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2033
                                               parent_pipe,
2034
                                               proc = proc))
505.1.9 by Teddy Hogeborn
Whitespace
2035
    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2036
    def handle_ipc(self, source, condition, parent_pipe=None,
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2037
                   proc = None, client_object=None):
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2038
        # error, or the other end of multiprocessing.Pipe has closed
577 by Teddy Hogeborn
* mandos (Client.start_checker): Reworded comment.
2039
        if condition & (gobject.IO_ERR | gobject.IO_HUP):
505.1.13 by Teddy Hogeborn
Miscellaneous fixes prompted by lintian:
2040
            # Wait for other process to exit
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2041
            proc.join()
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
2042
            return False
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2043
        
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2044
        # Read a request from the child
2045
        request = parent_pipe.recv()
2046
        command = request[0]
2047
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2048
        if command == 'init':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2049
            fpr = request[1]
2050
            address = request[2]
2051
            
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2052
            for c in self.clients.itervalues():
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2053
                if c.fingerprint == fpr:
2054
                    client = c
2055
                    break
2056
            else:
24.1.174 by Björn Påhlsson
* Makefile (CFLAGS): Added "-lrt" to include real time library.
2057
                logger.info("Client not found for fingerprint: %s, ad"
2058
                            "dress: %s", fpr, address)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2059
                if self.use_dbus:
2060
                    # Emit D-Bus signal
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2061
                    mandos_dbus_service.ClientNotFound(fpr,
2062
                                                       address[0])
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2063
                parent_pipe.send(False)
2064
                return False
2065
            
2066
            gobject.io_add_watch(parent_pipe.fileno(),
2067
                                 gobject.IO_IN | gobject.IO_HUP,
2068
                                 functools.partial(self.handle_ipc,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2069
                                                   parent_pipe =
2070
                                                   parent_pipe,
505.1.11 by Teddy Hogeborn
* mandos (Client.checked_ok): Set "expires" after setting timeout.
2071
                                                   proc = proc,
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2072
                                                   client_object =
2073
                                                   client))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2074
            parent_pipe.send(True)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2075
            # remove the old hook in favor of the new above hook on
2076
            # same fileno
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2077
            return False
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2078
        if command == 'funcall':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2079
            funcname = request[1]
2080
            args = request[2]
2081
            kwargs = request[3]
2082
            
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2083
            parent_pipe.send(('data', getattr(client_object,
2084
                                              funcname)(*args,
2085
                                                         **kwargs)))
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2086
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2087
        if command == 'getattr':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2088
            attrname = request[1]
2089
            if callable(client_object.__getattribute__(attrname)):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2090
                parent_pipe.send(('function',))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2091
            else:
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2092
                parent_pipe.send(('data', client_object
2093
                                  .__getattribute__(attrname)))
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
2094
        
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2095
        if command == 'setattr':
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
2096
            attrname = request[1]
2097
            value = request[2]
2098
            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
2099
        
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
2100
        return True
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
2101
3 by Björn Påhlsson
Python based server
2102
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2103
def rfc3339_duration_to_delta(duration):
2104
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2105
    
2106
    >>> rfc3339_duration_to_delta("P7D")
2107
    datetime.timedelta(7)
2108
    >>> rfc3339_duration_to_delta("PT60S")
2109
    datetime.timedelta(0, 60)
2110
    >>> rfc3339_duration_to_delta("PT60M")
2111
    datetime.timedelta(0, 3600)
2112
    >>> rfc3339_duration_to_delta("PT24H")
2113
    datetime.timedelta(1)
2114
    >>> rfc3339_duration_to_delta("P1W")
2115
    datetime.timedelta(7)
2116
    >>> rfc3339_duration_to_delta("PT5M30S")
2117
    datetime.timedelta(0, 330)
2118
    >>> rfc3339_duration_to_delta("P1DT3M20S")
2119
    datetime.timedelta(1, 200)
2120
    """
2121
    
2122
    # Parsing an RFC 3339 duration with regular expressions is not
2123
    # possible - there would have to be multiple places for the same
2124
    # values, like seconds.  The current code, while more esoteric, is
2125
    # cleaner without depending on a parsing library.  If Python had a
2126
    # built-in library for parsing we would use it, but we'd like to
2127
    # avoid excessive use of external libraries.
2128
    
2129
    # New type for defining tokens, syntax, and semantics all-in-one
2130
    Token = collections.namedtuple("Token",
2131
                                   ("regexp", # To match token; if
2132
                                              # "value" is not None,
2133
                                              # must have a "group"
2134
                                              # containing digits
2135
                                    "value",  # datetime.timedelta or
2136
                                              # None
2137
                                    "followers")) # Tokens valid after
2138
                                                  # this token
2139
    # RFC 3339 "duration" tokens, syntax, and semantics; taken from
2140
    # the "duration" ABNF definition in RFC 3339, Appendix A.
2141
    token_end = Token(re.compile(r"$"), None, frozenset())
2142
    token_second = Token(re.compile(r"(\d+)S"),
2143
                         datetime.timedelta(seconds=1),
2144
                         frozenset((token_end,)))
2145
    token_minute = Token(re.compile(r"(\d+)M"),
2146
                         datetime.timedelta(minutes=1),
2147
                         frozenset((token_second, token_end)))
2148
    token_hour = Token(re.compile(r"(\d+)H"),
2149
                       datetime.timedelta(hours=1),
2150
                       frozenset((token_minute, token_end)))
2151
    token_time = Token(re.compile(r"T"),
2152
                       None,
2153
                       frozenset((token_hour, token_minute,
2154
                                  token_second)))
2155
    token_day = Token(re.compile(r"(\d+)D"),
2156
                      datetime.timedelta(days=1),
2157
                      frozenset((token_time, token_end)))
2158
    token_month = Token(re.compile(r"(\d+)M"),
2159
                        datetime.timedelta(weeks=4),
2160
                        frozenset((token_day, token_end)))
2161
    token_year = Token(re.compile(r"(\d+)Y"),
2162
                       datetime.timedelta(weeks=52),
2163
                       frozenset((token_month, token_end)))
2164
    token_week = Token(re.compile(r"(\d+)W"),
2165
                       datetime.timedelta(weeks=1),
2166
                       frozenset((token_end,)))
2167
    token_duration = Token(re.compile(r"P"), None,
2168
                           frozenset((token_year, token_month,
2169
                                      token_day, token_time,
721 by Teddy Hogeborn
Fix two mutually cancelling bugs.
2170
                                      token_week)))
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2171
    # Define starting values
2172
    value = datetime.timedelta() # Value so far
2173
    found_token = None
721 by Teddy Hogeborn
Fix two mutually cancelling bugs.
2174
    followers = frozenset((token_duration,)) # Following valid tokens
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2175
    s = duration                # String left to parse
2176
    # Loop until end token is found
2177
    while found_token is not token_end:
2178
        # Search for any currently valid tokens
2179
        for token in followers:
2180
            match = token.regexp.match(s)
2181
            if match is not None:
2182
                # Token found
2183
                if token.value is not None:
2184
                    # Value found, parse digits
2185
                    factor = int(match.group(1), 10)
2186
                    # Add to value so far
2187
                    value += factor * token.value
2188
                # Strip token from string
2189
                s = token.regexp.sub("", s, 1)
2190
                # Go to found token
2191
                found_token = token
2192
                # Set valid next tokens
2193
                followers = found_token.followers
2194
                break
2195
        else:
2196
            # No currently valid tokens were found
2197
            raise ValueError("Invalid RFC 3339 duration")
2198
    # End token found
2199
    return value
2200
2201
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2202
def string_to_delta(interval):
2203
    """Parse a string and return a datetime.timedelta
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2204
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2205
    >>> string_to_delta('7d')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2206
    datetime.timedelta(7)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2207
    >>> string_to_delta('60s')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2208
    datetime.timedelta(0, 60)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2209
    >>> string_to_delta('60m')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2210
    datetime.timedelta(0, 3600)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2211
    >>> string_to_delta('24h')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2212
    datetime.timedelta(1)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2213
    >>> string_to_delta('1w')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2214
    datetime.timedelta(7)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2215
    >>> string_to_delta('5m 30s')
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2216
    datetime.timedelta(0, 330)
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2217
    """
609 by Teddy Hogeborn
* clients.conf: Convert all time intervals to new RFC 3339 syntax.
2218
    
2219
    try:
2220
        return rfc3339_duration_to_delta(interval)
2221
    except ValueError:
2222
        pass
2223
    
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2224
    timevalue = datetime.timedelta(0)
2225
    for s in interval.split():
2226
        try:
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2227
            suffix = s[-1]
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
2228
            value = int(s[:-1])
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2229
            if suffix == "d":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2230
                delta = datetime.timedelta(value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2231
            elif suffix == "s":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2232
                delta = datetime.timedelta(0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2233
            elif suffix == "m":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2234
                delta = datetime.timedelta(0, 0, 0, 0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2235
            elif suffix == "h":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2236
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2237
            elif suffix == "w":
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2238
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
2239
            else:
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2240
                raise ValueError("Unknown suffix {!r}"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2241
                                 .format(suffix))
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2242
        except IndexError as e:
464 by Teddy Hogeborn
* debian/mandos-client.postrm (purge): Bug fix: update initramfs also
2243
            raise ValueError(*(e.args))
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
2244
        timevalue += delta
2245
    return timevalue
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2246
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
2247
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
2248
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2249
    """See daemon(3).  Standard BSD Unix function.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
2250
    
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2251
    This should really exist as os.daemon, but it doesn't (yet)."""
2252
    if os.fork():
2253
        sys.exit()
2254
    os.setsid()
2255
    if not nochdir:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2256
        os.chdir("/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
2257
    if os.fork():
2258
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2259
    if not noclose:
2260
        # 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
2261
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2262
        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.
2263
            raise OSError(errno.ENODEV, "{} not a character device"
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2264
                          .format(os.devnull))
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2265
        os.dup2(null, sys.stdin.fileno())
2266
        os.dup2(null, sys.stdout.fileno())
2267
        os.dup2(null, sys.stderr.fileno())
2268
        if null > 2:
2269
            os.close(null)
2270
2271
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2272
def main():
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2273
    
379 by Teddy Hogeborn
* mandos: Fix line lengths.
2274
    ##################################################################
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2275
    # Parsing of options, both command line and config file
2276
    
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2277
    parser = argparse.ArgumentParser()
2278
    parser.add_argument("-v", "--version", action="version",
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2279
                        version = "%(prog)s {}".format(version),
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2280
                        help="show version number and exit")
2281
    parser.add_argument("-i", "--interface", metavar="IF",
2282
                        help="Bind to interface IF")
2283
    parser.add_argument("-a", "--address",
2284
                        help="Address to listen for requests on")
2285
    parser.add_argument("-p", "--port", type=int,
2286
                        help="Port number to receive requests on")
2287
    parser.add_argument("--check", action="store_true",
2288
                        help="Run self-test")
2289
    parser.add_argument("--debug", action="store_true",
2290
                        help="Debug mode; run in foreground and log"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2291
                        " to terminal", default=None)
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2292
    parser.add_argument("--debuglevel", metavar="LEVEL",
2293
                        help="Debug level for stdout output")
2294
    parser.add_argument("--priority", help="GnuTLS"
2295
                        " priority string (see GnuTLS documentation)")
2296
    parser.add_argument("--servicename",
2297
                        metavar="NAME", help="Zeroconf service name")
2298
    parser.add_argument("--configdir",
2299
                        default="/etc/mandos", metavar="DIR",
2300
                        help="Directory to search for configuration"
2301
                        " files")
2302
    parser.add_argument("--no-dbus", action="store_false",
2303
                        dest="use_dbus", help="Do not provide D-Bus"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2304
                        " system bus interface", default=None)
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2305
    parser.add_argument("--no-ipv6", action="store_false",
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2306
                        dest="use_ipv6", help="Do not use IPv6",
2307
                        default=None)
518.1.1 by Björn Påhlsson
Persistent state: New feature. Client state is now stored when mandos
2308
    parser.add_argument("--no-restore", action="store_false",
518.1.7 by Teddy Hogeborn
* mandos: Break long lines and fix some more white space.
2309
                        dest="restore", help="Do not restore stored"
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2310
                        " state", default=None)
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2311
    parser.add_argument("--socket", type=int,
2312
                        help="Specify a file descriptor to a network"
2313
                        " socket to use instead of creating one")
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2314
    parser.add_argument("--statedir", metavar="DIR",
2315
                        help="Directory to save/restore state in")
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2316
    parser.add_argument("--foreground", action="store_true",
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2317
                        help="Run in foreground", default=None)
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2318
    parser.add_argument("--no-zeroconf", action="store_false",
2319
                        dest="zeroconf", help="Do not use Zeroconf",
2320
                        default=None)
518.1.6 by Teddy Hogeborn
* mandos: Fix whitespace.
2321
    
474 by teddy at bsnet
* mandos: Use the new argparse library instead of optparse.
2322
    options = parser.parse_args()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2323
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
2324
    if options.check:
2325
        import doctest
654 by Teddy Hogeborn
Fix running of self-tests.
2326
        fail_count, test_count = doctest.testmod()
2327
        sys.exit(os.EX_OK if fail_count == 0 else 1)
3 by Björn Påhlsson
Python based server
2328
    
28 by Teddy Hogeborn
* server.conf: New file.
2329
    # Default values for config file for server-global settings
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2330
    server_defaults = { "interface": "",
2331
                        "address": "",
2332
                        "port": "",
2333
                        "debug": "False",
2334
                        "priority":
631 by Teddy Hogeborn
* mandos (priority): Bug fix: Add even more magic to make the old
2335
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:+SIGN-RSA-SHA224:+SIGN-RSA-RMD160",
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2336
                        "servicename": "Mandos",
2337
                        "use_dbus": "True",
2338
                        "use_ipv6": "True",
2339
                        "debuglevel": "",
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2340
                        "restore": "True",
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2341
                        "socket": "",
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2342
                        "statedir": "/var/lib/mandos",
2343
                        "foreground": "False",
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2344
                        "zeroconf": "True",
28 by Teddy Hogeborn
* server.conf: New file.
2345
                        }
2346
    
2347
    # Parse config file for server-global settings
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
2348
    server_config = configparser.SafeConfigParser(server_defaults)
28 by Teddy Hogeborn
* server.conf: New file.
2349
    del server_defaults
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2350
    server_config.read(os.path.join(options.configdir,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2351
                                    "mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
2352
    # Convert the SafeConfigParser object to a dict
89 by Teddy Hogeborn
* Makefile: Bug fix: fixed creation of man pages for section 5 pages.
2353
    server_settings = server_config.defaults()
282 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
2354
    # Use the appropriate methods on the non-string config options
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2355
    for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2356
        server_settings[option] = server_config.getboolean("DEFAULT",
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2357
                                                           option)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2358
    if server_settings["port"]:
2359
        server_settings["port"] = server_config.getint("DEFAULT",
2360
                                                       "port")
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2361
    if server_settings["socket"]:
2362
        server_settings["socket"] = server_config.getint("DEFAULT",
2363
                                                         "socket")
591 by Teddy Hogeborn
Merge "--socket" option for server.
2364
        # Later, stdin will, and stdout and stderr might, be dup'ed
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2365
        # over with an opened os.devnull.  But we don't want this to
2366
        # happen with a supplied network socket.
2367
        if 0 <= server_settings["socket"] <= 2:
2368
            server_settings["socket"] = os.dup(server_settings
2369
                                               ["socket"])
28 by Teddy Hogeborn
* server.conf: New file.
2370
    del server_config
2371
    
2372
    # Override the settings from the config file with command line
2373
    # options, if set.
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2374
    for option in ("interface", "address", "port", "debug",
2375
                   "priority", "servicename", "configdir",
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2376
                   "use_dbus", "use_ipv6", "debuglevel", "restore",
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2377
                   "statedir", "socket", "foreground", "zeroconf"):
28 by Teddy Hogeborn
* server.conf: New file.
2378
        value = getattr(options, option)
2379
        if value is not None:
2380
            server_settings[option] = value
2381
    del options
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2382
    # Force all strings to be unicode
2383
    for option in server_settings.keys():
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2384
        if isinstance(server_settings[option], bytes):
2385
            server_settings[option] = (server_settings[option]
2386
                                       .decode("utf-8"))
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2387
    # Force all boolean options to be boolean
2388
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2389
                   "foreground", "zeroconf"):
618 by Teddy Hogeborn
* mandos: Bug fix: Make boolean options work from the config file
2390
        server_settings[option] = bool(server_settings[option])
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2391
    # Debug implies foreground
2392
    if server_settings["debug"]:
2393
        server_settings["foreground"] = True
28 by Teddy Hogeborn
* server.conf: New file.
2394
    # Now we have our good server settings in "server_settings"
2395
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2396
    ##################################################################
2397
    
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2398
    if (not server_settings["zeroconf"] and
2399
        not (server_settings["port"]
2400
             or server_settings["socket"] != "")):
2401
            parser.error("Needs port or socket to work without"
2402
                         " Zeroconf")
2403
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2404
    # For convenience
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2405
    debug = server_settings["debug"]
2406
    debuglevel = server_settings["debuglevel"]
2407
    use_dbus = server_settings["use_dbus"]
2408
    use_ipv6 = server_settings["use_ipv6"]
518.2.2 by Teddy Hogeborn
Directory with persistent state can now be changed with the "statedir"
2409
    stored_state_path = os.path.join(server_settings["statedir"],
2410
                                     stored_state_file)
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2411
    foreground = server_settings["foreground"]
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2412
    zeroconf = server_settings["zeroconf"]
24.1.185 by Björn Påhlsson
working transition code for going between se.bsnet.fukt to se.recompile
2413
    
518.1.4 by Björn Påhlsson
restructured logger
2414
    if debug:
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2415
        initlogger(debug, logging.DEBUG)
518.1.4 by Björn Påhlsson
restructured logger
2416
    else:
2417
        if not debuglevel:
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2418
            initlogger(debug)
518.1.4 by Björn Påhlsson
restructured logger
2419
        else:
2420
            level = getattr(logging, debuglevel.upper())
530 by teddy at bsnet
* mandos (initlogger): Take new "debug" argument; all callers changed.
2421
            initlogger(debug, level)
518.1.4 by Björn Påhlsson
restructured logger
2422
    
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2423
    if server_settings["servicename"] != "Mandos":
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
2424
        syslogger.setFormatter(logging.Formatter
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
2425
                               ('Mandos ({}) [%(process)d]:'
568 by Teddy Hogeborn
* mandos-monitor: Use new string format method.
2426
                                ' %(levelname)s: %(message)s'
2427
                                .format(server_settings
2428
                                        ["servicename"])))
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
2429
    
28 by Teddy Hogeborn
* server.conf: New file.
2430
    # Parse config file with clients
543 by Teddy Hogeborn
* mandos: Break some long lines.
2431
    client_config = configparser.SafeConfigParser(Client
2432
                                                  .client_defaults)
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2433
    client_config.read(os.path.join(server_settings["configdir"],
2434
                                    "clients.conf"))
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2435
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
2436
    global mandos_dbus_service
2437
    mandos_dbus_service = None
28 by Teddy Hogeborn
* server.conf: New file.
2438
    
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2439
    socketfd = None
2440
    if server_settings["socket"] != "":
2441
        socketfd = server_settings["socket"]
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2442
    tcp_server = MandosServer((server_settings["address"],
2443
                               server_settings["port"]),
339 by Teddy Hogeborn
Code cleanup.
2444
                              ClientHandler,
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2445
                              interface=(server_settings["interface"]
237.2.35 by teddy at bsnet
* mandos (main): Bug fix: Don't try to bind to an empty string
2446
                                         or None),
339 by Teddy Hogeborn
Code cleanup.
2447
                              use_ipv6=use_ipv6,
2448
                              gnutls_priority=
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2449
                              server_settings["priority"],
589.1.1 by Teddy Hogeborn
* mandos: Implement "--socket" option.
2450
                              use_dbus=use_dbus,
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2451
                              socketfd=socketfd)
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2452
    if not foreground:
626 by Teddy Hogeborn
* Makefile (CFLAGS, LDFLAGS): Keep default flags from environment.
2453
        pidfilename = "/run/mandos.pid"
636 by Teddy Hogeborn
Fall back to /var/run for pidfile if /run is not a directory.
2454
        if not os.path.isdir("/run/."):
2455
            pidfilename = "/var/run/mandos.pid"
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2456
        pidfile = None
439 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
2457
        try:
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2458
            pidfile = open(pidfilename, "w")
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2459
        except IOError as e:
2460
            logger.error("Could not open file %r", pidfilename,
2461
                         exc_info=e)
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
2462
    
566 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2463
    for name in ("_mandos", "mandos", "nobody"):
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2464
        try:
566 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2465
            uid = pwd.getpwnam(name).pw_uid
2466
            gid = pwd.getpwnam(name).pw_gid
2467
            break
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
2468
        except KeyError:
566 by Teddy Hogeborn
* mandos (main): Simplify and shorten code selecting user and group ID
2469
            continue
2470
    else:
2471
        uid = 65534
2472
        gid = 65534
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2473
    try:
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2474
        os.setgid(gid)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
2475
        os.setuid(uid)
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2476
    except OSError as error:
583 by Teddy Hogeborn
* mandos (Client.start_checker): Remove undocumented support for "%%s"
2477
        if error.errno != errno.EPERM:
642 by Teddy Hogeborn
Syntax fix; use "raise" better in Mandos server.
2478
            raise
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
2479
    
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2480
    if debug:
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2481
        # Enable all possible GnuTLS debugging
2482
        
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2483
        # "Use a log level over 10 to enable all debugging options."
2484
        # - GnuTLS manual
2485
        gnutls.library.functions.gnutls_global_set_log_level(11)
2486
        
2487
        @gnutls.library.types.gnutls_log_func
2488
        def debug_gnutls(level, string):
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2489
            logger.debug("GnuTLS: %s", string[:-1])
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2490
        
2491
        (gnutls.library.functions
2492
         .gnutls_global_set_log_function(debug_gnutls))
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2493
        
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
2494
        # 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
2495
        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
2496
        os.dup2(null, sys.stdin.fileno())
2497
        if null > 2:
2498
            os.close(null)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
2499
    
458 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2500
    # Need to fork before connecting to D-Bus
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
2501
    if not foreground:
458 by teddy at bsnet
* mandos (main): Bug fix: Fork before connecting to D-Bus.
2502
        # Close all input and output, do double fork, etc.
2503
        daemon()
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
2504
    
590 by Teddy Hogeborn
* mandos: Change comment.
2505
    # multiprocessing will use threads, so before we use gobject we
2506
    # 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)
2507
    gobject.threads_init()
2508
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2509
    global main_loop
2510
    # From the Avahi example code
560.1.1 by teddy at recompile
* mandos: Use os.devnull instead of os.path.devnull. Fix some white
2511
    DBusGMainLoop(set_as_default=True)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2512
    main_loop = gobject.MainLoop()
2513
    bus = dbus.SystemBus()
2514
    # End of Avahi example code
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
2515
    if use_dbus:
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2516
        try:
505 by Björn Påhlsson
The domain name has changed, so the D-Bus bus and interface names must
2517
            bus_name = dbus.service.BusName("se.recompile.Mandos",
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2518
                                            bus, do_not_queue=True)
505.1.3 by Teddy Hogeborn
* mandos: Break long lines.
2519
            old_bus_name = (dbus.service.BusName
2520
                            ("se.bsnet.fukt.Mandos", bus,
2521
                             do_not_queue=True))
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
2522
        except dbus.exceptions.NameExistsException as e:
574 by Teddy Hogeborn
* mandos: White space and other misc. format fixes only.
2523
            logger.error("Disabling D-Bus:", exc_info=e)
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2524
            use_dbus = False
463.1.4 by teddy at bsnet
* mandos: Use unicode string literals.
2525
            server_settings["use_dbus"] = False
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
2526
            tcp_server.use_dbus = False
707 by Teddy Hogeborn
mandos: New "--no-zeroconf" option. Also make "--socket=0" work.
2527
    if zeroconf:
2528
        protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2529
        service = AvahiServiceToSyslog(name =
2530
                                       server_settings["servicename"],
2531
                                       servicetype = "_mandos._tcp",
2532
                                       protocol = protocol, bus = bus)
2533
        if server_settings["interface"]:
2534
            service.interface = (if_nametoindex
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2535
                                 (server_settings["interface"]
2536
                                  .encode("utf-8")))
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()
729 by Teddy Hogeborn
mandos: Stop using str() and remove unnecessary unicode() calls.
2660
                    pidfile.write("{}\n".format(pid).encode("utf-8"))
606 by Teddy Hogeborn
* mandos: New "--foreground" option.
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()