/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: Björn Påhlsson
  • Date: 2011-12-26 15:23:45 UTC
  • Revision ID: belorn@recompile.se-20111226152345-cd9mz98e7eiqbfbx
nicer stacktrace when mandos-monitor fail during startup

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-2012 Teddy Hogeborn
15
 
# Copyright © 2008-2012 Björn Påhlsson
 
14
# Copyright © 2008-2011 Teddy Hogeborn
 
15
# Copyright © 2008-2011 Björn Påhlsson
16
16
17
17
# This program is free software: you can redistribute it and/or modify
18
18
# it under the terms of the GNU General Public License as published by
85
85
    except ImportError:
86
86
        SO_BINDTODEVICE = None
87
87
 
88
 
version = "1.5.2"
 
88
version = "1.4.1"
89
89
stored_state_file = "clients.pickle"
90
90
 
91
91
logger = logging.getLogger()
142
142
        self.gnupg.options.meta_interactive = False
143
143
        self.gnupg.options.homedir = self.tempdir
144
144
        self.gnupg.options.extra_args.extend(['--force-mdc',
145
 
                                              '--quiet',
146
 
                                              '--no-use-agent'])
 
145
                                              '--quiet'])
147
146
    
148
147
    def __enter__(self):
149
148
        return self
415
414
    last_checked_ok: datetime.datetime(); (UTC) or None
416
415
    last_checker_status: integer between 0 and 255 reflecting exit
417
416
                         status of last checker. -1 reflects crashed
418
 
                         checker, -2 means no checker completed yet.
 
417
                         checker, or None.
419
418
    last_enabled: datetime.datetime(); (UTC) or None
420
419
    name:       string; from the config file, used in log messages and
421
420
                        D-Bus identifiers
422
421
    secret:     bytestring; sent verbatim (over TLS) to client
423
422
    timeout:    datetime.timedelta(); How long from last_checked_ok
424
423
                                      until this client is disabled
425
 
    extended_timeout:   extra long timeout when secret has been sent
 
424
    extended_timeout:   extra long timeout when password has been sent
426
425
    runtime_expansions: Allowed attributes for runtime expansion.
427
426
    expires:    datetime.datetime(); time (UTC) when a client will be
428
427
                disabled, or None
460
459
 
461
460
    @staticmethod
462
461
    def config_parser(config):
463
 
        """Construct a new dict of client settings of this form:
 
462
        """ Construct a new dict of client settings of this form:
464
463
        { client_name: {setting_name: value, ...}, ...}
465
 
        with exceptions for any special settings as defined above.
466
 
        NOTE: Must be a pure function. Must return the same result
467
 
        value given the same arguments.
468
 
        """
 
464
        with exceptions for any special settings as defined above"""
469
465
        settings = {}
470
466
        for client_name in config.sections():
471
467
            section = dict(config.items(client_name))
475
471
            # Reformat values from string types to Python types
476
472
            client["approved_by_default"] = config.getboolean(
477
473
                client_name, "approved_by_default")
478
 
            client["enabled"] = config.getboolean(client_name,
479
 
                                                  "enabled")
 
474
            client["enabled"] = config.getboolean(client_name, "enabled")
480
475
            
481
476
            client["fingerprint"] = (section["fingerprint"].upper()
482
477
                                     .replace(" ", ""))
501
496
            client["checker_command"] = section["checker"]
502
497
            client["last_approval_request"] = None
503
498
            client["last_checked_ok"] = None
504
 
            client["last_checker_status"] = -2
505
 
        
 
499
            client["last_checker_status"] = None
 
500
            if client["enabled"]:
 
501
                client["last_enabled"] = datetime.datetime.utcnow()
 
502
                client["expires"] = (datetime.datetime.utcnow()
 
503
                                     + client["timeout"])
 
504
            else:
 
505
                client["last_enabled"] = None
 
506
                client["expires"] = None
 
507
 
506
508
        return settings
507
509
        
508
510
        
515
517
        for setting, value in settings.iteritems():
516
518
            setattr(self, setting, value)
517
519
        
518
 
        if self.enabled:
519
 
            if not hasattr(self, "last_enabled"):
520
 
                self.last_enabled = datetime.datetime.utcnow()
521
 
            if not hasattr(self, "expires"):
522
 
                self.expires = (datetime.datetime.utcnow()
523
 
                                + self.timeout)
524
 
        else:
525
 
            self.last_enabled = None
526
 
            self.expires = None
527
 
       
528
520
        logger.debug("Creating client %r", self.name)
529
521
        # Uppercase and remove spaces from fingerprint for later
530
522
        # comparison purposes with return value from the fingerprint()
531
523
        # function
532
524
        logger.debug("  Fingerprint: %s", self.fingerprint)
