/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

working new feature: network-hooks - Enables user-scripts to take up
                     interfaces during bootup

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.1"
 
88
version = "1.4.1"
89
89
stored_state_file = "clients.pickle"
90
90
 
91
91
logger = logging.getLogger()
110
110
        return interface_index
111
111
 
112
112
 
113
 
def initlogger(debug, level=logging.WARNING):
 
113
def initlogger(level=logging.WARNING):
114
114
    """init logger and add loglevel"""
115
115
    
116
116
    syslogger.setFormatter(logging.Formatter
118
118
                            ' %(message)s'))
119
119
    logger.addHandler(syslogger)
120
120
    
121
 
    if debug:
122
 
        console = logging.StreamHandler()
123
 
        console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
124
 
                                               ' [%(process)d]:'
125
 
                                               ' %(levelname)s:'
126
 
                                               ' %(message)s'))
127
 
        logger.addHandler(console)
 
121
    console = logging.StreamHandler()
 
122
    console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
 
123
                                           ' [%(process)d]:'
 
124
                                           ' %(levelname)s:'
 
125
                                           ' %(message)s'))
 
126
    logger.addHandler(console)
128
127
    logger.setLevel(level)
129
128
 
130
129
 
142
141
        self.gnupg.options.meta_interactive = False
143
142
        self.gnupg.options.homedir = self.tempdir
144
143
        self.gnupg.options.extra_args.extend(['--force-mdc',
145
 
                                              '--quiet',
146
 
                                              '--no-use-agent'])
 
144
                                              '--quiet'])
147
145
    
148
146
    def __enter__(self):
149
147
        return self
422
420
    secret:     bytestring; sent verbatim (over TLS) to client
423
421
    timeout:    datetime.timedelta(); How long from last_checked_ok
424
422
                                      until this client is disabled
425
 
    extended_timeout:   extra long timeout when secret has been sent
 
423
    extended_timeout:   extra long timeout when password has been sent
426
424
    runtime_expansions: Allowed attributes for runtime expansion.
427
425
    expires:    datetime.datetime(); time (UTC) when a client will be
428
426
                disabled, or None
460
458
 
461
459
    @staticmethod
462
460
    def config_parser(config):
463
 
        """Construct a new dict of client settings of this form:
 
461
        """ Construct a new dict of client settings of this form:
