/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

* dbus-mandos.conf: New; to be copied to
                    "/etc/dbus-1/system.d/mandos.conf".

Show diffs side-by-side

added added

removed removed

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