533
 
        self.created = settings.get("created",
534
 
                                    datetime.datetime.utcnow())
 
525
        self.created = settings.get("created", datetime.datetime.utcnow())
535
526
 
536
527
        # attributes specific for this server instance
537
528
        self.checker = None
626
617
            logger.warning("Checker for %(name)s crashed?",
627
618
                           vars(self))
628
619
    
629
 
    def checked_ok(self):
630
 
        """Assert that the client has been seen, alive and well."""
631
 
        self.last_checked_ok = datetime.datetime.utcnow()
632
 
        self.last_checker_status = 0
633
 
        self.bump_timeout()
634
 
    
635
 
    def bump_timeout(self, timeout=None):
636
 
        """Bump up the timeout for this client."""
 
620
    def checked_ok(self, timeout=None):
 
621
        """Bump up the timeout for this client.
 
622
        
 
623
        This should only be called when the client has been seen,
 
624
        alive and well.
 
625
        """
637
626
        if timeout is None:
638
627
            timeout = self.timeout
 
628
        self.last_checked_ok = datetime.datetime.utcnow()
639
629
        if self.disable_initiator_tag is not None:
640
630
            gobject.source_remove(self.disable_initiator_tag)
641
631
        if getattr(self, "enabled", False):
851
841
            # signatures other than "ay".
852
842
            if prop._dbus_signature != "ay":
853
843
                raise ValueError
854
 
            value = dbus.ByteArray(b''.join(chr(byte)
855
 
                                            for byte in value))
 
844
            value = dbus.ByteArray(''.join(unichr(byte)
 
845
                                           for byte in value))
856
846
        prop(value)
857
847
    
858
848
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
1050
1040
    def __init__(self, bus = None, *args, **kwargs):
1051
1041
        self.bus = bus
1052
1042
        Client.__init__(self, *args, **kwargs)
 
1043
        self._approvals_pending = 0
 
1044
        
 
1045
        self._approvals_pending = 0
1053
1046
        # Only now, when this client is initialized, can it show up on
1054
1047
        # the D-Bus
1055
1048
        client_object_name = unicode(self.name).translate(
1101
1094
                                       checker is not None)
1102
1095
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
1103
1096
                                           "LastCheckedOK")
1104
 
    last_checker_status = notifychangeproperty(dbus.Int16,
1105
 
                                               "LastCheckerStatus")
1106
1097
    last_approval_request = notifychangeproperty(
1107
1098
        datetime_to_dbus, "LastApprovalRequest")
1108
1099
    approved_by_default = notifychangeproperty(dbus.Boolean,
1227
1218
        "D-Bus signal"
1228
1219
        return self.need_approval()
1229
1220
    
 
1221
    # NeRwequest - signal
 
1222
    @dbus.service.signal(_interface, signature="s")
 
1223
    def NewRequest(self, ip):
 
1224
        """D-Bus signal
 
1225
        Is sent after a client request a password.
 
1226
        """
 
1227
        pass
 
1228
    
1230
1229
    ## Methods
1231
1230
    
1232
1231
    # Approve - method
1342
1341
            return
1343
1342
        return datetime_to_dbus(self.last_checked_ok)
1344
1343
    
1345
 
    # LastCheckerStatus - property
1346
 
    @dbus_service_property(_interface, signature="n",
1347
 
                           access="read")
1348
 
    def LastCheckerStatus_dbus_property(self):
1349
 
        return dbus.Int16(self.last_checker_status)
1350
 
    
1351
1344
    # Expires - property
1352
1345
    @dbus_service_property(_interface, signature="s", access="read")
1353
1346
    def Expires_dbus_property(self):
1365
1358
        if value is None:       # get
1366
1359
            return dbus.UInt64(self.timeout_milliseconds())
1367
1360
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
1361
        if getattr(self, "disable_initiator_tag", None) is None:
 
1362
            return
1368
1363
        # Reschedule timeout
1369
 
        if self.enabled:
1370
 
            now = datetime.datetime.utcnow()
1371
 
            time_to_die = timedelta_to_milliseconds(
1372
 
                (self.last_checked_ok + self.timeout) - now)
1373
 
            if time_to_die <= 0:
1374
 
                # The timeout has passed
1375
 
                self.disable()
1376
 
            else:
1377
 
                self.expires = (now +
1378
 
                                datetime.timedelta(milliseconds =
1379
 
                                                   time_to_die))
1380
 
                if (getattr(self, "disable_initiator_tag", None)
1381
 
                    is None):
1382
 
                    return
1383
 
                gobject.source_remove(self.disable_initiator_tag)
1384
 
                self.disable_initiator_tag = (gobject.timeout_add
1385
 
                                              (time_to_die,
1386
 
                                               self.disable))
 
1364
        gobject.source_remove(self.disable_initiator_tag)
 
1365
        self.disable_initiator_tag = None
 
1366
        self.expires = None
 
1367
        time_to_die = timedelta_to_milliseconds((self
 
1368
                                                 .last_checked_ok
 
1369
                                                 + self.timeout)
 
1370
                                                - datetime.datetime
 
1371
                                                .utcnow())
 
1372
        if time_to_die <= 0:
 
1373
            # The timeout has passed
 
1374
            self.disable()
 
1375
        else:
 
1376
            self.expires = (datetime.datetime.utcnow()
 
1377
                            + datetime.timedelta(milliseconds =
 
1378
                                                 time_to_die))
 
1379
            self.disable_initiator_tag = (gobject.timeout_add
 
1380
                                          (time_to_die, self.disable))
1387
1381
    
1388
1382
    # ExtendedTimeout - property
1389
1383
    @dbus_service_property(_interface, signature="t",
1545
1539
                except KeyError:
1546
1540
                    return
1547
1541
                
 
1542
                if self.server.use_dbus:
 
1543
                    # Emit D-Bus signal
 
1544
                    client.NewRequest(str(self.client_address))
 
1545
                
1548
1546
                if client.approval_delay:
1549
1547
                    delay = client.approval_delay
1550
1548
                    client.approvals_pending += 1
1614
1612
                
1615
1613
                logger.info("Sending secret to %s", client.name)
1616
1614
                # bump the timeout using extended_timeout
1617
 
                client.bump_timeout(client.extended_timeout)
 
1615
                client.checked_ok(client.extended_timeout)
1618
1616
                if self.server.use_dbus:
1619
1617
                    # Emit D-Bus signal
1620
1618
                    client.GotSecret()
2096
2094
                                % server_settings["servicename"]))