464
462
        { 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
 
        """
 
463
        with exceptions for any special settings as defined above"""
469
464
        settings = {}
470
465
        for client_name in config.sections():
471
466
            section = dict(config.items(client_name))
475
470
            # Reformat values from string types to Python types
476
471
            client["approved_by_default"] = config.getboolean(
477
472
                client_name, "approved_by_default")
478
 
            client["enabled"] = config.getboolean(client_name,
479
 
                                                  "enabled")
 
473
            client["enabled"] = config.getboolean(client_name, "enabled")
480
474
            
481
475
            client["fingerprint"] = (section["fingerprint"].upper()
482
476
                                     .replace(" ", ""))
502
496
            client["last_approval_request"] = None
503
497
            client["last_checked_ok"] = None
504
498
            client["last_checker_status"] = None
505
 
        
 
499
            if client["enabled"]:
 
500
                client["last_enabled"] = datetime.datetime.utcnow()
 
501
                client["expires"] = (datetime.datetime.utcnow()
 
502
                                     + client["timeout"])
 
503
            else:
 
504
                client["last_enabled"] = None
 
505
                client["expires"] = None
 
506
 
506
507
        return settings
507
508
        
508
509
        
515
516
        for setting, value in settings.iteritems():
516
517
            setattr(self, setting, value)
517
518
        
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
519
        logger.debug("Creating client %r", self.name)
529
520
        # Uppercase and remove spaces from fingerprint for later
530
521
        # comparison purposes with return value from the fingerprint()
531
522
        # function
532
523
        logger.debug("  Fingerprint: %s", self.fingerprint)
533
 
        self.created = settings.get("created",
534
 
                                    datetime.datetime.utcnow())
 
524
        self.created = settings.get("created", datetime.datetime.utcnow())
535
525
 
536
526
        # attributes specific for this server instance
537
527
        self.checker = None
850
840
            # signatures other than "ay".
851
841
            if prop._dbus_signature != "ay":
852
842
                raise ValueError
853
 
            value = dbus.ByteArray(b''.join(chr(byte)
854
 
                                            for byte in value))
 
843
            value = dbus.ByteArray(''.join(unichr(byte)
 
844
                                           for byte in value))
855
845
        prop(value)
856
846
    
857
847
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
1049
1039
    def __init__(self, bus = None, *args, **kwargs):
1050
1040
        self.bus = bus
1051
1041
        Client.__init__(self, *args, **kwargs)
 
1042
        self._approvals_pending = 0
 
1043
        
 
1044
        self._approvals_pending = 0
1052
1045
        # Only now, when this client is initialized, can it show up on
1053
1046
        # the D-Bus
1054
1047
        client_object_name = unicode(self.name).translate(
1224
1217
        "D-Bus signal"
1225
1218
        return self.need_approval()
1226
1219
    
1227
 
    # NewRequest - signal
 
1220
    # NeRwequest - signal
1228
1221
    @dbus.service.signal(_interface, signature="s")
1229
1222
    def NewRequest(self, ip):
1230
1223
        """D-Bus signal
1231
 
        Is sent after a client request a secret.
 
1224
        Is sent after a client request a password.
1232
1225
        """
1233
1226
        pass
1234
1227
    
1364
1357
        if value is None:       # get
1365
1358
            return dbus.UInt64(self.timeout_milliseconds())
1366
1359
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
1360
        if getattr(self, "disable_initiator_tag", None) is None:
 
1361
            return
1367
1362
        # Reschedule timeout
1368
 
        if self.enabled:
1369
 
            now = datetime.datetime.utcnow()
1370
 
            time_to_die = timedelta_to_milliseconds(
1371
 
                (self.last_checked_ok + self.timeout) - now)
1372
 
            if time_to_die <= 0:
1373
 
                # The timeout has passed
1374
 
                self.disable()
1375
 
            else:
1376
 
                self.expires = (now +
1377
 
                                datetime.timedelta(milliseconds =
1378
 
                                                   time_to_die))
1379
 
                if (getattr(self, "disable_initiator_tag", None)
1380
 
                    is None):
1381
 
                    return
1382
 
                gobject.source_remove(self.disable_initiator_tag)
1383
 
                self.disable_initiator_tag = (gobject.timeout_add
1384
 
                                              (time_to_die,
1385
 
                                               self.disable))
 
1363
        gobject.source_remove(self.disable_initiator_tag)
 
1364
        self.disable_initiator_tag = None
 
1365
        self.expires = None
 
1366
        time_to_die = timedelta_to_milliseconds((self
 
1367
                                                 .last_checked_ok
 
1368
                                                 + self.timeout)
 
1369
                                                - datetime.datetime
 
1370
                                                .utcnow())
 
1371
        if time_to_die <= 0:
 
1372
            # The timeout has passed
 
1373
            self.disable()
 
1374
        else:
 
1375
            self.expires = (datetime.datetime.utcnow()
 
1376
                            + datetime.timedelta(milliseconds =
 
1377
                                                 time_to_die))
 
1378
            self.disable_initiator_tag = (gobject.timeout_add
 
1379
                                          (time_to_die, self.disable))
1386
1380
    
1387
1381
    # ExtendedTimeout - property
1388
1382
    @dbus_service_property(_interface, signature="t",
1546
1540
                
1547
1541
                if self.server.use_dbus:
1548
1542
                    # Emit D-Bus signal
1549
 
                    client.NewRequest(unicode(self.client_address)[0])
 
1543
                    client.NewRequest(str(self.client_address))
1550
1544
                
1551
1545
                if client.approval_delay:
1552
1546
                    delay = client.approval_delay
2084
2078
                                     stored_state_file)
2085
2079
    
2086
2080
    if debug:
2087
 
        initlogger(debug, logging.DEBUG)
 
2081
        initlogger(logging.DEBUG)
2088
2082
    else:
2089
2083
        if not debuglevel:
2090
 
            initlogger(debug)
 
2084
            initlogger()
2091
2085
        else:
2092
2086
            level = getattr(logging, debuglevel.upper())
2093
 
            initlogger(debug, level)
 
2087
            initlogger(level)
2094
2088
    
2095
2089
    if server_settings["servicename"] != "Mandos":
2096
2090
        syslogger.setFormatter(logging.Formatter
2099
2093
                                % server_settings["servicename"]))
2100
2094
    
2101
2095
    # Parse config file with clients
2102
 
    client_config = configparser.SafeConfigParser(Client
2103
 
                                                  .client_defaults)
 
2096
    client_config = configparser.SafeConfigParser(Client.client_defaults)
2104
2097
    client_config.read(os.path.join(server_settings["configdir"],
2105
2098
                                    "clients.conf"))
2106
2099
    
2163
2156
        os.dup2(null, sys.stdin.fileno())
2164
2157
        if null > 2:
2165
2158
            os.close(null)
 
2159
    else:
 
2160
        # No console logging
 
2161
        logger.removeHandler(console)
2166
2162
    
2167
2163
    # Need to fork before connecting to D-Bus
2168
2164
    if not debug:
2169
2165
        # Close all input and output, do double fork, etc.
2170
2166
        daemon()
2171
2167
    
2172
 
    gobject.threads_init()
2173
 
    
2174
2168
    global main_loop
2175
2169
    # From the Avahi example code
2176
2170
    DBusGMainLoop(set_as_default=True )
2222
2216
                           .format(e))
