/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
441 by Teddy Hogeborn
* mandos (ClientDBus.__init__): Bug fix: Translate "-" in client names
726
        client_object_name = unicode(self.name).translate(
727
            {ord(u"."): ord(u"_"),
728
             ord(u"-"): ord(u"_")})
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
729
        self.dbus_object_path = (dbus.ObjectPath
441 by Teddy Hogeborn
* mandos (ClientDBus.__init__): Bug fix: Translate "-" in client names
730
                                 (u"/clients/" + client_object_name))
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
731
        DBusObjectWithProperties.__init__(self, self.bus,
732
                                          self.dbus_object_path)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
733
        
24.1.154 by Björn Påhlsson
merge
734
    def _get_approvals_pending(self):
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
735
        return self._approvals_pending
24.2.5 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Bug fix: Only send D-Bus
736
    def _set_approvals_pending(self, value):
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
737
        old_value = self._approvals_pending
738
        self._approvals_pending = value
739
        bval = bool(value)
24.2.5 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Bug fix: Only send D-Bus
740
        if (hasattr(self, "dbus_object_path")
24.1.154 by Björn Påhlsson
merge
741
            and bval is not bool(old_value)):
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
742
            dbus_bool = dbus.Boolean(bval, variant_level=1)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
743
            self.PropertyChanged(dbus.String(u"ApprovalPending"),
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
744
                                 dbus_bool)
24.1.154 by Björn Påhlsson
merge
745
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
746
    approvals_pending = property(_get_approvals_pending,
747
                                 _set_approvals_pending)
748
    del _get_approvals_pending, _set_approvals_pending
749
    
336 by Teddy Hogeborn
Code cleanup.
750
    @staticmethod
751
    def _datetime_to_dbus(dt, variant_level=0):
752
        """Convert a UTC datetime.datetime() to a D-Bus type."""
753
        return dbus.String(dt.isoformat(),
754
                           variant_level=variant_level)
755
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
756
    def enable(self):
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
757
        oldstate = getattr(self, u"enabled", False)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
758
        r = Client.enable(self)
759
        if oldstate != self.enabled:
760
            # Emit D-Bus signals
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
761
            self.PropertyChanged(dbus.String(u"Enabled"),
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
762
                                 dbus.Boolean(True, variant_level=1))
336 by Teddy Hogeborn
Code cleanup.
763
            self.PropertyChanged(
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
764
                dbus.String(u"LastEnabled"),
336 by Teddy Hogeborn
Code cleanup.
765
                self._datetime_to_dbus(self.last_enabled,
766
                                       variant_level=1))
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
767
        return r
768
    
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
769
    def disable(self, quiet = False):
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
770
        oldstate = getattr(self, u"enabled", False)
403 by Teddy Hogeborn
* mandos (ClientDBus.disable): Bug fix: complete rename of "log" and
771
        r = Client.disable(self, quiet=quiet)
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
772
        if not quiet and oldstate != self.enabled:
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
773
            # Emit D-Bus signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
774
            self.PropertyChanged(dbus.String(u"Enabled"),
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
775
                                 dbus.Boolean(False, variant_level=1))
776
        return r
777
    
778
    def __del__(self, *args, **kwargs):
779
        try:
780
            self.remove_from_connection()
329 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
781
        except LookupError:
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
782
            pass
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
783
        if hasattr(DBusObjectWithProperties, u"__del__"):
784
            DBusObjectWithProperties.__del__(self, *args, **kwargs)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
785
        Client.__del__(self, *args, **kwargs)
786
    
787
    def checker_callback(self, pid, condition, command,
788
                         *args, **kwargs):
789
        self.checker_callback_tag = None
790
        self.checker = None
791
        # Emit D-Bus signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
792
        self.PropertyChanged(dbus.String(u"CheckerRunning"),
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
793
                             dbus.Boolean(False, variant_level=1))
794
        if os.WIFEXITED(condition):
795
            exitstatus = os.WEXITSTATUS(condition)
796
            # Emit D-Bus signal
797
            self.CheckerCompleted(dbus.Int16(exitstatus),
798
                                  dbus.Int64(condition),
799
                                  dbus.String(command))
800
        else:
801
            # Emit D-Bus signal
802
            self.CheckerCompleted(dbus.Int16(-1),
803
                                  dbus.Int64(condition),
804
                                  dbus.String(command))
805
        
806
        return Client.checker_callback(self, pid, condition, command,
807
                                       *args, **kwargs)
808
    
809
    def checked_ok(self, *args, **kwargs):
810
        r = Client.checked_ok(self, *args, **kwargs)
811
        # Emit D-Bus signal
812
        self.PropertyChanged(
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
813
            dbus.String(u"LastCheckedOK"),
336 by Teddy Hogeborn
Code cleanup.
814
            (self._datetime_to_dbus(self.last_checked_ok,
815
                                    variant_level=1)))
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
816
        return r
817
    
818
    def start_checker(self, *args, **kwargs):
819
        old_checker = self.checker
820
        if self.checker is not None:
821
            old_checker_pid = self.checker.pid
822
        else:
823
            old_checker_pid = None
824
        r = Client.start_checker(self, *args, **kwargs)
329 by Teddy Hogeborn
* mandos (ClientDBus.__del__): Bug fix: Correct mispasted code, and do
825
        # Only if new checker process was started
826
        if (self.checker is not None
827
            and old_checker_pid != self.checker.pid):
828
            # Emit D-Bus signal
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
829
            self.CheckerStarted(self.current_checker_command)
830
            self.PropertyChanged(
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
831
                dbus.String(u"CheckerRunning"),
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
832
                dbus.Boolean(True, variant_level=1))
833
        return r
834
    
835
    def stop_checker(self, *args, **kwargs):
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
836
        old_checker = getattr(self, u"checker", None)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
837
        r = Client.stop_checker(self, *args, **kwargs)
838
        if (old_checker is not None
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
839
            and getattr(self, u"checker", None) is None):
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
840
            self.PropertyChanged(dbus.String(u"CheckerRunning"),
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
841
                                 dbus.Boolean(False, variant_level=1))
842
        return r
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
843
844
    def _reset_approved(self):
845
        self._approved = None
846
        return False
847
    
848
    def approve(self, value=True):
24.1.154 by Björn Påhlsson
merge
849
        self.send_changedstate()
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
850
        self._approved = value
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
851
        gobject.timeout_add(self._timedelta_to_milliseconds
852
                            (self.approval_duration),
24.1.154 by Björn Påhlsson
merge
853
                            self._reset_approved)
24.2.1 by teddy at bsnet
* debian/control (mandos/Depends): Added "python-urwid".
854
    
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
855
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
856
    ## D-Bus methods, signals & properties
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
857
    _interface = u"se.bsnet.fukt.Mandos.Client"
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
858
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
859
    ## Signals
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
860
    
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
861
    # CheckerCompleted - signal
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
862
    @dbus.service.signal(_interface, signature=u"nxs")
280 by Teddy Hogeborn
* mandos (Client.CheckerCompleted): Changed signature to "nxs"; return
863
    def CheckerCompleted(self, exitcode, waitstatus, command):
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
864
        "D-Bus signal"
865
        pass
866
    
867
    # CheckerStarted - signal
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
868
    @dbus.service.signal(_interface, signature=u"s")
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
869
    def CheckerStarted(self, command):
870
        "D-Bus signal"
871
        pass