2097
2095
    
2098
2096
    # Parse config file with clients
2099
 
    client_config = configparser.SafeConfigParser(Client
2100
 
                                                  .client_defaults)
 
2097
    client_config = configparser.SafeConfigParser(Client.client_defaults)
2101
2098
    client_config.read(os.path.join(server_settings["configdir"],
2102
2099
                                    "clients.conf"))
2103
2100
    
2244
2241
            
2245
2242
            # Clients who has passed its expire date can still be
2246
2243
            # enabled if its last checker was successful.  Clients
2247
 
            # whose checker succeeded before we stored its state is
2248
 
            # assumed to have successfully run all checkers during
2249
 
            # downtime.
 
2244
            # whose checker failed before we stored its state is
 
2245
            # assumed to have failed all checkers during downtime.
2250
2246
            if client["enabled"]:
2251
2247
                if datetime.datetime.utcnow() >= client["expires"]:
2252
2248
                    if not client["last_checked_ok"]:
2253
2249
                        logger.warning(
2254
2250
                            "disabling client {0} - Client never "
2255
 
                            "performed a successful checker"
2256
 
                            .format(client_name))
 
2251
                            "performed a successfull checker"
 
2252
                            .format(client["name"]))
2257
2253
                        client["enabled"] = False
2258
2254
                    elif client["last_checker_status"] != 0:
2259
2255
                        logger.warning(
2260
2256
                            "disabling client {0} - Client "
2261
2257
                            "last checker failed with error code {1}"
2262
 
                            .format(client_name,
 
2258
                            .format(client["name"],
2263
2259
                                    client["last_checker_status"]))
2264
2260
                        client["enabled"] = False
2265
2261
                    else:
2266
2262
                        client["expires"] = (datetime.datetime
2267
2263
                                             .utcnow()
2268
2264
                                             + client["timeout"])
2269
 
                        logger.debug("Last checker succeeded,"
2270
 
                                     " keeping {0} enabled"
2271
 
                                     .format(client_name))
 
2265
                    
2272
2266
            try:
2273
2267
                client["secret"] = (
2274
2268
                    pgp.decrypt(client["encrypted_secret"],
2283
2277
 
2284
2278
    
2285
2279
    # Add/remove clients based on new changes made to config
2286
 
    for client_name in (set(old_client_settings)
2287
 
                        - set(client_settings)):
 
2280
    for client_name in set(old_client_settings) - set(client_settings):
2288
2281
        del clients_data[client_name]
2289
 
    for client_name in (set(client_settings)
2290
 
                        - set(old_client_settings)):
 
2282
    for client_name in set(client_settings) - set(old_client_settings):
2291
2283
        clients_data[client_name] = client_settings[client_name]
2292
2284
 
2293
 
    # Create all client objects
 
2285
    # Create clients all clients
2294
2286
    for client_name, client in clients_data.iteritems():
2295
2287
        tcp_server.clients[client_name] = client_class(
2296
2288
            name = client_name, settings = client)