/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2014-07-25 22:44:20 UTC
  • mto: This revision was merged to the branch mainline in revision 724.
  • Revision ID: teddy@recompile.se-20140725224420-4a5ct2ptt0hsc92z
Require Python 2.7.

This is in preparation for the eventual move to Python 3, which will
happen as soon as all Python modules required by Mandos are available.
The mandos-ctl and mandos-monitor programs are already portable
between Python 2.6 and Python 3 without changes; this change will
bring the requirement up to Python 2.7.

* INSTALL (Prerequisites/Libraries/Mandos Server): Document
                                                   requirement of
                                                   Python 2.7; remove
                                                   Python-argparse
                                                   which is in the
                                                   Python 2.7 standard
                                                   library.
* debian/control (Source: mandos/Build-Depends-Indep): Depend on
                                                       exactly the
                                                       python2.7
                                                       package and all
                                                       the Python 2.7
                                                       versions of the
                                                       python modules.
  (Package: mandos/Depends): - '' - but still depend on python (<=2.7)
                            and the generic versions of the Python
                            modules; this is for mandos-ctl and
                            mandos-monitor, both of which are
                            compatible with Python 3, and use
                            #!/usr/bin/python.
* mandos: Use #!/usr/bin/python2.7 instead of #!/usr/bin/python.

Show diffs side-by-side

added added

removed removed

Lines of Context:
88
88
    except ImportError:
89
89
        SO_BINDTODEVICE = None
90
90
 
91
 
if sys.version_info.major == 2:
92
 
    str = unicode
93
 
 
94
 
version = "1.6.9"
 
91
version = "1.6.7"
95
92
stored_state_file = "clients.pickle"
96
93
 
97
94
logger = logging.getLogger()
107
104
        SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
108
105
        with contextlib.closing(socket.socket()) as s:
109
106
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
110
 
                                struct.pack(b"16s16x", interface))
111
 
        interface_index = struct.unpack("I", ifreq[16:20])[0]
 
107
                                struct.pack(str("16s16x"),
 
108
                                            interface))
 
109
        interface_index = struct.unpack(str("I"),
 
110
                                        ifreq[16:20])[0]
112
111
        return interface_index
113
112
 
114
113
 
119
118
    syslogger = (logging.handlers.SysLogHandler
120
119
                 (facility =
121
120
                  logging.handlers.SysLogHandler.LOG_DAEMON,
122
 
                  address = "/dev/log"))
 
121
                  address = str("/dev/log")))
123
122
    syslogger.setFormatter(logging.Formatter
124
123
                           ('Mandos [%(process)d]: %(levelname)s:'
125
124
                            ' %(message)s'))
225
224
class AvahiError(Exception):
226
225
    def __init__(self, value, *args, **kwargs):
227
226
        self.value = value
228
 
        return super(AvahiError, self).__init__(value, *args,
229
 
                                                **kwargs)
 
227
        super(AvahiError, self).__init__(value, *args, **kwargs)
 
228
    def __unicode__(self):
 
229
        return unicode(repr(self.value))
230
230
 
231
231
class AvahiServiceError(AvahiError):
232
232
    pass
275
275
        self.bus = bus
276
276
        self.entry_group_state_changed_match = None
277
277
    
278
 
    def rename(self, remove=True):
 
278
    def rename(self):
279
279
        """Derived from the Avahi example code"""
280
280
        if self.rename_count >= self.max_renames:
281
281
            logger.critical("No suitable Zeroconf service name found"
282
282
                            " after %i retries, exiting.",
283
283
                            self.rename_count)
284
284
            raise AvahiServiceError("Too many renames")
285
 
        self.name = str(self.server
286
 
                        .GetAlternativeServiceName(self.name))
287
 
        self.rename_count += 1
 
285
        self.name = unicode(self.server
 
286
                            .GetAlternativeServiceName(self.name))
288
287
        logger.info("Changing Zeroconf service name to %r ...",
289
288
                    self.name)
290
 
        if remove:
291
 
            self.remove()
 
289
        self.remove()
292
290
        try:
293
291
            self.add()
294
292
        except dbus.exceptions.DBusException as error:
295
 
            if (error.get_dbus_name()
296
 
                == "org.freedesktop.Avahi.CollisionError"):
297
 
                logger.info("Local Zeroconf service name collision.")
