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