/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

  • Committer: Björn Påhlsson
  • Date: 2010-09-09 22:06:10 UTC
  • mto: (237.7.1 mandos)
  • mto: This revision was merged to the branch mainline in revision 270.
  • Revision ID: belorn@fukt.bsnet.se-20100909220610-fcpkaykznlq22oaw
minor debug info for plugin-runner.
Commit before merge

Show diffs side-by-side

added added

removed removed

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