872
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
873
    # PropertyChanged - signal
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
874
    @dbus.service.signal(_interface, signature=u"sv")
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
875
    def PropertyChanged(self, property, value):
876
        "D-Bus signal"
877
        pass
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
878
    
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
879
    # GotSecret - signal
327 by Teddy Hogeborn
Merge from pipe IPC branch.
880
    @dbus.service.signal(_interface)
387 by Teddy Hogeborn
* mandos (ClientDBus.ReceivedSecret): Renamed to "GotSecret". All
881
    def GotSecret(self):
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
882
        """D-Bus signal
883
        Is sent after a successful transfer of secret from the Mandos
884
        server to mandos-client
885
        """
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
886
        pass
327 by Teddy Hogeborn
Merge from pipe IPC branch.
887
    
888
    # Rejected - signal
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
889
    @dbus.service.signal(_interface, signature=u"s")
890
    def Rejected(self, reason):
891
        "D-Bus signal"
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
892
        pass
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
893
    
894
    # NeedApproval - signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
895
    @dbus.service.signal(_interface, signature=u"tb")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
896
    def NeedApproval(self, timeout, default):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
897
        "D-Bus signal"
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
898
        pass
327 by Teddy Hogeborn
Merge from pipe IPC branch.
899
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
900
    ## Methods
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
901
902
    # Approve - method
903
    @dbus.service.method(_interface, in_signature=u"b")
904
    def Approve(self, value):
905
        self.approve(value)
906
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
907
    # CheckedOK - method
908
    @dbus.service.method(_interface)
909
    def CheckedOK(self):
910
        return self.checked_ok()
911
    
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
912
    # Enable - method
913
    @dbus.service.method(_interface)
914
    def Enable(self):
915
        "D-Bus method"
916
        self.enable()
917
    
918
    # StartChecker - method
919
    @dbus.service.method(_interface)
920
    def StartChecker(self):
921
        "D-Bus method"
922
        self.start_checker()
923
    
924
    # Disable - method
925
    @dbus.service.method(_interface)
926
    def Disable(self):
927
        "D-Bus method"
928
        self.disable()
929
    
930
    # StopChecker - method
931
    @dbus.service.method(_interface)
932
    def StopChecker(self):
933
        self.stop_checker()
934
    
24.1.147 by Björn Påhlsson
The IPC pipes are now file objects, not file descriptors:
935
    ## Properties
936
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
937
    # ApprovalPending - property
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
938
    @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;
939
    def ApprovalPending_dbus_property(self):
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
940
        return dbus.Boolean(bool(self.approvals_pending))
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
941
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
942
    # ApprovedByDefault - property
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
943
    @dbus_service_property(_interface, signature=u"b",
944
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
945
    def ApprovedByDefault_dbus_property(self, value=None):
946
        if value is None:       # get
947
            return dbus.Boolean(self.approved_by_default)
948
        self.approved_by_default = bool(value)
949
        # Emit D-Bus signal
950
        self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
951
                             dbus.Boolean(value, variant_level=1))
952
    
953
    # ApprovalDelay - property
954
    @dbus_service_property(_interface, signature=u"t",
955
                           access=u"readwrite")
956
    def ApprovalDelay_dbus_property(self, value=None):
957
        if value is None:       # get
958
            return dbus.UInt64(self.approval_delay_milliseconds())
959
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
960
        # Emit D-Bus signal
961
        self.PropertyChanged(dbus.String(u"ApprovalDelay"),
962
                             dbus.UInt64(value, variant_level=1))
963
    
964
    # ApprovalDuration - property
965
    @dbus_service_property(_interface, signature=u"t",
966
                           access=u"readwrite")
967
    def ApprovalDuration_dbus_property(self, value=None):
968
        if value is None:       # get
969
            return dbus.UInt64(self._timedelta_to_milliseconds(
970
                    self.approval_duration))
971
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
972
        # Emit D-Bus signal
973
        self.PropertyChanged(dbus.String(u"ApprovalDuration"),
974
                             dbus.UInt64(value, variant_level=1))
975
    
976
    # Name - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
977
    @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;
978
    def Name_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
979
        return dbus.String(self.name)
980
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
981
    # Fingerprint - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
982
    @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;
983
    def Fingerprint_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
984
        return dbus.String(self.fingerprint)
985
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
986
    # Host - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