298
 
                return self.rename(remove=False)
299
 
            else:
300
 
                logger.critical("D-Bus Exception", exc_info=error)
301
 
                self.cleanup()
302
 
                os._exit(1)
 
293
            logger.critical("D-Bus Exception", exc_info=error)
 
294
            self.cleanup()
 
295
            os._exit(1)
 
296
        self.rename_count += 1
303
297
    
304
298
    def remove(self):
305
299
        """Derived from the Avahi example code"""
343
337
            self.rename()
344
338
        elif state == avahi.ENTRY_GROUP_FAILURE:
345
339
            logger.critical("Avahi: Error in group state changed %s",
346
 
                            str(error))
347
 
            raise AvahiGroupError("State changed: {!s}"
 
340
                            unicode(error))
 
341
            raise AvahiGroupError("State changed: {0!s}"
348
342
                                  .format(error))
349
343
    
350
344
    def cleanup(self):
397
391
 
398
392
 
399
393
class AvahiServiceToSyslog(AvahiService):
400
 
    def rename(self, *args, **kwargs):
 
394
    def rename(self):
401
395
        """Add the new name to the syslog messages"""
402
 
        ret = AvahiService.rename(self, *args, **kwargs)
 
396
        ret = AvahiService.rename(self)
403
397
        syslogger.setFormatter(logging.Formatter
404
 
                               ('Mandos ({}) [%(process)d]:'
 
398
                               ('Mandos ({0}) [%(process)d]:'
405
399
                                ' %(levelname)s: %(message)s'
406
400
                                .format(self.name)))
407
401
        return ret
408
402
 
409
403
 
 
404
def timedelta_to_milliseconds(td):
 
405
    "Convert a datetime.timedelta() to milliseconds"
 
406
    return ((td.days * 24 * 60 * 60 * 1000)
 
407
            + (td.seconds * 1000)
 
408
            + (td.microseconds // 1000))
 
409
 
 
410
 
410
411
class Client(object):
411
412
    """A representation of a client host served by this server.
412
413
    
467
468
                        "enabled": "True",
468
469
                        }
469
470
    
 
471
    def timeout_milliseconds(self):
 
472
        "Return the 'timeout' attribute in milliseconds"
 
473
        return timedelta_to_milliseconds(self.timeout)
 
474
    
 
475
    def extended_timeout_milliseconds(self):
 
476
        "Return the 'extended_timeout' attribute in milliseconds"
 
477
        return timedelta_to_milliseconds(self.extended_timeout)
 
478
    
 
479
    def interval_milliseconds(self):
 
480
        "Return the 'interval' attribute in milliseconds"
 
481
        return timedelta_to_milliseconds(self.interval)
 
482
    
 
483
    def approval_delay_milliseconds(self):
 
484
        return timedelta_to_milliseconds(self.approval_delay)
 
485
    
470
486
    @staticmethod
471
487
    def config_parser(config):
472
488
        """Construct a new dict of client settings of this form:
497
513
                          "rb") as secfile:
498
514
                    client["secret"] = secfile.read()
499
515
            else:
500
 
                raise TypeError("No secret or secfile for section {}"
 
516
                raise TypeError("No secret or secfile for section {0}"
501
517
                                .format(section))
502
518
            client["timeout"] = string_to_delta(section["timeout"])
503
519
            client["extended_timeout"] = string_to_delta(
520
536
            server_settings = {}
521
537
        self.server_settings = server_settings
522
538
        # adding all client settings
523
 
        for setting, value in settings.items():
 
539
        for setting, value in settings.iteritems():
524
540
            setattr(self, setting, value)
525
541
        
526
542
        if self.enabled:
609
625
        if self.checker_initiator_tag is not None:
610
626
            gobject.source_remove(self.checker_initiator_tag)
611
627
        self.checker_initiator_tag = (gobject.timeout_add
612
 
                                      (int(self.interval
613
 
                                           .total_seconds() * 1000),
 
628
                                      (self.interval_milliseconds(),
614
629
                                       self.start_checker))
615
630
        # Schedule a disable() when 'timeout' has passed
616
631
        if self.disable_initiator_tag is not None:
617
632
            gobject.source_remove(self.disable_initiator_tag)
618
633
        self.disable_initiator_tag = (gobject.timeout_add
619
 
                                      (int(self.timeout
620
 
                                           .total_seconds() * 1000),
621
 
                                       self.disable))
 
634
                                   (self.timeout_milliseconds(),
 
635
                                    self.disable))
622
636
        # Also start a new checker *right now*.
623
637
        self.start_checker()
624
638
    
655
669
            self.disable_initiator_tag = None
656
670
        if getattr(self, "enabled", False):
657
671
            self.disable_initiator_tag = (gobject.timeout_add
658
 
                                          (int(timeout.total_seconds()
659
 
                                               * 1000), self.disable))
 
672
                                          (timedelta_to_milliseconds
 
673
                                           (timeout), self.disable))
660
674
            self.expires = datetime.datetime.utcnow() + timeout
661
675
    
662
676
    def need_approval(self):
693
707
        # Start a new checker if needed
694
708
        if self.checker is None:
695
709
            # Escape attributes for the shell
696
 
            escaped_attrs = { attr:
697
 
                                  re.escape(str(getattr(self, attr)))
698
 
                              for attr in self.runtime_expansions }
 
710
            escaped_attrs = dict(
 
711
                (attr, re.escape(unicode(getattr(self, attr))))
 
712
                for attr in
 
713
                self.runtime_expansions)
699
714
            try:
700
715
                command = self.checker_command % escaped_attrs
701
716
            except TypeError as error:
782
797
    # "Set" method, so we fail early here:
783
798
    if byte_arrays and signature != "ay":
784
799
        raise ValueError("Byte arrays not supported for non-'ay'"
785
 
                         " signature {!r}".format(signature))
 
800
                         " signature {0!r}".format(signature))
786
801
    def decorator(func):
787
802
        func._dbus_is_property = True
788
803
        func._dbus_interface = dbus_interface
819
834
    """Decorator to annotate D-Bus methods, signals or properties
820
835
    Usage:
821
836
    
822
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true",
823
 
                       "org.freedesktop.DBus.Property."
824
 
                       "EmitsChangedSignal": "false"})
825
837
    @dbus_service_property("org.example.Interface", signature="b",
826
838
                           access="r")
 
839
    @dbus_annotations({{"org.freedesktop.DBus.Deprecated": "true",
 
840
                        "org.freedesktop.DBus.Property."
 
841
                        "EmitsChangedSignal": "false"})
827
842
    def Property_dbus_property(self):
828
843
        return dbus.Boolean(False)
829
844
    """
836
851
class DBusPropertyException(dbus.exceptions.DBusException):
837
852
    """A base class for D-Bus property-related exceptions
838
853
    """
839
 
    pass
 
854
    def __unicode__(self):
 
855
        return unicode(str(self))
 
856
 
840
857
 
841
858
class DBusPropertyAccessException(DBusPropertyException):
842
859
    """A property's access permissions disallows an operation.
865
882
        If called like _is_dbus_thing("method") it returns a function
866
883
        suitable for use as predicate to inspect.getmembers().
867
884
        """
868
 
        return lambda obj: getattr(obj, "_dbus_is_{}".format(thing),
 
885
        return lambda obj: getattr(obj, "_dbus_is_{0}".format(thing),
869
886
                                   False)
870
887
    
871
888
    def _get_all_dbus_things(self, thing):
921
938
            # signatures other than "ay".
922
939
            if prop._dbus_signature != "ay":
923
940
                raise ValueError("Byte arrays not supported for non-"
924
 
                                 "'ay' signature {!r}"
 
941
                                 "'ay' signature {0!r}"
925
942
                                 .format(prop._dbus_signature))
926
943
            value = dbus.ByteArray(b''.join(chr(byte)
927
944
                                            for byte in value))
952
969
                                           value.variant_level+1)
953
970
        return dbus.Dictionary(properties, signature="sv")
954
971
    
955
 
    @dbus.service.signal(dbus.PROPERTIES_IFACE, signature="sa{sv}as")
956
 
    def PropertiesChanged(self, interface_name, changed_properties,
957
 
                          invalidated_properties):
958
 
        """Standard D-Bus PropertiesChanged() signal, see D-Bus
959
 
        standard.
960
 
        """
961
 
        pass
962
 
    
963
972
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
964
973
                         out_signature="s",
965
974
                         path_keyword='object_path',
1000
1009
                                              (prop,
1001
1010
                                               "_dbus_annotations",
1002
1011
                                               {}))
1003
 
                        for name, value in annots.items():
 
1012
                        for name, value in annots.iteritems():
1004
1013
                            ann_tag = document.createElement(
1005
1014
                                "annotation")
1006
1015
                            ann_tag.setAttribute("name", name)
1009
1018
                # Add interface annotation tags
1010
1019
                for annotation, value in dict(
1011
1020
                    itertools.chain.from_iterable(
1012
 
                        annotations().items()
 
1021
                        annotations().iteritems()
1013
1022
                        for name, annotations in
1014
1023
                        self._get_all_dbus_things("interface")
1015
1024
                        if name == if_tag.getAttribute("name")
1016
 
                        )).items():
 
1025
                        )).iteritems():