2223
2217
            if e.errno != errno.ENOENT:
2224
2218
                raise
2225
 
        except EOFError as e:
2226
 
            logger.warning("Could not load persistent state: "
2227
 
                           "EOFError: {0}".format(e))
2228
2219
    
2229
2220
    with PGPEngine() as pgp:
2230
2221
        for client_name, client in clients_data.iteritems():
2247
2238
            
2248
2239
            # Clients who has passed its expire date can still be
2249
2240
            # enabled if its last checker was successful.  Clients
2250
 
            # whose checker succeeded before we stored its state is
2251
 
            # assumed to have successfully run all checkers during
2252
 
            # downtime.
 
2241
            # whose checker failed before we stored its state is
 
2242
            # assumed to have failed all checkers during downtime.
2253
2243
            if client["enabled"]:
2254
2244
                if datetime.datetime.utcnow() >= client["expires"]:
2255
2245
                    if not client["last_checked_ok"]:
2256
2246
                        logger.warning(
2257
2247
                            "disabling client {0} - Client never "
2258
 
                            "performed a successful checker"
2259
 
                            .format(client_name))
 
2248
                            "performed a successfull checker"
 
2249
                            .format(client["name"]))
2260
2250
                        client["enabled"] = False
2261
2251
                    elif client["last_checker_status"] != 0:
2262
2252
                        logger.warning(
2263
2253
                            "disabling client {0} - Client "
2264
2254
                            "last checker failed with error code {1}"
2265
 
                            .format(client_name,
 
2255
                            .format(client["name"],
2266
2256
                                    client["last_checker_status"]))
2267
2257
                        client["enabled"] = False
2268
2258
                    else:
2269
2259
                        client["expires"] = (datetime.datetime
2270
2260
                                             .utcnow()
2271
2261
                                             + client["timeout"])
2272
 
                        logger.debug("Last checker succeeded,"
2273
 
                                     " keeping {0} enabled"
2274
 
                                     .format(client_name))
 
2262
                    
2275
2263
            try:
2276
2264
                client["secret"] = (
2277
2265
                    pgp.decrypt(client["encrypted_secret"],
2286
2274
 
2287
2275
    
2288
2276
    # Add/remove clients based on new changes made to config
2289
 
    for client_name in (set(old_client_settings)
2290
 
                        - set(client_settings)):
 
2277
    for client_name in set(old_client_settings) - set(client_settings):
2291
2278
        del clients_data[client_name]
2292
 
    for client_name in (set(client_settings)
2293
 
                        - set(old_client_settings)):
 
2279
    for client_name in set(client_settings) - set(old_client_settings):
2294
2280
        clients_data[client_name] = client_settings[client_name]
2295
2281
 
2296
 
    # Create all client objects
 
2282
    # Create clients all clients
2297
2283
    for client_name, client in clients_data.iteritems():
2298
2284
        tcp_server.clients[client_name] = client_class(
2299
2285
            name = client_name, settings = client)
2414
2400
                del client_settings[client.name]["secret"]
2415
2401
        
2416
2402
        try:
2417
 
            tempfd, tempname = tempfile.mkstemp(suffix=".pickle",
2418
 
                                                prefix="clients-",
2419
 
                                                dir=os.path.dirname
2420
 
                                                (stored_state_path))
2421
 
            with os.fdopen(tempfd, "wb") as stored_state:
 
2403
            with os.fdopen(os.open(stored_state_path,
 
2404
                                   os.O_CREAT|os.O_WRONLY|os.O_TRUNC,
 
2405
                                   0600), "wb") as stored_state:
2422
2406
                pickle.dump((clients, client_settings), stored_state)
2423
 
            os.rename(tempname, stored_state_path)
2424
2407
        except (IOError, OSError) as e:
2425
2408
            logger.warning("Could not save persistent state: {0}"
2426
2409
                           .format(e))
2427
 
            if not debug:
2428
 
                try:
2429
 
                    os.remove(tempname)
2430
 
                except NameError:
2431
 
                    pass
2432
 
            if e.errno not in set((errno.ENOENT, errno.EACCES,
2433
 
                                   errno.EEXIST)):
2434
 
                raise e
 
2410
            if e.errno not in (errno.ENOENT, errno.EACCES):
 
2411
                raise
2435
2412
        
2436
2413
        # Delete all clients, and settings from config
2437
2414
        while tcp_server.clients: