/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: 2011-11-26 23:08:17 UTC
  • mto: (518.1.8 mandos-persistent)
  • mto: This revision was merged to the branch mainline in revision 524.
  • Revision ID: teddy@recompile.se-20111126230817-tv08v831s2yltbkd
Make "enabled" a client config option.

* DBUS-API: Fix wording on "Expires" option.
* clients.conf (enabled): New.
* mandos (Client): "last_enabled" can now be None.
  (Client.__init__): Get "enabled" from config.  Only set
                     "last_enabled" and "expires" if enabled.
  (ClientDBus.Created_dbus_property): Removed redundant dbus.String().
  (ClientDBus.Interval_dbus_property): If changed, only reschedule
                                       checker if enabled.
  (main/special_settings): Added "enabled".
* mandos-clients.conf (OPTIONS): Added "enabled".

Show diffs side-by-side

added added

removed removed

Lines of Context:
128
128
    logger.setLevel(level)
129
129
 
130
130
 
131
 
class PGPError(Exception):
132
 
    """Exception if encryption/decryption fails"""
 
131
class CryptoError(Exception):
133
132
    pass
134
133
 
135
134
 
136
 
class PGPEngine(object):
 
135
class Crypto(object):
137
136
    """A simple class for OpenPGP symmetric encryption & decryption"""
138
137
    def __init__(self):
139
138
        self.gnupg = GnuPGInterface.GnuPG()
185
184
                    ciphertext = f.read()
186
185
                proc.wait()
187
186
            except IOError as e:
188
 
                raise PGPError(e)
 
187
                raise CryptoError(e)
189
188
        self.gnupg.passphrase = None
190
189
        return ciphertext
191
190
    
202
201
                    decrypted_plaintext = f.read()
203
202
                proc.wait()
204
203
            except IOError as e:
205
 
                raise PGPError(e)
 
204
                raise CryptoError(e)
206
205
        self.gnupg.passphrase = None
207
206
        return decrypted_plaintext
208
207
 
378
377
                                % self.name))
379
378
        return ret
380
379
 
381
 
def timedelta_to_milliseconds(td):
 
380
def _timedelta_to_milliseconds(td):
382
381
    "Convert a datetime.timedelta() to milliseconds"
383
382
    return ((td.days * 24 * 60 * 60 * 1000)
384
383
            + (td.seconds * 1000)
388
387
    """A representation of a client host served by this server.
389
388
    
390
389
    Attributes:
391
 
    approved:   bool(); 'None' if not yet approved/disapproved
 
390
    _approved:   bool(); 'None' if not yet approved/disapproved
392
391
    approval_delay: datetime.timedelta(); Time to wait for approval
393
392
    approval_duration: datetime.timedelta(); Duration of one approval
394
393
    checker:    subprocess.Popen(); a running checker process used
435
434
    
436
435
    def timeout_milliseconds(self):
437
436
        "Return the 'timeout' attribute in milliseconds"
438
 
        return timedelta_to_milliseconds(self.timeout)
 
437
        return _timedelta_to_milliseconds(self.timeout)
439
438
    
440
439
    def extended_timeout_milliseconds(self):
441
440
        "Return the 'extended_timeout' attribute in milliseconds"
442
 
        return timedelta_to_milliseconds(self.extended_timeout)
 
441
        return _timedelta_to_milliseconds(self.extended_timeout)
443
442
    
444
443
    def interval_milliseconds(self):
445
444
        "Return the 'interval' attribute in milliseconds"
446
 
        return timedelta_to_milliseconds(self.interval)
 
445
        return _timedelta_to_milliseconds(self.interval)
447
446
    
448
447
    def approval_delay_milliseconds(self):
449
 
        return timedelta_to_milliseconds(self.approval_delay)
 
448
        return _timedelta_to_milliseconds(self.approval_delay)
450
449
    
451
450
    def __init__(self, name = None, config=None):
452
451
        """Note: the 'checker' key in 'config' sets the
496
495
        self.checker_callback_tag = None
497
496
        self.checker_command = config["checker"]
498
497
        self.current_checker_command = None
499
 
        self.approved = None
 
498
        self._approved = None
500
499
        self.approved_by_default = config.get("approved_by_default",
501
500
                                              True)
502
501
        self.approvals_pending = 0
576
575
        self.checker_callback_tag = None
577
576
        self.checker = None
578
577
        if os.WIFEXITED(condition):
579
 
            self.last_checker_status = os.WEXITSTATUS(condition)
 
578
            self.last_checker_status =  os.WEXITSTATUS(condition)
580
579
            if self.last_checker_status == 0:
581
580
                logger.info("Checker for %(name)s succeeded",
582
581
                            vars(self))
602
601
            gobject.source_remove(self.disable_initiator_tag)
603
602
        if getattr(self, "enabled", False):
604
603
            self.disable_initiator_tag = (gobject.timeout_add
605
 
                                          (timedelta_to_milliseconds
 
604
                                          (_timedelta_to_milliseconds
606
605
                                           (timeout), self.disable))
607
606
            self.expires = datetime.datetime.utcnow() + timeout
608
607
    
1069
1068
        datetime_to_dbus, "LastApprovalRequest")
1070
1069
    approved_by_default = notifychangeproperty(dbus.Boolean,
1071
1070
                                               "ApprovedByDefault")
1072
 
    approval_delay = notifychangeproperty(dbus.UInt64,
 
1071
    approval_delay = notifychangeproperty(dbus.UInt16,
1073
1072
                                          "ApprovalDelay",
1074
1073
                                          type_func =
1075
 
                                          timedelta_to_milliseconds)
 
1074
                                          _timedelta_to_milliseconds)
1076
1075
    approval_duration = notifychangeproperty(
1077
 
        dbus.UInt64, "ApprovalDuration",
1078
 
        type_func = timedelta_to_milliseconds)
 
1076
        dbus.UInt16, "ApprovalDuration",
 
1077
        type_func = _timedelta_to_milliseconds)