1017
1026
                    ann_tag = document.createElement("annotation")
1018
1027
                    ann_tag.setAttribute("name", annotation)
1019
1028
                    ann_tag.setAttribute("value", value)
1075
1084
    """
1076
1085
    def wrapper(cls):
1077
1086
        for orig_interface_name, alt_interface_name in (
1078
 
            alt_interface_names.items()):
 
1087
            alt_interface_names.iteritems()):
1079
1088
            attr = {}
1080
1089
            interface_names = set()
1081
1090
            # Go though all attributes of the class
1198
1207
                                        attribute.func_closure)))
1199
1208
            if deprecate:
1200
1209
                # Deprecate all alternate interfaces
1201
 
                iname="_AlternateDBusNames_interface_annotation{}"
 
1210
                iname="_AlternateDBusNames_interface_annotation{0}"
1202
1211
                for interface_name in interface_names:
1203
1212
                    @dbus_interface_annotations(interface_name)
1204
1213
                    def func(self):
1213
1222
            if interface_names:
1214
1223
                # Replace the class with a new subclass of it with
1215
1224
                # methods, signals, etc. as created above.
1216
 
                cls = type(b"{}Alternate".format(cls.__name__),
 
1225
                cls = type(b"{0}Alternate".format(cls.__name__),
1217
1226
                           (cls,), attr)
1218
1227
        return cls
1219
1228
    return wrapper
1232
1241
    runtime_expansions = (Client.runtime_expansions
1233
1242
                          + ("dbus_object_path",))
1234
1243
    
1235
 
    _interface = "se.recompile.Mandos.Client"
1236
 
    
1237
1244
    # dbus.service.Object doesn't use super(), so we can't either.
1238
1245
    
1239
1246
    def __init__(self, bus = None, *args, **kwargs):
1241
1248
        Client.__init__(self, *args, **kwargs)
1242
1249
        # Only now, when this client is initialized, can it show up on
1243
1250
        # the D-Bus
1244
 
        client_object_name = str(self.name).translate(
 
1251
        client_object_name = unicode(self.name).translate(
1245
1252
            {ord("."): ord("_"),
1246
1253
             ord("-"): ord("_")})
1247
1254
        self.dbus_object_path = (dbus.ObjectPath
1251
1258
    
1252
1259
    def notifychangeproperty(transform_func,
1253
1260
                             dbus_name, type_func=lambda x: x,
1254
 
                             variant_level=1, invalidate_only=False,
1255
 
                             _interface=_interface):
 
1261
                             variant_level=1):
1256
1262
        """ Modify a variable so that it's a property which announces
