/mandos/trunk

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