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