1079
1078
    host = notifychangeproperty(dbus.String, "Host")
1080
 
    timeout = notifychangeproperty(dbus.UInt64, "Timeout",
 
1079
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
1081
1080
                                   type_func =
1082
 
                                   timedelta_to_milliseconds)
 
1081
                                   _timedelta_to_milliseconds)
1083
1082
    extended_timeout = notifychangeproperty(
1084
 
        dbus.UInt64, "ExtendedTimeout",
1085
 
        type_func = timedelta_to_milliseconds)
1086
 
    interval = notifychangeproperty(dbus.UInt64,
 
1083
        dbus.UInt16, "ExtendedTimeout",
 
1084
        type_func = _timedelta_to_milliseconds)
 
1085
    interval = notifychangeproperty(dbus.UInt16,
1087
1086
                                    "Interval",
1088
1087
                                    type_func =
1089
 
                                    timedelta_to_milliseconds)
 
1088
                                    _timedelta_to_milliseconds)
1090
1089
    checker_command = notifychangeproperty(dbus.String, "Checker")
1091
1090
    
1092
1091
    del notifychangeproperty
1134
1133
        return r
1135
1134
    
1136
1135
    def _reset_approved(self):
1137
 
        self.approved = None
 
1136
        self._approved = None
1138
1137
        return False
1139
1138
    
1140
1139
    def approve(self, value=True):
1141
1140
        self.send_changedstate()
1142
 
        self.approved = value
1143
 
        gobject.timeout_add(timedelta_to_milliseconds
 
1141
        self._approved = value
 
1142
        gobject.timeout_add(_timedelta_to_milliseconds
1144
1143
                            (self.approval_duration),
1145
1144
                            self._reset_approved)
1146
1145
    
1260
1259
                           access="readwrite")
1261
1260
    def ApprovalDuration_dbus_property(self, value=None):
1262
1261
        if value is None:       # get
