/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2010-09-25 23:52:17 UTC
  • Revision ID: teddy@fukt.bsnet.se-20100925235217-4hhqfryz1ste6uw3
* mandos (ClientDBus.__init__): Bug fix: Translate "-" in client names
                                to "_" in D-Bus object paths.
  (MandosServer.handle_ipc): Bug fix: Send only address string to
                             D-Bus signal, not whole tuple.

* mandos-ctl: New options "--approve-by-default", "--deny-by-default",
              "--approval-delay", and "--approval-duration".
* mandos-ctl.xml (SYNOPSIS, OPTIONS): Document new options.

* mandos-monitor (MandosClientWidget.update): Fix spelling.

Show diffs side-by-side

added added

removed removed

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