987
    @dbus_service_property(_interface, signature=u"s",
988
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
989
    def Host_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
990
        if value is None:       # get
991
            return dbus.String(self.host)
992
        self.host = value
993
        # Emit D-Bus signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
994
        self.PropertyChanged(dbus.String(u"Host"),
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
995
                             dbus.String(value, variant_level=1))
996
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
997
    # Created - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
998
    @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;
999
    def Created_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1000
        return dbus.String(self._datetime_to_dbus(self.created))
1001
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1002
    # LastEnabled - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1003
    @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;
1004
    def LastEnabled_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1005
        if self.last_enabled is None:
1006
            return dbus.String(u"")
1007
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
1008
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1009
    # Enabled - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1010
    @dbus_service_property(_interface, signature=u"b",
1011
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1012
    def Enabled_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1013
        if value is None:       # get
1014
            return dbus.Boolean(self.enabled)
1015
        if value:
1016
            self.enable()
1017
        else:
1018
            self.disable()
1019
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1020
    # LastCheckedOK - property
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1021
    @dbus_service_property(_interface, signature=u"s",
1022
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1023
    def LastCheckedOK_dbus_property(self, value=None):
381 by Teddy Hogeborn
Restore some poor D-Bus methods who got a bit hastily deleted. Enable
1024
        if value is not None:
1025
            self.checked_ok()
1026
            return
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1027
        if self.last_checked_ok is None:
1028
            return dbus.String(u"")
1029
        return dbus.String(self._datetime_to_dbus(self
1030
                                                  .last_checked_ok))
1031
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1032
    # Timeout - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1033
    @dbus_service_property(_interface, signature=u"t",
1034
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1035
    def Timeout_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1036
        if value is None:       # get
1037
            return dbus.UInt64(self.timeout_milliseconds())
1038
        self.timeout = datetime.timedelta(0, 0, 0, value)
1039
        # Emit D-Bus signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1040
        self.PropertyChanged(dbus.String(u"Timeout"),
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1041
                             dbus.UInt64(value, variant_level=1))
1042
        if getattr(self, u"disable_initiator_tag", None) is None:
1043
            return
1044
        # Reschedule timeout
1045
        gobject.source_remove(self.disable_initiator_tag)
1046
        self.disable_initiator_tag = None
1047
        time_to_die = (self.
1048
                       _timedelta_to_milliseconds((self
1049
                                                   .last_checked_ok
1050
                                                   + self.timeout)
1051
                                                  - datetime.datetime
1052
                                                  .utcnow()))
1053
        if time_to_die <= 0:
1054
            # The timeout has passed
1055
            self.disable()
1056
        else:
1057
            self.disable_initiator_tag = (gobject.timeout_add
1058
                                          (time_to_die, self.disable))
1059
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1060
    # Interval - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1061
    @dbus_service_property(_interface, signature=u"t",
1062
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1063
    def Interval_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1064
        if value is None:       # get
1065
            return dbus.UInt64(self.interval_milliseconds())
1066
        self.interval = datetime.timedelta(0, 0, 0, value)
1067
        # Emit D-Bus signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1068
        self.PropertyChanged(dbus.String(u"Interval"),
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1069
                             dbus.UInt64(value, variant_level=1))
1070
        if getattr(self, u"checker_initiator_tag", None) is None:
1071
            return
1072
        # Reschedule checker run
1073
        gobject.source_remove(self.checker_initiator_tag)
1074
        self.checker_initiator_tag = (gobject.timeout_add
1075
                                      (value, self.start_checker))
1076
        self.start_checker()    # Start one now, too
1077
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1078
    # Checker - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1079
    @dbus_service_property(_interface, signature=u"s",
1080
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1081
    def Checker_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1082
        if value is None:       # get
1083
            return dbus.String(self.checker_command)
1084
        self.checker_command = value
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1085
        # Emit D-Bus signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1086
        self.PropertyChanged(dbus.String(u"Checker"),
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1087
                             dbus.String(self.checker_command,
1088
                                         variant_level=1))
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1089
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1090
    # CheckerRunning - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1091
    @dbus_service_property(_interface, signature=u"b",
1092
                           access=u"readwrite")
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1093
    def CheckerRunning_dbus_property(self, value=None):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1094
        if value is None:       # get
1095
            return dbus.Boolean(self.checker is not None)
1096
        if value:
1097
            self.start_checker()
1098
        else:
1099
            self.stop_checker()
1100
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1101
    # ObjectPath - property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1102
    @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;
1103
    def ObjectPath_dbus_property(self):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1104
        return self.dbus_object_path # is already a dbus.ObjectPath
1105
    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1106
    # Secret = property
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1107
    @dbus_service_property(_interface, signature=u"ay",
1108
                           access=u"write", byte_arrays=True)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1109
    def Secret_dbus_property(self, value):
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1110
        self.secret = str(value)
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1111
    
1112
    del _interface
3 by Björn Påhlsson
Python based server
1113
1114
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1115
class ProxyClient(object):
1116
    def __init__(self, child_pipe, fpr, address):
1117
        self._pipe = child_pipe
1118
        self._pipe.send(('init', fpr, address))
1119
        if not self._pipe.recv():
1120
            raise KeyError()
1121
1122
    def __getattribute__(self, name):
1123
        if(name == '_pipe'):
1124
            return super(ProxyClient, self).__getattribute__(name)
1125
        self._pipe.send(('getattr', name))
1126
        data = self._pipe.recv()
1127
        if data[0] == 'data':
1128
            return data[1]
1129
        if data[0] == 'function':
1130
            def func(*args, **kwargs):
1131
                self._pipe.send(('funcall', name, args, kwargs))
1132
                return self._pipe.recv()[1]
1133
            return func
1134
1135
    def __setattr__(self, name, value):
1136
        if(name == '_pipe'):
1137
            return super(ProxyClient, self).__setattr__(name, value)
1138
        self._pipe.send(('setattr', name, value))
1139
1140
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1141
class ClientHandler(socketserver.BaseRequestHandler, object):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1142
    """A class to handle client connections.
1143
    
1144
    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
1145
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
1146
    
3 by Björn Påhlsson
Python based server
1147
    def handle(self):
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1148
        with contextlib.closing(self.server.child_pipe) as child_pipe:
1149
            logger.info(u"TCP connection from: %s",
1150
                        unicode(self.client_address))
1151
            logger.debug(u"Pipe FD: %d",
1152
                         self.server.child_pipe.fileno())
1153
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1154
            session = (gnutls.connection
1155
                       .ClientSession(self.request,
1156
                                      gnutls.connection
1157
                                      .X509Credentials()))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1158
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1159
            # Note: gnutls.connection.X509Credentials is really a
1160
            # generic GnuTLS certificate credentials object so long as
1161
            # no X.509 keys are added to it.  Therefore, we can use it
1162
            # here despite using OpenPGP certificates.
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1163
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1164
            #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1165
            #                      u"+AES-256-CBC", u"+SHA1",
1166
            #                      u"+COMP-NULL", u"+CTYPE-OPENPGP",
1167
            #                      u"+DHE-DSS"))
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1168
            # Use a fallback default, since this MUST be set.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1169
            priority = self.server.gnutls_priority
1170
            if priority is None:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1171
                priority = u"NORMAL"
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1172
            (gnutls.library.functions
1173
             .gnutls_priority_set_direct(session._c_object,
1174
                                         priority, None))
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1175
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1176
            # Start communication using the Mandos protocol
1177
            # Get protocol number
1178
            line = self.request.makefile().readline()
1179
            logger.debug(u"Protocol version: %r", line)
1180
            try:
1181
                if int(line.strip().split()[0]) > 1:
1182
                    raise RuntimeError
1183
            except (ValueError, IndexError, RuntimeError), error:
1184
                logger.error(u"Unknown protocol version: %s", error)
1185
                return
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1186
416.1.2 by Teddy Hogeborn
* mandos (ClientHandler.handle): Set up the GnuTLS session object
1187
            # Start GnuTLS connection
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1188
            try:
1189
                session.handshake()
1190
            except gnutls.errors.GNUTLSError, error:
1191
                logger.warning(u"Handshake failed: %s", error)
1192
                # Do not run session.bye() here: the session is not
1193
                # established.  Just abandon the request.
1194
                return
1195
            logger.debug(u"Handshake succeeded")
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1196
1197
            approval_required = False
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1198
            try:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1199
                try:
1200
                    fpr = self.fingerprint(self.peer_certificate
1201
                                           (session))
1202
                except (TypeError, gnutls.errors.GNUTLSError), error:
1203
                    logger.warning(u"Bad certificate: %s", error)
1204
                    return
1205
                logger.debug(u"Fingerprint: %s", fpr)
1206
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1207
                try:
1208
                    client = ProxyClient(child_pipe, fpr,
1209
                                         self.client_address)
1210
                except KeyError:
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1211
                    return
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1212
                
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1213
                if client.approval_delay:
1214
                    delay = client.approval_delay
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1215
                    client.approvals_pending += 1
1216
                    approval_required = True
1217
                
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
1218
                while True:
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1219
                    if not client.enabled:
1220
                        logger.warning(u"Client %s is disabled",
1221
                                       client.name)
1222
                        if self.server.use_dbus:
1223
                            # Emit D-Bus signal
1224
                            client.Rejected("Disabled")                    
1225
                        return
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1226
                    
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1227
                    if client._approved or not client.approval_delay:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1228
                        #We are approved or approval is disabled
1229
                        break
1230
                    elif client._approved is None:
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1231
                        logger.info(u"Client %s needs approval",
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1232
                                    client.name)
1233
                        if self.server.use_dbus:
1234
                            # Emit D-Bus signal
1235
                            client.NeedApproval(
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1236
                                client.approval_delay_milliseconds(),
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1237
                                client.approved_by_default)
1238
                    else:
1239
                        logger.warning(u"Client %s was not approved",
1240
                                       client.name)
1241
                        if self.server.use_dbus:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1242
                            # Emit D-Bus signal
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1243
                            client.Rejected("Denied")
24.1.148 by Björn Påhlsson
half working on-demand password and approved code
1244
                        return
1245
                    
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1246
                    #wait until timeout or approved
1247
                    #x = float(client._timedelta_to_milliseconds(delay))
1248
                    time = datetime.datetime.now()
1249
                    client.changedstate.acquire()
1250
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1251
                    client.changedstate.release()
1252
                    time2 = datetime.datetime.now()
1253
                    if (time2 - time) >= delay:
1254
                        if not client.approved_by_default:
1255
                            logger.warning("Client %s timed out while"
1256
                                           " waiting for approval",
1257
                                           client.name)
1258
                            if self.server.use_dbus:
1259
                                # Emit D-Bus signal
24.1.163 by Björn Påhlsson
mandos-client: Added never ending loop for --connect
1260
                                client.Rejected("Approval timed out")
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1261
                            return
1262
                        else:
1263
                            break
1264
                    else:
1265
                        delay -= time2 - time
1266
                
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1267
                sent_size = 0
1268
                while sent_size < len(client.secret):
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1269
                    try:
1270
                        sent = session.send(client.secret[sent_size:])
1271
                    except (gnutls.errors.GNUTLSError), error:
1272
                        logger.warning("gnutls send failed")
1273
                        return
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1274
                    logger.debug(u"Sent: %d, remaining: %d",
1275
                                 sent, len(client.secret)
1276
                                 - (sent_size + sent))
1277
                    sent_size += sent
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1278
1279
                logger.info(u"Sending secret to %s", client.name)
1280
                # bump the timeout as if seen
1281
                client.checked_ok()
1282
                if self.server.use_dbus:
1283
                    # Emit D-Bus signal
1284
                    client.GotSecret()
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1285
            
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1286
            finally:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1287
                if approval_required:
1288
                    client.approvals_pending -= 1
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1289
                try:
1290
                    session.bye()
1291
                except (gnutls.errors.GNUTLSError), error:
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1292
                    logger.warning("GnuTLS bye failed")
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1293
    
1294
    @staticmethod
1295
    def peer_certificate(session):
1296
        "Return the peer's OpenPGP certificate as a bytestring"
1297
        # If not an OpenPGP certificate...
1298
        if (gnutls.library.functions
1299
            .gnutls_certificate_type_get(session._c_object)
1300
            != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1301
            # ...do the normal thing
1302
            return session.peer_certificate
1303
        list_size = ctypes.c_uint(1)
1304
        cert_list = (gnutls.library.functions
1305
                     .gnutls_certificate_get_peers
1306
                     (session._c_object, ctypes.byref(list_size)))
1307
        if not bool(cert_list) and list_size.value != 0:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1308
            raise gnutls.errors.GNUTLSError(u"error getting peer"
1309
                                            u" certificate")
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1310
        if list_size.value == 0:
1311
            return None
1312
        cert = cert_list[0]
1313
        return ctypes.string_at(cert.data, cert.size)
1314
    
1315
    @staticmethod
1316
    def fingerprint(openpgp):
1317
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
1318
        # New GnuTLS "datum" with the OpenPGP public key
1319
        datum = (gnutls.library.types
1320
                 .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1321
                                             ctypes.POINTER
1322
                                             (ctypes.c_ubyte)),
1323
                                 ctypes.c_uint(len(openpgp))))
1324
        # New empty GnuTLS certificate
1325
        crt = gnutls.library.types.gnutls_openpgp_crt_t()
1326
        (gnutls.library.functions
1327
         .gnutls_openpgp_crt_init(ctypes.byref(crt)))
1328
        # Import the OpenPGP public key into the certificate
1329
        (gnutls.library.functions
1330
         .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1331
                                    gnutls.library.constants
1332
                                    .GNUTLS_OPENPGP_FMT_RAW))
1333
        # Verify the self signature in the key
1334
        crtverify = ctypes.c_uint()
1335
        (gnutls.library.functions
1336
         .gnutls_openpgp_crt_verify_self(crt, 0,
1337
                                         ctypes.byref(crtverify)))
1338
        if crtverify.value != 0:
1339
            gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1340
            raise (gnutls.errors.CertificateSecurityError
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1341
                   (u"Verify failed"))
330 by Teddy Hogeborn
* mandos (peer_certificate, fingerprint): Moved into "TCP_handler"
1342
        # New buffer for the fingerprint
1343
        buf = ctypes.create_string_buffer(20)
1344
        buf_len = ctypes.c_size_t()
1345
        # Get the fingerprint from the certificate into the buffer
1346
        (gnutls.library.functions
1347
         .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1348
                                             ctypes.byref(buf_len)))
1349
        # Deinit the certificate
1350
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1351
        # Convert the buffer to a Python bytestring
1352
        fpr = ctypes.string_at(buf, buf_len.value)
1353
        # Convert the bytestring to hexadecimal notation
1354
        hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1355
        return hex_fpr
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1356
1357
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1358
class MultiprocessingMixIn(object):
1359
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
1360
    def sub_process_main(self, request, address):
1361
        try:
1362
            self.finish_request(request, address)
1363
        except:
1364
            self.handle_error(request, address)
1365
        self.close_request(request)
1366
            
1367
    def process_request(self, request, address):
1368
        """Start a new process to process the request."""
1369
        multiprocessing.Process(target = self.sub_process_main,
1370
                                args = (request, address)).start()
1371
1372
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1373
    """ adds a pipe to the MixIn """
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1374
    def process_request(self, request, client_address):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1375
        """Overrides and wraps the original process_request().
1376
        
355 by Teddy Hogeborn
* mandos: White-space fixes only.
1377
        This function creates a new pipe in self.pipe
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1378
        """
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1379
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1380
1381
        super(MultiprocessingMixInWithPipe,
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1382
              self).process_request(request, client_address)
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
1383
        self.child_pipe.close()
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1384
        self.add_pipe(parent_pipe)
24.1.154 by Björn Påhlsson
merge
1385
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1386
    def add_pipe(self, parent_pipe):
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1387
        """Dummy function; override as necessary"""
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1388
        pass
1389
1390
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1391
                     socketserver.TCPServer, object):
237.2.13 by Teddy Hogeborn
Merge from trunk. Notable changes:
1392
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1393
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1394
    Attributes:
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1395
        enabled:        Boolean; whether this server is activated yet
1396
        interface:      None or a network interface name (string)
1397
        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
1398
    """
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1399
    def __init__(self, server_address, RequestHandlerClass,
339 by Teddy Hogeborn
Code cleanup.
1400
                 interface=None, use_ipv6=True):
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1401
        self.interface = interface
1402
        if use_ipv6:
1403
            self.address_family = socket.AF_INET6
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1404
        socketserver.TCPServer.__init__(self, server_address,
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1405
                                        RequestHandlerClass)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1406
    def server_bind(self):
1407
        """This overrides the normal server_bind() function
1408
        to bind to an interface if one was specified, and also NOT to
1409
        bind to an address or port if they were not specified."""
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1410
        if self.interface is not None:
338 by Teddy Hogeborn
* mandos: Minor doc string fixes.
1411
            if SO_BINDTODEVICE is None:
1412
                logger.error(u"SO_BINDTODEVICE does not exist;"
1413
                             u" cannot bind to interface %s",
1414
                             self.interface)
1415
            else:
1416
                try:
1417
                    self.socket.setsockopt(socket.SOL_SOCKET,
1418
                                           SO_BINDTODEVICE,
1419
                                           str(self.interface
1420
                                               + u'\0'))
1421
                except socket.error, error:
1422
                    if error[0] == errno.EPERM:
1423
                        logger.error(u"No permission to"
1424
                                     u" bind to interface %s",
1425
                                     self.interface)
1426
                    elif error[0] == errno.ENOPROTOOPT:
1427
                        logger.error(u"SO_BINDTODEVICE not available;"
1428
                                     u" cannot bind to interface %s",
1429
                                     self.interface)
1430
                    else:
1431
                        raise
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1432
        # Only bind(2) the socket if we really need to.
1433
        if self.server_address[0] or self.server_address[1]:
1434
            if not self.server_address[0]:
314 by Teddy Hogeborn
Support not using IPv6 in server:
1435
                if self.address_family == socket.AF_INET6:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1436
                    any_address = u"::" # in6addr_any
314 by Teddy Hogeborn
Support not using IPv6 in server:
1437
                else:
1438
                    any_address = socket.INADDR_ANY
1439
                self.server_address = (any_address,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1440
                                       self.server_address[1])
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
1441
            elif not self.server_address[1]:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1442
                self.server_address = (self.server_address[0],
1443
                                       0)
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1444
#                 if self.interface:
49 by Teddy Hogeborn
* mandos (IPv6_TCPServer.server_bind): Bug fix: allow port to be empty
1445
#                     self.server_address = (self.server_address[0],
1446
#                                            0, # port
1447
#                                            0, # flowinfo
1448
#                                            if_nametoindex
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1449
#                                            (self.interface))
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1450
            return socketserver.TCPServer.server_bind(self)
339 by Teddy Hogeborn
Code cleanup.
1451
1452
1453
class MandosServer(IPv6_TCPServer):
1454
    """Mandos server.
1455
    
1456
    Attributes:
1457
        clients:        set of Client objects
1458
        gnutls_priority GnuTLS priority string
1459
        use_dbus:       Boolean; to emit D-Bus signals or not
340 by Teddy Hogeborn
Code cleanup.
1460
    
1461
    Assumes a gobject.MainLoop event loop.
339 by Teddy Hogeborn
Code cleanup.
1462
    """
1463
    def __init__(self, server_address, RequestHandlerClass,
1464
                 interface=None, use_ipv6=True, clients=None,
1465
                 gnutls_priority=None, use_dbus=True):
1466
        self.enabled = False
1467
        self.clients = clients
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1468
        if self.clients is None:
1469
            self.clients = set()
339 by Teddy Hogeborn
Code cleanup.
1470
        self.use_dbus = use_dbus
1471
        self.gnutls_priority = gnutls_priority
1472
        IPv6_TCPServer.__init__(self, server_address,
1473
                                RequestHandlerClass,
1474
                                interface = interface,
1475
                                use_ipv6 = use_ipv6)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1476
    def server_activate(self):
1477
        if self.enabled:
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1478
            return socketserver.TCPServer.server_activate(self)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1479
    def enable(self):
1480
        self.enabled = True
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1481
    def add_pipe(self, parent_pipe):
340 by Teddy Hogeborn
Code cleanup.
1482
        # Call "handle_ipc" for both data and EOF events
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1483
        gobject.io_add_watch(parent_pipe.fileno(),
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1484
                             gobject.IO_IN | gobject.IO_HUP,
1485
                             functools.partial(self.handle_ipc,
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1486
                                               parent_pipe = parent_pipe))
1487
        
1488
    def handle_ipc(self, source, condition, parent_pipe=None,
1489
                   client_object=None):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1490
        condition_names = {
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1491
            gobject.IO_IN: u"IN",   # There is data to read.
1492
            gobject.IO_OUT: u"OUT", # Data can be written (without
1493
                                    # blocking).
1494
            gobject.IO_PRI: u"PRI", # There is urgent data to read.
1495
            gobject.IO_ERR: u"ERR", # Error condition.
1496
            gobject.IO_HUP: u"HUP"  # Hung up (the connection has been
1497
                                    # broken, usually for pipes and
1498
                                    # sockets).
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1499
            }
1500
        conditions_string = ' | '.join(name
1501
                                       for cond, name in
1502
                                       condition_names.iteritems()
1503
                                       if cond & condition)
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1504
        # error or the other end of multiprocessing.Pipe has closed
1505
        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
1506
            return False
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1507
        
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1508
        # Read a request from the child
1509
        request = parent_pipe.recv()
1510
        command = request[0]
1511
        
1512
        if command == 'init':
1513
            fpr = request[1]
1514
            address = request[2]
1515
            
1516
            for c in self.clients:
1517
                if c.fingerprint == fpr:
1518
                    client = c
1519
                    break
1520
            else:
1521
                logger.warning(u"Client not found for fingerprint: %s, ad"
1522
                               u"dress: %s", fpr, address)
1523
                if self.use_dbus:
1524
                    # Emit D-Bus signal
441 by Teddy Hogeborn
* mandos (ClientDBus.__init__): Bug fix: Translate "-" in client names
1525
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1526
                parent_pipe.send(False)
1527
                return False
1528
            
1529
            gobject.io_add_watch(parent_pipe.fileno(),
1530
                                 gobject.IO_IN | gobject.IO_HUP,
1531
                                 functools.partial(self.handle_ipc,
1532
                                                   parent_pipe = parent_pipe,
1533
                                                   client_object = client))
1534
            parent_pipe.send(True)
1535
            # 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.
1536
            return False
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1537
        if command == 'funcall':
1538
            funcname = request[1]
1539
            args = request[2]
1540
            kwargs = request[3]
1541
            
1542
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1543
1544
        if command == 'getattr':
1545
            attrname = request[1]
1546
            if callable(client_object.__getattribute__(attrname)):
1547
                parent_pipe.send(('function',))
1548
            else:
1549
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
24.2.4 by teddy at bsnet
* mandos (ClientDBus.approvals_pending): Changed to be a property
1550
        
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1551
        if command == 'setattr':
1552
            attrname = request[1]
1553
            value = request[2]
1554
            setattr(client_object, attrname, value)
24.1.154 by Björn Påhlsson
merge
1555
288.1.1 by Teddy Hogeborn
Start of new pipe-based IPC mechanism.
1556
        return True
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
1557
3 by Björn Påhlsson
Python based server
1558
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1559
def string_to_delta(interval):
1560
    """Parse a string and return a datetime.timedelta
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1561
    
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1562
    >>> string_to_delta(u'7d')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1563
    datetime.timedelta(7)
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1564
    >>> string_to_delta(u'60s')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1565
    datetime.timedelta(0, 60)
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1566
    >>> string_to_delta(u'60m')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1567
    datetime.timedelta(0, 3600)
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1568
    >>> string_to_delta(u'24h')
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1569
    datetime.timedelta(1)
1570
    >>> string_to_delta(u'1w')
1571
    datetime.timedelta(7)
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1572
    >>> string_to_delta(u'5m 30s')
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
1573
    datetime.timedelta(0, 330)
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1574
    """
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
1575
    timevalue = datetime.timedelta(0)
1576
    for s in interval.split():
1577
        try:
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1578
            suffix = unicode(s[-1])
1579
            value = int(s[:-1])
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
1580
            if suffix == u"d":
1581
                delta = datetime.timedelta(value)
1582
            elif suffix == u"s":
1583
                delta = datetime.timedelta(0, value)
1584
            elif suffix == u"m":
1585
                delta = datetime.timedelta(0, 0, 0, 0, value)
1586
            elif suffix == u"h":
1587
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
1588
            elif suffix == u"w":
1589
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1590
            else:
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
1591
                raise ValueError(u"Unknown suffix %r" % suffix)
1592
        except (ValueError, IndexError), e:
1593
            raise ValueError(e.message)
93 by Teddy Hogeborn
* mandos (string_to_delta): Accept a whitespace-separated sequence of
1594
        timevalue += delta
1595
    return timevalue
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1596
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
1597
24.1.13 by Björn Påhlsson
mandosclient
1598
def if_nametoindex(interface):
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1599
    """Call the C function if_nametoindex(), or equivalent
1600
    
1601
    Note: This function cannot accept a unicode string."""
24.1.13 by Björn Påhlsson
mandosclient
1602
    global if_nametoindex
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1603
    try:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1604
        if_nametoindex = (ctypes.cdll.LoadLibrary
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1605
                          (ctypes.util.find_library(u"c"))
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1606
                          .if_nametoindex)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
1607
    except (OSError, AttributeError):
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1608
        logger.warning(u"Doing if_nametoindex the hard way")
24.1.13 by Björn Påhlsson
mandosclient
1609
        def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
1610
            "Get an interface index the hard way, i.e. using fcntl()"
1611
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1612
            with contextlib.closing(socket.socket()) as s:
237 by Teddy Hogeborn
* mandos: Also import "with_statement" and "absolute_import" from
1613
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1614
                                    struct.pack(str(u"16s16x"),
1615
                                                interface))
1616
            interface_index = struct.unpack(str(u"I"),
1617
                                            ifreq[16:20])[0]
28 by Teddy Hogeborn
* server.conf: New file.
1618
            return interface_index
24.1.13 by Björn Påhlsson
mandosclient
1619
    return if_nametoindex(interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1620
1621
47 by Teddy Hogeborn
* plugbasedclient.c: Renamed to "mandos-client.c". All users changed.
1622
def daemon(nochdir = False, noclose = False):
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1623
    """See daemon(3).  Standard BSD Unix function.
331 by Teddy Hogeborn
Minor code cleanup, and a bug fix.
1624
    
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1625
    This should really exist as os.daemon, but it doesn't (yet)."""
1626
    if os.fork():
1627
        sys.exit()
1628
    os.setsid()
1629
    if not nochdir:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1630
        os.chdir(u"/")
46 by Teddy Hogeborn
* network-protocol.txt: New.
1631
    if os.fork():
1632
        sys.exit()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1633
    if not noclose:
1634
        # Close all standard open file descriptors
28 by Teddy Hogeborn
* server.conf: New file.
1635
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1636
        if not stat.S_ISCHR(os.fstat(null).st_mode):
1637
            raise OSError(errno.ENODEV,
388 by Teddy Hogeborn
* mandos (daemon): Use "os.path.devnull" in the error message.
1638
                          u"%s not a character device"
1639
                          % os.path.devnull)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1640
        os.dup2(null, sys.stdin.fileno())
1641
        os.dup2(null, sys.stdout.fileno())
1642
        os.dup2(null, sys.stderr.fileno())
1643
        if null > 2:
1644
            os.close(null)
1645
1646
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1647
def main():
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1648
    
379 by Teddy Hogeborn
* mandos: Fix line lengths.
1649
    ##################################################################
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1650
    # Parsing of options, both command line and config file
1651
    
237.2.1 by Teddy Hogeborn
Merge from trunk, but disable the unfinished D-Bus feature:
1652
    parser = optparse.OptionParser(version = "%%prog %s" % version)
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1653
    parser.add_option("-i", u"--interface", type=u"string",
1654
                      metavar="IF", help=u"Bind to interface IF")
1655
    parser.add_option("-a", u"--address", type=u"string",
1656
                      help=u"Address to listen for requests on")
1657
    parser.add_option("-p", u"--port", type=u"int",
1658
                      help=u"Port number to receive requests on")
1659
    parser.add_option("--check", action=u"store_true",
1660
                      help=u"Run self-test")
1661
    parser.add_option("--debug", action=u"store_true",
1662
                      help=u"Debug mode; run in foreground and log to"
1663
                      u" terminal")
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1664
    parser.add_option("--debuglevel", type=u"string", metavar="Level",
1665
                      help=u"Debug level for stdout output")
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1666
    parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1667
                      u" priority string (see GnuTLS documentation)")
1668
    parser.add_option("--servicename", type=u"string",
1669
                      metavar=u"NAME", help=u"Zeroconf service name")
1670
    parser.add_option("--configdir", type=u"string",
1671
                      default=u"/etc/mandos", metavar=u"DIR",
1672
                      help=u"Directory to search for configuration"
1673
                      u" files")
1674
    parser.add_option("--no-dbus", action=u"store_false",
1675
                      dest=u"use_dbus", help=u"Do not provide D-Bus"
1676
                      u" system bus interface")
1677
    parser.add_option("--no-ipv6", action=u"store_false",
1678
                      dest=u"use_ipv6", help=u"Do not use IPv6")
215 by Teddy Hogeborn
* mandos: Remove unused "select" module. Import "ctypes.util".
1679
    options = parser.parse_args()[0]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1680
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
1681
    if options.check:
1682
        import doctest
1683
        doctest.testmod()
1684
        sys.exit()
3 by Björn Påhlsson
Python based server
1685
    
28 by Teddy Hogeborn
* server.conf: New file.
1686
    # Default values for config file for server-global settings
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1687
    server_defaults = { u"interface": u"",
1688
                        u"address": u"",
1689
                        u"port": u"",
1690
                        u"debug": u"False",
1691
                        u"priority":
1692
                        u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1693
                        u"servicename": u"Mandos",
1694
                        u"use_dbus": u"True",
1695
                        u"use_ipv6": u"True",
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1696
                        u"debuglevel": u"",
28 by Teddy Hogeborn
* server.conf: New file.
1697
                        }
1698
    
1699
    # Parse config file for server-global settings
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1700
    server_config = configparser.SafeConfigParser(server_defaults)
28 by Teddy Hogeborn
* server.conf: New file.
1701
    del server_defaults
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1702
    server_config.read(os.path.join(options.configdir,
1703
                                    u"mandos.conf"))
28 by Teddy Hogeborn
* server.conf: New file.
1704
    # Convert the SafeConfigParser object to a dict
89 by Teddy Hogeborn
* Makefile: Bug fix: fixed creation of man pages for section 5 pages.
1705
    server_settings = server_config.defaults()
282 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
1706
    # Use the appropriate methods on the non-string config options
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1707
    for option in (u"debug", u"use_dbus", u"use_ipv6"):
1708
        server_settings[option] = server_config.getboolean(u"DEFAULT",
1709
                                                           option)
282 by Teddy Hogeborn
* mandos (main): Bug fix: use "getint" on the "port" config file
1710
    if server_settings["port"]:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1711
        server_settings["port"] = server_config.getint(u"DEFAULT",
1712
                                                       u"port")
28 by Teddy Hogeborn
* server.conf: New file.
1713
    del server_config
1714
    
1715
    # Override the settings from the config file with command line
1716
    # options, if set.
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1717
    for option in (u"interface", u"address", u"port", u"debug",
1718
                   u"priority", u"servicename", u"configdir",
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1719
                   u"use_dbus", u"use_ipv6", u"debuglevel"):
28 by Teddy Hogeborn
* server.conf: New file.
1720
        value = getattr(options, option)
1721
        if value is not None:
1722
            server_settings[option] = value
1723
    del options
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1724
    # Force all strings to be unicode
1725
    for option in server_settings.keys():
1726
        if type(server_settings[option]) is str:
1727
            server_settings[option] = unicode(server_settings[option])
28 by Teddy Hogeborn
* server.conf: New file.
1728
    # Now we have our good server settings in "server_settings"
1729
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1730
    ##################################################################
1731
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1732
    # For convenience
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1733
    debug = server_settings[u"debug"]
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1734
    debuglevel = server_settings[u"debuglevel"]
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1735
    use_dbus = server_settings[u"use_dbus"]
1736
    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
1737
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1738
    if server_settings[u"servicename"] != u"Mandos":
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
1739
        syslogger.setFormatter(logging.Formatter
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1740
                               (u'Mandos (%s) [%%(process)d]:'
1741
                                u' %%(levelname)s: %%(message)s'
1742
                                % server_settings[u"servicename"]))
52 by Teddy Hogeborn
* mandos: Make syslog use "/dev/log" instead of UDP to localhost.
1743
    
28 by Teddy Hogeborn
* server.conf: New file.
1744
    # Parse config file with clients
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1745
    client_defaults = { u"timeout": u"1h",
1746
                        u"interval": u"5m",
1747
                        u"checker": u"fping -q -- %%(host)s",
1748
                        u"host": u"",
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1749
                        u"approval_delay": u"0s",
1750
                        u"approval_duration": u"1s",
28 by Teddy Hogeborn
* server.conf: New file.
1751
                        }
335 by Teddy Hogeborn
* mandos: Import "SocketServer" as "socketserver" and "ConfigParser"
1752
    client_config = configparser.SafeConfigParser(client_defaults)
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1753
    client_config.read(os.path.join(server_settings[u"configdir"],
1754
                                    u"clients.conf"))
1755
    
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1756
    global mandos_dbus_service
1757
    mandos_dbus_service = None
28 by Teddy Hogeborn
* server.conf: New file.
1758
    
339 by Teddy Hogeborn
Code cleanup.
1759
    tcp_server = MandosServer((server_settings[u"address"],
1760
                               server_settings[u"port"]),
1761
                              ClientHandler,
237.2.35 by teddy at bsnet
* mandos (main): Bug fix: Don't try to bind to an empty string
1762
                              interface=(server_settings[u"interface"]
1763
                                         or None),
339 by Teddy Hogeborn
Code cleanup.
1764
                              use_ipv6=use_ipv6,
1765
                              gnutls_priority=
1766
                              server_settings[u"priority"],
1767
                              use_dbus=use_dbus)
439 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
1768
    if not debug:
1769
        pidfilename = u"/var/run/mandos.pid"
1770
        try:
1771
            pidfile = open(pidfilename, u"w")
1772
        except IOError:
1773
            logger.error(u"Could not open file %r", pidfilename)
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1774
    
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1775
    try:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1776
        uid = pwd.getpwnam(u"_mandos").pw_uid
1777
        gid = pwd.getpwnam(u"_mandos").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1778
    except KeyError:
1779
        try:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1780
            uid = pwd.getpwnam(u"mandos").pw_uid
1781
            gid = pwd.getpwnam(u"mandos").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1782
        except KeyError:
1783
            try:
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1784
                uid = pwd.getpwnam(u"nobody").pw_uid
1785
                gid = pwd.getpwnam(u"nobody").pw_gid
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1786
            except KeyError:
1787
                uid = 65534
1788
                gid = 65534
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1789
    try:
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1790
        os.setgid(gid)
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1791
        os.setuid(uid)
1792
    except OSError, error:
1793
        if error[0] != errno.EPERM:
1794
            raise error
164 by Teddy Hogeborn
* mandos: Open the PID file before daemonizing, but write to it
1795
    
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1796
    if not debug and not debuglevel:
1797
        syslogger.setLevel(logging.WARNING)
1798
        console.setLevel(logging.WARNING)
1799
    if debuglevel:
1800
        level = getattr(logging, debuglevel.upper())
1801
        syslogger.setLevel(level)
1802
        console.setLevel(level)
1803
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1804
    if debug:
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1805
        # Enable all possible GnuTLS debugging
1806
        
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1807
        # "Use a log level over 10 to enable all debugging options."
1808
        # - GnuTLS manual
1809
        gnutls.library.functions.gnutls_global_set_log_level(11)
1810
        
1811
        @gnutls.library.types.gnutls_log_func
1812
        def debug_gnutls(level, string):
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1813
            logger.debug(u"GnuTLS: %s", string[:-1])
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1814
        
1815
        (gnutls.library.functions
1816
         .gnutls_global_set_log_function(debug_gnutls))
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1817
        
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1818
        # Redirect stdin so all checkers get /dev/null
1819
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1820
        os.dup2(null, sys.stdin.fileno())
1821
        if null > 2:
1822
            os.close(null)
1823
    else:
1824
        # No console logging
1825
        logger.removeHandler(console)
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
1826
    
290 by Teddy Hogeborn
* mandos (main): Bug fix: Do setgid before setuid. Add verbose GnuTLS
1827
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
1828
    global main_loop
1829
    # From the Avahi example code
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1830
    DBusGMainLoop(set_as_default=True )
1831
    main_loop = gobject.MainLoop()
1832
    bus = dbus.SystemBus()
1833
    # End of Avahi example code
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1834
    if use_dbus:
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
1835
        try:
1836
            bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1837
                                            bus, do_not_queue=True)
1838
        except dbus.exceptions.NameExistsException, e:
1839
            logger.error(unicode(e) + u", disabling D-Bus")
1840
            use_dbus = False
1841
            server_settings[u"use_dbus"] = False
1842
            tcp_server.use_dbus = False
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
1843
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1844
    service = AvahiService(name = server_settings[u"servicename"],
1845
                           servicetype = u"_mandos._tcp",
1846
                           protocol = protocol, bus = bus)
1847
    if server_settings["interface"]:
1848
        service.interface = (if_nametoindex
1849
                             (str(server_settings[u"interface"])))
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
1850
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1851
    if not debug:
1852
        # Close all input and output, do double fork, etc.
1853
        daemon()
1854
        
24.1.152 by Björn Påhlsson
bug fixes that prevent problems when runing server as root
1855
    global multiprocessing_manager
1856
    multiprocessing_manager = multiprocessing.Manager()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1857
    
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1858
    client_class = Client
1859
    if use_dbus:
337 by Teddy Hogeborn
Code cleanup. Move some global stuff into main.
1860
        client_class = functools.partial(ClientDBus, bus = bus)
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1861
    def client_config_items(config, section):
1862
        special_settings = {
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
                lambda: config.getboolean(section,
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1865
                                          "approved_by_default"),
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1866
            }
1867
        for name, value in config.items(section):
1868
            try:
24.1.150 by Björn Påhlsson
* mandos: Added ClientDBus.approve_pending property. Exposed
1869
                yield (name, special_settings[name]())
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1870
            except KeyError:
1871
                yield (name, value)
1872
    
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1873
    tcp_server.clients.update(set(
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1874
            client_class(name = section,
24.1.149 by Björn Påhlsson
Changed ForkingMixIn in favor of multiprocessing
1875
                         config= dict(client_config_items(
1876
                        client_config, section)))
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1877
            for section in client_config.sections()))
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1878
    if not tcp_server.clients:
244 by Teddy Hogeborn
* debian/control (Build-Depends): Bug fix: Added "docbook-xml".
1879
        logger.warning(u"No clients defined")
24.1.155 by Björn Påhlsson
mandos server: Added debuglevel that adjust at what level information
1880
        
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1881
    if not debug:
439 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
1882
        try:
1883
            with pidfile:
1884
                pid = os.getpid()
1885
                pidfile.write(str(pid) + "\n")
1886
            del pidfile
1887
        except IOError:
1888
            logger.error(u"Could not write to file %r with PID %d",
1889
                         pidfilename, pid)
1890
        except NameError:
1891
            # "pidfile" was never created
1892
            pass
1893
        del pidfilename
1894
        
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1895
        signal.signal(signal.SIGINT, signal.SIG_IGN)
439 by Teddy Hogeborn
* mandos: Do not write pid file if --debug is passed.
1896
28 by Teddy Hogeborn
* server.conf: New file.
1897
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1898
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
1899
    
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1900
    if use_dbus:
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1901
        class MandosDBusService(dbus.service.Object):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1902
            """A D-Bus proxy object"""
1903
            def __init__(self):
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1904
                dbus.service.Object.__init__(self, bus, u"/")
279 by Teddy Hogeborn
* mandos (Client.__init__): Disable D-Bus during init to avoid
1905
            _interface = u"se.bsnet.fukt.Mandos"
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1906
            
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1907
            @dbus.service.signal(_interface, signature=u"o")
1908
            def ClientAdded(self, objpath):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1909
                "D-Bus signal"
1910
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1911
            
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
1912
            @dbus.service.signal(_interface, signature=u"ss")
1913
            def ClientNotFound(self, fingerprint, address):
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1914
                "D-Bus signal"
1915
                pass
1916
            
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1917
            @dbus.service.signal(_interface, signature=u"os")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1918
            def ClientRemoved(self, objpath, name):
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1919
                "D-Bus signal"
1920
                pass
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1921
            
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1922
            @dbus.service.method(_interface, out_signature=u"ao")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1923
            def GetAllClients(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1924
                "D-Bus method"
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1925
                return dbus.Array(c.dbus_object_path
1926
                                  for c in tcp_server.clients)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1927
            
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1928
            @dbus.service.method(_interface,
1929
                                 out_signature=u"a{oa{sv}}")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1930
            def GetAllClientsWithProperties(self):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1931
                "D-Bus method"
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1932
                return dbus.Dictionary(
380 by Teddy Hogeborn
Use D-Bus properties instead of our own methods.
1933
                    ((c.dbus_object_path, c.GetAll(u""))
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1934
                     for c in tcp_server.clients),
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1935
                    signature=u"oa{sv}")
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1936
            
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
1937
            @dbus.service.method(_interface, in_signature=u"o")
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1938
            def RemoveClient(self, object_path):
283 by Teddy Hogeborn
* mandos (peer_certificate): Handle NULL pointer from
1939
                "D-Bus method"
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1940
                for c in tcp_server.clients:
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1941
                    if c.dbus_object_path == object_path:
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1942
                        tcp_server.clients.remove(c)
328 by Teddy Hogeborn
* mandos (Client): Move all D-Bus code to new "ClientDBus" class.
1943
                        c.remove_from_connection()
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1944
                        # Don't signal anything except ClientRemoved
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
1945
                        c.disable(quiet=True)
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1946
                        # Emit D-Bus signal
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1947
                        self.ClientRemoved(object_path, c.name)
1948
                        return
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
1949
                raise KeyError(object_path)
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1950
            
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1951
            del _interface
278 by Teddy Hogeborn
* mandos (Client.GetAllProperties): Also send Client.dbus_object_path
1952
        
327 by Teddy Hogeborn
Merge from pipe IPC branch.
1953
        mandos_dbus_service = MandosDBusService()
238 by Teddy Hogeborn
First version of a somewhat complete D-Bus server interface. Also
1954
    
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
1955
    def cleanup():
1956
        "Cleanup function; run on exit"
1957
        service.cleanup()
1958
        
1959
        while tcp_server.clients:
1960
            client = tcp_server.clients.pop()
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
1961
            if use_dbus:
1962
                client.remove_from_connection()
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
1963
            client.disable_hook = None
1964
            # Don't signal anything except ClientRemoved
402 by Teddy Hogeborn
* mandos (Client.disable): Rename keyword argument "log" to "quiet",
1965
            client.disable(quiet=True)
400 by Teddy Hogeborn
* mandos (Client.enable): Bug fix: Start new immediate checker last to
1966
            if use_dbus:
1967
                # Emit D-Bus signal
1968
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1969
                                                  client.name)
1970
    
1971
    atexit.register(cleanup)
1972
    
341 by Teddy Hogeborn
Code cleanup and one bug fix.
1973
    for client in tcp_server.clients:
243 by Teddy Hogeborn
* mandos (Client.timeout, Client.interval): Changed from being a
1974
        if use_dbus:
1975
            # Emit D-Bus signal
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
1976
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
239 by Teddy Hogeborn
* mandos (_datetime_to_dbus_struct): Renamed to "_datetime_to_dbus";
1977
        client.enable()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
1978
    
163 by Teddy Hogeborn
* Makefile (PIDDIR, USER, GROUP): Removed.
1979
    tcp_server.enable()
1980
    tcp_server.server_activate()
1981
    
28 by Teddy Hogeborn
* server.conf: New file.
1982
    # Find out what port we got
1983
    service.port = tcp_server.socket.getsockname()[1]
314 by Teddy Hogeborn
Support not using IPv6 in server:
1984
    if use_ipv6:
1985
        logger.info(u"Now listening on address %r, port %d,"
1986
                    " flowinfo %d, scope_id %d"
1987
                    % tcp_server.socket.getsockname())
1988
    else:                       # IPv4
1989
        logger.info(u"Now listening on address %r, port %d"
1990
                    % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
1991
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
1992
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
1993
    
1994
    try:
1995
        # From the Avahi example code
1996
        try:
336 by Teddy Hogeborn
Code cleanup.
1997
            service.activate()
28 by Teddy Hogeborn
* server.conf: New file.
1998
        except dbus.exceptions.DBusException, error:
1999
            logger.critical(u"DBusException: %s", error)
401 by Teddy Hogeborn
* README (FAQ): Fix typo.
2000
            cleanup()
28 by Teddy Hogeborn
* server.conf: New file.
2001
            sys.exit(1)
2002
        # End of Avahi example code
2003
        
2004
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
2005
                             lambda *args, **kwargs:
237.1.2 by Teddy Hogeborn
Further steps towards a D-Bus server interface, plus minor syntax
2006
                             (tcp_server.handle_request
2007
                              (*args[2:], **kwargs) or True))
28 by Teddy Hogeborn
* server.conf: New file.
2008
        
51 by Teddy Hogeborn
* clients.conf: Better comments.
2009
        logger.debug(u"Starting main loop")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2010
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
2011
    except AvahiError, error:
242 by Teddy Hogeborn
* mandos (AvahiError): Converted to use unicode. All users changed.
2012
        logger.critical(u"AvahiError: %s", error)
401 by Teddy Hogeborn
* README (FAQ): Fix typo.
2013
        cleanup()
28 by Teddy Hogeborn
* server.conf: New file.
2014
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
2015
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2016
        if debug:
292 by Teddy Hogeborn
* Makefile (run-server): Use "--no-dbus" unconditionally.
2017
            print >> sys.stderr
333 by Teddy Hogeborn
Minor code cleanup; one minor bug fix.
2018
        logger.debug(u"Server received KeyboardInterrupt")
2019
    logger.debug(u"Server exiting")
401 by Teddy Hogeborn
* README (FAQ): Fix typo.
2020
    # Must run before the D-Bus bus name gets deregistered
2021
    cleanup()
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
2022
2023
if __name__ == '__main__':
2024
    main()