1263
 
            return dbus.UInt64(timedelta_to_milliseconds(
 
1262
            return dbus.UInt64(_timedelta_to_milliseconds(
1264
1263
                    self.approval_duration))
1265
1264
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1266
1265
    
1280
1279
    def Host_dbus_property(self, value=None):
1281
1280
        if value is None:       # get
1282
1281
            return dbus.String(self.host)
1283
 
        self.host = unicode(value)
 
1282
        self.host = value
1284
1283
    
1285
1284
    # Created - property
1286
1285
    @dbus_service_property(_interface, signature="s", access="read")
1335
1334
        gobject.source_remove(self.disable_initiator_tag)
1336
1335
        self.disable_initiator_tag = None
1337
1336
        self.expires = None
1338
 
        time_to_die = timedelta_to_milliseconds((self
1339
 
                                                 .last_checked_ok
1340
 
                                                 + self.timeout)
1341
 
                                                - datetime.datetime
1342
 
                                                .utcnow())
 
1337
        time_to_die = _timedelta_to_milliseconds((self
 
1338
                                                  .last_checked_ok
 
1339
                                                  + self.timeout)
 
1340
                                                 - datetime.datetime
 
1341
                                                 .utcnow())
1343
1342
        if time_to_die <= 0:
1344
1343
            # The timeout has passed
1345
1344
            self.disable()
1380
1379
    def Checker_dbus_property(self, value=None):
1381
1380
        if value is None:       # get
1382
1381
            return dbus.String(self.checker_command)
1383
 
        self.checker_command = unicode(value)
 
1382
        self.checker_command = value
1384
1383
    
1385
1384
    # CheckerRunning - property
1386
1385
    @dbus_service_property(_interface, signature="b",
1415
1414
            raise KeyError()
1416
1415
    
1417
1416
    def __getattribute__(self, name):
1418
 
        if name == '_pipe':
 
1417
        if(name == '_pipe'):
1419
1418
            return super(ProxyClient, self).__getattribute__(name)
1420
1419
        self._pipe.send(('getattr', name))
1421
1420
        data = self._pipe.recv()
1428
1427
            return func
1429
1428
    
1430
1429
    def __setattr__(self, name, value):
1431
 
        if name == '_pipe':
 
1430
        if(name == '_pipe'):
1432
1431
            return super(ProxyClient, self).__setattr__(name, value)
1433
1432
        self._pipe.send(('setattr', name, value))
1434
1433
 
1503
1502
                    logger.warning("Bad certificate: %s", error)
1504
1503
                    return
1505
1504
                logger.debug("Fingerprint: %s", fpr)
 
1505
                if self.server.use_dbus:
 
1506
                    # Emit D-Bus signal
 
1507
                    client.NewRequest(str(self.client_address))
1506
1508
                
1507
1509
                try:
1508
1510
                    client = ProxyClient(child_pipe, fpr,
1510
1512
                except KeyError:
1511
1513
                    return
1512
1514
                
1513
 
                if self.server.use_dbus:
1514
 
                    # Emit D-Bus signal
1515
 
                    client.NewRequest(str(self.client_address))
1516
 
                
1517
1515
                if client.approval_delay:
1518
1516
                    delay = client.approval_delay
1519
1517
                    client.approvals_pending += 1
1528
1526
                            client.Rejected("Disabled")
1529
1527
                        return
1530
1528
                    
1531
 
                    if client.approved or not client.approval_delay:
 
1529
                    if client._approved or not client.approval_delay:
1532
1530
                        #We are approved or approval is disabled
1533
1531
                        break
1534
 
                    elif client.approved is None:
 
1532
                    elif client._approved is None:
1535
1533
                        logger.info("Client %s needs approval",
1536
1534
                                    client.name)
1537
1535
                        if self.server.use_dbus:
1551
1549
                    time = datetime.datetime.now()
1552
1550
                    client.changedstate.acquire()
1553
1551
                    (client.changedstate.wait
1554
 
                     (float(client.timedelta_to_milliseconds(delay)
 
1552
                     (float(client._timedelta_to_milliseconds(delay)
1555
1553
                            / 1000)))
1556
1554
                    client.changedstate.release()
1557
1555
                    time2 = datetime.datetime.now()
2219
2217
            if e.errno != errno.ENOENT:
2220
2218
                raise
2221
2219
    
2222
 
    with PGPEngine() as pgp:
 
2220
    with Crypto() as crypt:
2223
2221
        for client in clients_data:
2224
2222
            client_name = client["name"]
2225
2223
            
2236
2234
                    if (name != "secret" and
2237
2235
                        value != old_client_settings[client_name]
2238
2236
                        [name]):
2239
 
                        client[name] = value
 
2237
                        setattr(client, name, value)
2240
2238
                except KeyError:
2241
2239
                    pass
2242
2240
            
2244
2242
            # enabled if its last checker was sucessful.  Clients
2245
2243
            # whose checker failed before we stored its state is
2246
2244
            # assumed to have failed all checkers during downtime.
2247
 
            if client["enabled"]:
2248
 
                if client["expires"] <= (datetime.datetime
2249
 
                                         .utcnow()):
2250
 
                    # Client has expired
 
2245
            if client["enabled"] and client["last_checked_ok"]:
 
2246
                if ((datetime.datetime.utcnow()
 
2247
                     - client["last_checked_ok"])
 
2248
                    > client["interval"]):
2251
2249
                    if client["last_checker_status"] != 0:
2252
2250
                        client["enabled"] = False
2253
2251
                    else:
2285
2283
            
2286
2284
            try:
2287
2285
                tcp_server.clients[client_name].secret = (
2288
 
                    pgp.decrypt(tcp_server.clients[client_name]
2289
 
                                .encrypted_secret,
2290
 
                                client_settings[client_name]
2291
 
                                ["secret"]))
2292
 
            except PGPError:
 
2286
                    crypt.decrypt(tcp_server.clients[client_name]
 
2287
                                  .encrypted_secret,
 
2288
                                  client_settings[client_name]
 
2289
                                  ["secret"]))
 
2290
            except CryptoError:
2293
2291
                # If decryption fails, we use secret from new settings
2294
 
                logger.debug("Failed to decrypt {0} old secret"
2295
 
                             .format(client_name))
2296
2292
                tcp_server.clients[client_name].secret = (
2297
2293
                    client_settings[client_name]["secret"])
2298
2294
    
2398
2394
        # based on what config file has. If config file is
2399
2395
        # removed/edited, old secret will thus be unrecovable.
2400
2396
        clients = []
2401
 
        with PGPEngine() as pgp:
 
2397
        with Crypto() as crypt:
2402
2398
            for client in tcp_server.clients.itervalues():
2403
2399
                key = client_settings[client.name]["secret"]
2404
 
                client.encrypted_secret = pgp.encrypt(client.secret,
2405
 
                                                      key)
 
2400
                client.encrypted_secret = crypt.encrypt(client.secret,
 
2401
                                                        key)
2406
2402
                client_dict = {}
2407
2403
                
2408
2404
                # A list of attributes that will not be stored when