/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: 2011-10-09 17:36:01 UTC
  • mto: (237.4.29 release)
  • mto: This revision was merged to the branch mainline in revision 514.
  • Revision ID: teddy@recompile.se-20111009173601-ctzsqstad3q2bs4c
Tags: version-1.4.0-1
* Makefile (version): Changed to "1.4.0".
* NEWS (Version 1.4.0): New entry.
* debian/changelog (1.4.0-1): - '' -

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