358
365
self.host = config.get("host", "")
359
366
self.created = datetime.datetime.utcnow()
361
368
self.last_approval_request = None
362
self.last_enabled = None
369
self.last_enabled = datetime.datetime.utcnow()
363
370
self.last_checked_ok = None
371
self.last_checker_status = 0
364
372
self.timeout = string_to_delta(config["timeout"])
365
373
self.extended_timeout = string_to_delta(config
366
374
["extended_timeout"])
367
375
self.interval = string_to_delta(config["interval"])
368
self.disable_hook = disable_hook
369
376
self.checker = None
370
377
self.checker_initiator_tag = None
371
378
self.disable_initiator_tag = None
379
self.expires = datetime.datetime.utcnow() + self.timeout
373
380
self.checker_callback_tag = None
374
381
self.checker_command = config["checker"]
375
382
self.current_checker_command = None
376
self.last_connect = None
377
383
self._approved = None
378
384
self.approved_by_default = config.get("approved_by_default",
397
412
# Already enabled
399
414
self.send_changedstate()
400
# Schedule a new checker to be started an 'interval' from now,
401
# and every interval from then on.
402
self.checker_initiator_tag = (gobject.timeout_add
403
(self.interval_milliseconds(),
405
# Schedule a disable() when 'timeout' has passed
406
415
self.expires = datetime.datetime.utcnow() + self.timeout
407
self.disable_initiator_tag = (gobject.timeout_add
408
(self.timeout_milliseconds(),
410
416
self.enabled = True
411
417
self.last_enabled = datetime.datetime.utcnow()
412
# Also start a new checker *right now*.
415
420
def disable(self, quiet=True):
416
421
"""Disable this client."""
428
433
gobject.source_remove(self.checker_initiator_tag)
429
434
self.checker_initiator_tag = None
430
435
self.stop_checker()
431
if self.disable_hook:
432
self.disable_hook(self)
433
436
self.enabled = False
434
437
# Do not run this again if called by a gobject.timeout_add
437
440
def __del__(self):
438
self.disable_hook = None
443
def init_checker(self):
444
# Schedule a new checker to be started an 'interval' from now,
445
# and every interval from then on.
446
self.checker_initiator_tag = (gobject.timeout_add
447
(self.interval_milliseconds(),
449
# Schedule a disable() when 'timeout' has passed
450
self.disable_initiator_tag = (gobject.timeout_add
451
(self.timeout_milliseconds(),
453
# Also start a new checker *right now*.
441
457
def checker_callback(self, pid, condition, command):
442
458
"""The checker has completed, so take appropriate actions."""
443
459
self.checker_callback_tag = None
444
460
self.checker = None
445
461
if os.WIFEXITED(condition):
446
exitstatus = os.WEXITSTATUS(condition)
462
self.last_checker_status = os.WEXITSTATUS(condition)
463
if self.last_checker_status == 0:
448
464
logger.info("Checker for %(name)s succeeded",
450
466
self.checked_ok()
569
586
self.checker = None
588
# Encrypts a client secret and stores it in a varible encrypted_secret
589
def encrypt_secret(self, key):
590
# Encryption-key need to be specific size, so we hash inputed key
591
hasheng = hashlib.sha256()
593
encryptionkey = hasheng.digest()
595
# Create validation hash so we know at decryption if it was sucessful
596
hasheng = hashlib.sha256()
597
hasheng.update(self.secret)
598
validationhash = hasheng.digest()
601
iv = os.urandom(Crypto.Cipher.AES.block_size)
602
ciphereng = Crypto.Cipher.AES.new(encryptionkey,
603
Crypto.Cipher.AES.MODE_CFB, iv)
604
ciphertext = ciphereng.encrypt(validationhash+self.secret)
605
self.encrypted_secret = (ciphertext, iv)
607
# Decrypt a encrypted client secret
608
def decrypt_secret(self, key):
609
# Decryption-key need to be specific size, so we hash inputed key
610
hasheng = hashlib.sha256()
612
encryptionkey = hasheng.digest()
614
# Decrypt encrypted secret
615
ciphertext, iv = self.encrypted_secret
616
ciphereng = Crypto.Cipher.AES.new(encryptionkey,
617
Crypto.Cipher.AES.MODE_CFB, iv)
618
plain = ciphereng.decrypt(ciphertext)
620
# Validate decrypted secret to know if it was succesful
621
hasheng = hashlib.sha256()
622
validationhash = plain[:hasheng.digest_size]
623
secret = plain[hasheng.digest_size:]
624
hasheng.update(secret)
626
# if validation fails, we use key as new secret. Otherwhise, we use
627
# the decrypted secret
628
if hasheng.digest() == validationhash:
632
del self.encrypted_secret
572
635
def dbus_service_property(dbus_interface, signature="v",
573
636
access="readwrite", byte_arrays=False):
2039
2107
client_class = functools.partial(ClientDBusTransitional,
2041
def client_config_items(config, section):
2042
special_settings = {
2043
"approved_by_default":
2044
lambda: config.getboolean(section,
2045
"approved_by_default"),
2047
for name, value in config.items(section):
2110
special_settings = {
2111
# Some settings need to be accessd by special methods;
2112
# booleans need .getboolean(), etc. Here is a list of them:
2113
"approved_by_default":
2115
client_config.getboolean(section, "approved_by_default"),
2117
# Construct a new dict of client settings of this form:
2118
# { client_name: {setting_name: value, ...}, ...}
2119
# with exceptions for any special settings as defined above
2120
client_settings = dict((clientname,
2122
(value if setting not in special_settings
2123
else special_settings[setting](clientname)))
2124
for setting, value in client_config.items(clientname)))
2125
for clientname in client_config.sections())
2127
old_client_settings = {}
2130
if server_settings["restore"]:
2132
with open(stored_state_path, "rb") as stored_state:
2133
clients_data, old_client_settings = pickle.load(stored_state)
2134
os.remove(stored_state_path)
2135
except IOError as e:
2136
logger.warning("Could not load persistant state: {0}".format(e))
2137
if e.errno != errno.ENOENT:
2140
for client in clients_data:
2141
client_name = client["name"]
2143
# Decide which value to use after restoring saved state.
2144
# We have three different values: Old config file,
2145
# new config file, and saved state.
2146
# New config value takes precedence if it differs from old
2147
# config value, otherwise use saved state.
2148
for name, value in client_settings[client_name].items():
2049
yield (name, special_settings[name]())
2150
# For each value in new config, check if it differs
2151
# from the old config value (Except for the "secret"
2153
if name != "secret" and value != old_client_settings[client_name][name]:
2154
setattr(client, name, value)
2050
2155
except KeyError:
2158
# Clients who has passed its expire date, can still be enabled if its
2159
# last checker was sucessful. Clients who checkers failed before we
2160
# stored it state is asumed to had failed checker during downtime.
2161
if client["enabled"] and client["last_checked_ok"]:
2162
if ((datetime.datetime.utcnow() - client["last_checked_ok"])
2163
> client["interval"]):
2164
if client["last_checker_status"] != 0:
2165
client["enabled"] = False
2167
client["expires"] = datetime.datetime.utcnow() + client["timeout"]
2169
client["changedstate"] = (multiprocessing_manager
2170
.Condition(multiprocessing_manager
2173
new_client = ClientDBusTransitional.__new__(ClientDBusTransitional)
2174
tcp_server.clients[client_name] = new_client
2175
new_client.bus = bus
2176
for name, value in client.iteritems():
2177
setattr(new_client, name, value)
2178
client_object_name = unicode(client_name).translate(
2179
{ord("."): ord("_"),
2180
ord("-"): ord("_")})
2181
new_client.dbus_object_path = (dbus.ObjectPath
2182
("/clients/" + client_object_name))
2183
DBusObjectWithProperties.__init__(new_client,
2185
new_client.dbus_object_path)
2187
tcp_server.clients[client_name] = Client.__new__(Client)
2188
for name, value in client.iteritems():
2189
setattr(tcp_server.clients[client_name], name, value)
2191
tcp_server.clients[client_name].decrypt_secret(
2192
client_settings[client_name]["secret"])
2194
# Create/remove clients based on new changes made to config
2195
for clientname in set(old_client_settings) - set(client_settings):
2196
del tcp_server.clients[clientname]
2197
for clientname in set(client_settings) - set(old_client_settings):
2198
tcp_server.clients[clientname] = (client_class(name = clientname,
2053
tcp_server.clients.update(set(
2054
client_class(name = section,
2055
config= dict(client_config_items(
2056
client_config, section)))
2057
for section in client_config.sections()))
2058
2204
if not tcp_server.clients:
2059
2205
logger.warning("No clients defined")
2112
2259
return dbus.Dictionary(
2113
2260
((c.dbus_object_path, c.GetAll(""))
2114
for c in tcp_server.clients),
2261
for c in tcp_server.clients.itervalues()),
2115
2262
signature="oa{sv}")
2117
2264
@dbus.service.method(_interface, in_signature="o")
2118
2265
def RemoveClient(self, object_path):
2120
for c in tcp_server.clients:
2267
for c in tcp_server.clients.itervalues():
2121
2268
if c.dbus_object_path == object_path:
2122
tcp_server.clients.remove(c)
2269
del tcp_server.clients[c.name]
2123
2270
c.remove_from_connection()
2124
2271
# Don't signal anything except ClientRemoved
2125
2272
c.disable(quiet=True)
2139
2286
service.cleanup()
2141
2288
multiprocessing.active_children()
2289
if not (tcp_server.clients or client_settings):
2292
# Store client before exiting. Secrets are encrypted with key based
2293
# on what config file has. If config file is removed/edited, old
2294
# secret will thus be unrecovable.
2296
for client in tcp_server.clients.itervalues():
2297
client.encrypt_secret(client_settings[client.name]["secret"])
2301
# A list of attributes that will not be stored when shuting down.
2302
exclude = set(("bus", "changedstate", "secret"))
2303
for name, typ in inspect.getmembers(dbus.service.Object):
2306
client_dict["encrypted_secret"] = client.encrypted_secret
2307
for attr in client.client_structure:
2308
if attr not in exclude:
2309
client_dict[attr] = getattr(client, attr)
2311
clients.append(client_dict)
2312
del client_settings[client.name]["secret"]
2315
with os.fdopen(os.open(stored_state_path, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0600), "wb") as stored_state:
2316
pickle.dump((clients, client_settings), stored_state)
2317
except IOError as e:
2318
logger.warning("Could not save persistant state: {0}".format(e))
2319
if e.errno != errno.ENOENT:
2322
# Delete all clients, and settings from config
2142
2323
while tcp_server.clients:
2143
client = tcp_server.clients.pop()
2324
name, client = tcp_server.clients.popitem()
2145
2326
client.remove_from_connection()
2146
client.disable_hook = None
2147
2327
# Don't signal anything except ClientRemoved
2148
2328
client.disable(quiet=True)
2150
2330
# Emit D-Bus signal
2151
2331
mandos_dbus_service.ClientRemoved(client
2334
client_settings.clear()
2155
2336
atexit.register(cleanup)
2157
for client in tcp_server.clients:
2338
for client in tcp_server.clients.itervalues():
2159
2340
# Emit D-Bus signal
2160
2341
mandos_dbus_service.ClientAdded(client.dbus_object_path)
2342
# Need to initiate checking of clients
2344
client.init_checker()
2163
2347
tcp_server.enable()
2164
2348
tcp_server.server_activate()