1257
1263
        its changes to DBus.
1258
1264
        
1263
1269
                   to the D-Bus.  Default: no transform
1264
1270
        variant_level: D-Bus variant level.  Default: 1
1265
1271
        """
1266
 
        attrname = "_{}".format(dbus_name)
 
1272
        attrname = "_{0}".format(dbus_name)
1267
1273
        def setter(self, value):
1268
1274
            if hasattr(self, "dbus_object_path"):
1269
1275
                if (not hasattr(self, attrname) or
1270
1276
                    type_func(getattr(self, attrname, None))
1271
1277
                    != type_func(value)):
1272
 
                    if invalidate_only:
1273
 
                        self.PropertiesChanged(_interface,
1274
 
                                               dbus.Dictionary(),
1275
 
                                               dbus.Array
1276
 
                                               ((dbus_name,)))
1277
 
                    else:
1278
 
                        dbus_value = transform_func(type_func(value),
1279
 
                                                    variant_level
1280
 
                                                    =variant_level)
1281
 
                        self.PropertyChanged(dbus.String(dbus_name),
1282
 
                                             dbus_value)
1283
 
                        self.PropertiesChanged(_interface,
1284
 
                                               dbus.Dictionary({
1285
 
                                    dbus.String(dbus_name):
1286
 
                                        dbus_value }), dbus.Array())
 
1278
                    dbus_value = transform_func(type_func(value),
 
1279
                                                variant_level
 
1280
                                                =variant_level)
 
1281
                    self.PropertyChanged(dbus.String(dbus_name),
 
1282
                                         dbus_value)
1287
1283
            setattr(self, attrname, value)
1288
1284
        
1289
1285
        return property(lambda self: getattr(self, attrname), setter)
1309
1305
    approval_delay = notifychangeproperty(dbus.UInt64,
1310
1306
                                          "ApprovalDelay",
1311
1307
                                          type_func =
1312
 
                                          lambda td: td.total_seconds()
1313
 
                                          * 1000)
 
1308
                                          timedelta_to_milliseconds)
1314
1309
    approval_duration = notifychangeproperty(
1315
1310
        dbus.UInt64, "ApprovalDuration",
1316
 
        type_func = lambda td: td.total_seconds() * 1000)
 
1311
        type_func = timedelta_to_milliseconds)
1317
1312
    host = notifychangeproperty(dbus.String, "Host")
1318
1313
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1319
 
                                   type_func = lambda td:
1320
 
                                       td.total_seconds() * 1000)
 
1314
                                   type_func =
 
1315
                                   timedelta_to_milliseconds)
1321
1316
    extended_timeout = notifychangeproperty(
1322
1317
        dbus.UInt64, "ExtendedTimeout",
1323
 
        type_func = lambda td: td.total_seconds() * 1000)
 
1318
        type_func = timedelta_to_milliseconds)
1324
1319
    interval = notifychangeproperty(dbus.UInt64,
1325
1320
                                    "Interval",
1326
1321
                                    type_func =
1327
 
                                    lambda td: td.total_seconds()
1328
 
                                    * 1000)
 
1322
                                    timedelta_to_milliseconds)
1329
1323
    checker_command = notifychangeproperty(dbus.String, "Checker")
1330
 
    secret = notifychangeproperty(dbus.ByteArray, "Secret",
1331
 
                                  invalidate_only=True)
1332
1324
    
1333
1325
    del notifychangeproperty
1334
1326
    
1376
1368
    
1377
1369
    def approve(self, value=True):
1378
1370
        self.approved = value
1379
 
        gobject.timeout_add(int(self.approval_duration.total_seconds()
1380
 
                                * 1000), self._reset_approved)
 
1371
        gobject.timeout_add(timedelta_to_milliseconds
 
1372
                            (self.approval_duration),
 
1373
                            self._reset_approved)
1381
1374
        self.send_changedstate()
1382
1375
    
1383
1376
    ## D-Bus methods, signals & properties
 
1377
    _interface = "se.recompile.Mandos.Client"
1384
1378
    
1385
1379
    ## Interfaces
1386
1380
    
 
1381
    @dbus_interface_annotations(_interface)
 
1382
    def _foo(self):
 
1383
        return { "org.freedesktop.DBus.Property.EmitsChangedSignal":
 
1384
                     "false"}
 
1385
    
1387
1386
    ## Signals
1388
1387
    
1389
1388
    # CheckerCompleted - signal
1399
1398
        pass
1400
1399
    
1401
1400
    # PropertyChanged - signal
1402
 
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1403
1401
    @dbus.service.signal(_interface, signature="sv")
1404
1402
    def PropertyChanged(self, property, value):
1405
1403
        "D-Bus signal"
1481
1479
                           access="readwrite")
1482
1480
    def ApprovalDelay_dbus_property(self, value=None):
1483
1481
        if value is None:       # get
1484
 
            return dbus.UInt64(self.approval_delay.total_seconds()
1485
 
                               * 1000)
 
1482
            return dbus.UInt64(self.approval_delay_milliseconds())
1486
1483
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
1487
1484
    
1488
1485
    # ApprovalDuration - property
1490
1487
                           access="readwrite")
1491
1488
    def ApprovalDuration_dbus_property(self, value=None):
1492
1489
        if value is None:       # get
1493
 
            return dbus.UInt64(self.approval_duration.total_seconds()
1494
 
                               * 1000)
 
1490
            return dbus.UInt64(timedelta_to_milliseconds(
 
1491
                    self.approval_duration))
1495
1492
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1496
1493
    
1497
1494
    # Name - property
1510
1507
    def Host_dbus_property(self, value=None):
1511
1508
        if value is None:       # get
1512
1509
            return dbus.String(self.host)
1513
 
        self.host = str(value)
 
1510
        self.host = unicode(value)
1514
1511
    
1515
1512
    # Created - property
1516
1513
    @dbus_service_property(_interface, signature="s", access="read")
1563
1560
                           access="readwrite")
1564
1561
    def Timeout_dbus_property(self, value=None):
1565
1562
        if value is None:       # get
1566
 
            return dbus.UInt64(self.timeout.total_seconds() * 1000)
 
1563
            return dbus.UInt64(self.timeout_milliseconds())
1567
1564
        old_timeout = self.timeout
1568
1565
        self.timeout = datetime.timedelta(0, 0, 0, value)
1569
1566
        # Reschedule disabling
1580
1577
                gobject.source_remove(self.disable_initiator_tag)
1581
1578
                self.disable_initiator_tag = (
1582
1579
                    gobject.timeout_add(
1583
 
                        int((self.expires - now).total_seconds()
1584
 
                            * 1000), self.disable))
 
1580
                        timedelta_to_milliseconds(self.expires - now),
 
1581
                        self.disable))
1585
1582
    
1586
1583
    # ExtendedTimeout - property
1587
1584
    @dbus_service_property(_interface, signature="t",
1588
1585
                           access="readwrite")
1589
1586
    def ExtendedTimeout_dbus_property(self, value=None):
1590
1587
        if value is None:       # get
1591
 
            return dbus.UInt64(self.extended_timeout.total_seconds()
1592
 
                               * 1000)
 
1588
            return dbus.UInt64(self.extended_timeout_milliseconds())
1593
1589
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1594
1590
    
1595
1591
    # Interval - property
1597
1593
                           access="readwrite")
1598
1594
    def Interval_dbus_property(self, value=None):
1599
1595
        if value is None:       # get
1600
 
            return dbus.UInt64(self.interval.total_seconds() * 1000)
 
1596
            return dbus.UInt64(self.interval_milliseconds())
1601
1597
        self.interval = datetime.timedelta(0, 0, 0, value)
1602
1598
        if getattr(self, "checker_initiator_tag", None) is None:
1603
1599
            return
1614
1610
    def Checker_dbus_property(self, value=None):
1615
1611
        if value is None:       # get
1616
1612
            return dbus.String(self.checker_command)
1617
 
        self.checker_command = str(value)
 
1613
        self.checker_command = unicode(value)
1618
1614
    
1619
1615
    # CheckerRunning - property
1620
1616
    @dbus_service_property(_interface, signature="b",
1636
1632
    @dbus_service_property(_interface, signature="ay",
1637
1633
                           access="write", byte_arrays=True)
1638
1634
    def Secret_dbus_property(self, value):
1639
 
        self.secret = bytes(value)
 
1635
        self.secret = str(value)
1640
1636
    
1641
1637
    del _interface
1642
1638
 
1676
1672
    def handle(self):
1677
1673
        with contextlib.closing(self.server.child_pipe) as child_pipe:
1678
1674
            logger.info("TCP connection from: %s",
1679
 
                        str(self.client_address))
 
1675
                        unicode(self.client_address))
1680
1676
            logger.debug("Pipe FD: %d",
1681
1677
                         self.server.child_pipe.fileno())
1682
1678
            
1763
1759
                        if self.server.use_dbus:
1764
1760
                            # Emit D-Bus signal
1765
1761
                            client.NeedApproval(
1766
 
                                client.approval_delay.total_seconds()
1767
 
                                * 1000, client.approved_by_default)
 
1762
                                client.approval_delay_milliseconds(),
 
1763
                                client.approved_by_default)
1768
1764
                    else:
1769
1765
                        logger.warning("Client %s was not approved",
1770
1766
                                       client.name)
1776
1772
                    #wait until timeout or approved
1777
1773
                    time = datetime.datetime.now()
1778
1774
                    client.changedstate.acquire()
1779
 
                    client.changedstate.wait(delay.total_seconds())
 
1775
                    client.changedstate.wait(
 
1776
                        float(timedelta_to_milliseconds(delay)
 
1777
                              / 1000))
1780
1778
                    client.changedstate.release()
1781
1779
                    time2 = datetime.datetime.now()
1782
1780
                    if (time2 - time) >= delay:
1981
1979
                try:
1982
1980
                    self.socket.setsockopt(socket.SOL_SOCKET,
1983
1981
                                           SO_BINDTODEVICE,
1984
 
                                           (self.interface + "\0")
1985
 
                                           .encode("utf-8"))
 
1982
                                           str(self.interface + '\0'))
1986
1983
                except socket.error as error:
1987
1984
                    if error.errno == errno.EPERM:
1988
1985
                        logger.error("No permission to bind to"
2248
2245
    timevalue = datetime.timedelta(0)
2249
2246
    for s in interval.split():
2250
2247
        try:
2251
 
            suffix = s[-1]
 
2248
            suffix = unicode(s[-1])
2252
2249
            value = int(s[:-1])
2253
2250
            if suffix == "d":
2254
2251
                delta = datetime.timedelta(value)
2261
2258
            elif suffix == "w":
2262
2259
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
2263
2260
            else:
2264
 
                raise ValueError("Unknown suffix {!r}"
 
2261
                raise ValueError("Unknown suffix {0!r}"
2265
2262
                                 .format(suffix))
2266
2263
        except IndexError as e:
2267
2264
            raise ValueError(*(e.args))
2284
2281
        # Close all standard open file descriptors
2285
2282
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
2286
2283
        if not stat.S_ISCHR(os.fstat(null).st_mode):
2287
 
            raise OSError(errno.ENODEV, "{} not a character device"
 
2284
            raise OSError(errno.ENODEV,
 
2285
                          "{0} not a character device"
2288
2286
                          .format(os.devnull))
2289
2287
        os.dup2(null, sys.stdin.fileno())
2290
2288
        os.dup2(null, sys.stdout.fileno())
2300
2298
    
2301
2299
    parser = argparse.ArgumentParser()
2302
2300
    parser.add_argument("-v", "--version", action="version",
2303
 
                        version = "%(prog)s {}".format(version),
 
2301
                        version = "%(prog)s {0}".format(version),
2304
2302
                        help="show version number and exit")
2305
2303
    parser.add_argument("-i", "--interface", metavar="IF",
2306
2304
                        help="Bind to interface IF")
2405
2403
    del options
2406
2404
    # Force all strings to be unicode
2407
2405
    for option in server_settings.keys():
2408
 
        if isinstance(server_settings[option], bytes):
2409
 
            server_settings[option] = (server_settings[option]
2410
 
                                       .decode("utf-8"))
 
2406
        if type(server_settings[option]) is str:
 
2407
            server_settings[option] = unicode(server_settings[option])
2411
2408
    # Force all boolean options to be boolean
2412
2409
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
2413
2410
                   "foreground", "zeroconf"):
2446
2443
    
2447
2444
    if server_settings["servicename"] != "Mandos":
2448
2445
        syslogger.setFormatter(logging.Formatter
2449
 
                               ('Mandos ({}) [%(process)d]:'
 
2446
                               ('Mandos ({0}) [%(process)d]:'
2450
2447
                                ' %(levelname)s: %(message)s'
2451
2448
                                .format(server_settings
2452
2449
                                        ["servicename"])))
2556
2553
                                       protocol = protocol, bus = bus)
2557
2554
        if server_settings["interface"]:
2558
2555
            service.interface = (if_nametoindex
2559
 
                                 (server_settings["interface"]
2560
 
                                  .encode("utf-8")))
 
2556
                                 (str(server_settings["interface"])))
2561
2557
    
2562
2558
    global multiprocessing_manager
2563
2559
    multiprocessing_manager = multiprocessing.Manager()
2587
2583
            os.remove(stored_state_path)
2588
2584
        except IOError as e:
2589
2585
            if e.errno == errno.ENOENT:
2590
 
                logger.warning("Could not load persistent state: {}"
 
2586
                logger.warning("Could not load persistent state: {0}"
2591
2587
                                .format(os.strerror(e.errno)))
2592
2588
            else:
2593
2589
                logger.critical("Could not load persistent state:",
2598
2594
                           "EOFError:", exc_info=e)
2599
2595
    
2600
2596
    with PGPEngine() as pgp:
2601
 
        for client_name, client in clients_data.items():
 
2597
        for client_name, client in clients_data.iteritems():
2602
2598
            # Skip removed clients
2603
2599
            if client_name not in client_settings:
2604
2600
                continue
2629
2625
                if datetime.datetime.utcnow() >= client["expires"]:
2630
2626
                    if not client["last_checked_ok"]:
2631
2627
                        logger.warning(
2632
 
                            "disabling client {} - Client never "
 
2628
                            "disabling client {0} - Client never "
2633
2629
                            "performed a successful checker"
2634
2630
                            .format(client_name))
2635
2631
                        client["enabled"] = False
2636
2632
                    elif client["last_checker_status"] != 0:
2637
2633
                        logger.warning(
2638
 
                            "disabling client {} - Client last"
2639
 
                            " checker failed with error code {}"
 
2634
                            "disabling client {0} - Client "
 
2635
                            "last checker failed with error code {1}"
2640
2636
                            .format(client_name,
2641
2637
                                    client["last_checker_status"]))
2642
2638
                        client["enabled"] = False
2645
2641
                                             .utcnow()
2646
2642
                                             + client["timeout"])
2647
2643
                        logger.debug("Last checker succeeded,"
2648
 
                                     " keeping {} enabled"
 
2644
                                     " keeping {0} enabled"
2649
2645
                                     .format(client_name))
2650
2646
            try:
2651
2647
                client["secret"] = (
2654
2650
                                ["secret"]))
2655
2651
            except PGPError:
2656
2652
                # If decryption fails, we use secret from new settings
2657
 
                logger.debug("Failed to decrypt {} old secret"
 
2653
                logger.debug("Failed to decrypt {0} old secret"
2658
2654
                             .format(client_name))
2659
2655
                client["secret"] = (
2660
2656
                    client_settings[client_name]["secret"])
2668
2664
        clients_data[client_name] = client_settings[client_name]
2669
2665
    
2670
2666
    # Create all client objects
2671
 
    for client_name, client in clients_data.items():
 
2667
    for client_name, client in clients_data.iteritems():
2672
2668
        tcp_server.clients[client_name] = client_class(
2673
2669
            name = client_name, settings = client,
2674
2670
            server_settings = server_settings)
2681
2677
            try:
2682
2678
                with pidfile:
2683
2679
                    pid = os.getpid()
2684
 
                    pidfile.write("{}\n".format(pid).encode("utf-8"))
 
2680
                    pidfile.write(str(pid) + "\n".encode("utf-8"))
2685
2681
            except IOError:
2686
2682
                logger.error("Could not write to file %r with PID %d",
2687
2683
                             pidfilename, pid)
2733
2729
            def GetAllClientsWithProperties(self):
2734
2730
                "D-Bus method"
2735
2731
                return dbus.Dictionary(
2736
 
                    { c.dbus_object_path: c.GetAll("")
2737
 
                      for c in tcp_server.clients.itervalues() },
 
2732
                    ((c.dbus_object_path, c.GetAll(""))
 
2733
                     for c in tcp_server.clients.itervalues()),
2738
2734
                    signature="oa{sv}")
2739
2735
            
2740
2736
            @dbus.service.method(_interface, in_signature="o")
2778
2774
                
2779
2775
                # A list of attributes that can not be pickled
2780
2776
                # + secret.
2781
 
                exclude = { "bus", "changedstate", "secret",
2782
 
                            "checker", "server_settings" }
 
2777
                exclude = set(("bus", "changedstate", "secret",
 
2778
                               "checker", "server_settings"))
2783
2779
                for name, typ in (inspect.getmembers
2784
2780
                                  (dbus.service.Object)):
2785
2781
                    exclude.add(name)
2808
2804
                except NameError:
2809
2805
                    pass
2810
2806
            if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST):
2811
 
                logger.warning("Could not save persistent state: {}"
 
2807
                logger.warning("Could not save persistent state: {0}"
2812
2808
                               .format(os.strerror(e.errno)))
2813
2809
            else:
2814
2810
                logger.warning("Could not save persistent state:",