189
189
facility=logging.handlers.SysLogHandler.LOG_DAEMON,
190
190
address="/dev/log"))
191
191
syslogger.setFormatter(logging.Formatter
192
("Mandos [%(process)d]: %(levelname)s:"
194
log.addHandler(syslogger)
192
('Mandos [%(process)d]: %(levelname)s:'
194
logger.addHandler(syslogger)
197
197
console = logging.StreamHandler()
198
console.setFormatter(logging.Formatter("%(asctime)s %(name)s"
202
log.addHandler(console)
198
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
202
logger.addHandler(console)
203
logger.setLevel(level)
206
206
class PGPError(Exception):
224
224
except OSError as e:
225
225
if e.errno != errno.ENOENT:
227
self.gnupgargs = ["--batch",
228
"--homedir", self.tempdir,
227
self.gnupgargs = ['--batch',
228
'--homedir', self.tempdir,
231
231
# Only GPG version 1 has the --no-use-agent option.
232
232
if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"):
233
233
self.gnupgargs.append("--no-use-agent")
272
272
dir=self.tempdir) as passfile:
273
273
passfile.write(passphrase)
275
proc = subprocess.Popen([self.gpg, "--symmetric",
275
proc = subprocess.Popen([self.gpg, '--symmetric',
278
278
+ self.gnupgargs,
279
279
stdin=subprocess.PIPE,
290
290
dir=self.tempdir) as passfile:
291
291
passfile.write(passphrase)
293
proc = subprocess.Popen([self.gpg, "--decrypt",
293
proc = subprocess.Popen([self.gpg, '--decrypt',
296
296
+ self.gnupgargs,
297
297
stdin=subprocess.PIPE,
351
351
interface: integer; avahi.IF_UNSPEC or an interface index.
352
352
Used to optionally bind to the specified interface.
353
name: string; Example: "Mandos"
354
type: string; Example: "_mandos._tcp".
353
name: string; Example: 'Mandos'
354
type: string; Example: '_mandos._tcp'.
355
355
See <https://www.iana.org/assignments/service-names-port-numbers>
356
356
port: integer; what port to announce
357
357
TXT: list of strings; TXT record for the service
394
394
def rename(self, remove=True):
395
395
"""Derived from the Avahi example code"""
396
396
if self.rename_count >= self.max_renames:
397
log.critical("No suitable Zeroconf service name found"
398
" after %i retries, exiting.",
397
logger.critical("No suitable Zeroconf service name found"
398
" after %i retries, exiting.",
400
400
raise AvahiServiceError("Too many renames")
402
402
self.server.GetAlternativeServiceName(self.name))
403
403
self.rename_count += 1
404
log.info("Changing Zeroconf service name to %r ...",
404
logger.info("Changing Zeroconf service name to %r ...",
410
410
except dbus.exceptions.DBusException as error:
411
411
if (error.get_dbus_name()
412
412
== "org.freedesktop.Avahi.CollisionError"):
413
log.info("Local Zeroconf service name collision.")
413
logger.info("Local Zeroconf service name collision.")
414
414
return self.rename(remove=False)
416
log.critical("D-Bus Exception", exc_info=error)
416
logger.critical("D-Bus Exception", exc_info=error)
435
435
avahi.DBUS_INTERFACE_ENTRY_GROUP)
436
436
self.entry_group_state_changed_match = (
437
437
self.group.connect_to_signal(
438
"StateChanged", self.entry_group_state_changed))
439
log.debug("Adding Zeroconf service '%s' of type '%s' ...",
440
self.name, self.type)
438
'StateChanged', self.entry_group_state_changed))
439
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
440
self.name, self.type)
441
441
self.group.AddService(
451
451
def entry_group_state_changed(self, state, error):
452
452
"""Derived from the Avahi example code"""
453
log.debug("Avahi entry group state change: %i", state)
453
logger.debug("Avahi entry group state change: %i", state)
455
455
if state == avahi.ENTRY_GROUP_ESTABLISHED:
456
log.debug("Zeroconf service established.")
456
logger.debug("Zeroconf service established.")
457
457
elif state == avahi.ENTRY_GROUP_COLLISION:
458
log.info("Zeroconf service name collision.")
458
logger.info("Zeroconf service name collision.")
460
460
elif state == avahi.ENTRY_GROUP_FAILURE:
461
log.critical("Avahi: Error in group state changed %s",
461
logger.critical("Avahi: Error in group state changed %s",
463
463
raise AvahiGroupError("State changed: {!s}".format(error))
465
465
def cleanup(self):
476
476
def server_state_changed(self, state, error=None):
477
477
"""Derived from the Avahi example code"""
478
log.debug("Avahi server state change: %i", state)
478
logger.debug("Avahi server state change: %i", state)
480
480
avahi.SERVER_INVALID: "Zeroconf server invalid",
481
481
avahi.SERVER_REGISTERING: None,
485
485
if state in bad_states:
486
486
if bad_states[state] is not None:
487
487
if error is None:
488
log.error(bad_states[state])
488
logger.error(bad_states[state])
490
log.error(bad_states[state] + ": %r", error)
490
logger.error(bad_states[state] + ": %r", error)
492
492
elif state == avahi.SERVER_RUNNING:
495
495
except dbus.exceptions.DBusException as error:
496
496
if (error.get_dbus_name()
497
497
== "org.freedesktop.Avahi.CollisionError"):
498
log.info("Local Zeroconf service name collision.")
498
logger.info("Local Zeroconf service name"
499
500
return self.rename(remove=False)
501
log.critical("D-Bus Exception", exc_info=error)
502
logger.critical("D-Bus Exception", exc_info=error)
505
506
if error is None:
506
log.debug("Unknown state: %r", state)
507
logger.debug("Unknown state: %r", state)
508
log.debug("Unknown state: %r: %r", state, error)
509
logger.debug("Unknown state: %r: %r", state, error)
510
511
def activate(self):
511
512
"""Derived from the Avahi example code"""
526
527
ret = super(AvahiServiceToSyslog, self).rename(*args,
528
529
syslogger.setFormatter(logging.Formatter(
529
"Mandos ({}) [%(process)d]: %(levelname)s: %(message)s"
530
'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
530
531
.format(self.name)))
573
574
certificate_type_t = ctypes.c_int
575
576
class datum_t(ctypes.Structure):
576
_fields_ = [("data", ctypes.POINTER(ctypes.c_ubyte)),
577
("size", ctypes.c_uint)]
577
_fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
578
('size', ctypes.c_uint)]
579
580
class _openpgp_crt_int(ctypes.Structure):
675
676
# Error handling functions
676
677
def _error_code(result):
677
678
"""A function to raise exceptions on errors, suitable
678
for the "restype" attribute on ctypes functions"""
679
for the 'restype' attribute on ctypes functions"""
679
680
if result >= gnutls.E_SUCCESS:
681
682
if result == gnutls.E_NO_CERTIFICATE_FOUND:
685
686
def _retry_on_error(result, func, arguments,
686
687
_error_code=_error_code):
687
688
"""A function to retry on some errors, suitable
688
for the "errcheck" attribute on ctypes functions"""
689
for the 'errcheck' attribute on ctypes functions"""
689
690
while result < gnutls.E_SUCCESS:
690
691
if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
691
692
return _error_code(result)
875
876
"""A representation of a client host served by this server.
878
approved: bool(); None if not yet approved/disapproved
879
approved: bool(); 'None' if not yet approved/disapproved
879
880
approval_delay: datetime.timedelta(); Time to wait for approval
880
881
approval_duration: datetime.timedelta(); Duration of one approval
881
882
checker: multiprocessing.Process(); a running checker process used
882
to see if the client lives. None if no process is
883
to see if the client lives. 'None' if no process is
884
885
checker_callback_tag: a GLib event source tag, or None
885
886
checker_command: string; External command which is run to check
1009
1010
self.last_enabled = None
1010
1011
self.expires = None
1012
log.debug("Creating client %r", self.name)
1013
log.debug(" Key ID: %s", self.key_id)
1014
log.debug(" Fingerprint: %s", self.fingerprint)
1013
logger.debug("Creating client %r", self.name)
1014
logger.debug(" Key ID: %s", self.key_id)
1015
logger.debug(" Fingerprint: %s", self.fingerprint)
1015
1016
self.created = settings.get("created",
1016
1017
datetime.datetime.utcnow())
1056
1057
if not getattr(self, "enabled", False):
1059
log.info("Disabling client %s", self.name)
1060
logger.info("Disabling client %s", self.name)
1060
1061
if getattr(self, "disable_initiator_tag", None) is not None:
1061
1062
GLib.source_remove(self.disable_initiator_tag)
1062
1063
self.disable_initiator_tag = None
1106
1107
self.last_checker_status = returncode
1107
1108
self.last_checker_signal = None
1108
1109
if self.last_checker_status == 0:
1109
log.info("Checker for %(name)s succeeded", vars(self))
1110
logger.info("Checker for %(name)s succeeded",
1110
1112
self.checked_ok()
1112
log.info("Checker for %(name)s failed", vars(self))
1114
logger.info("Checker for %(name)s failed", vars(self))
1114
1116
self.last_checker_status = -1
1115
1117
self.last_checker_signal = -returncode
1116
log.warning("Checker for %(name)s crashed?", vars(self))
1118
logger.warning("Checker for %(name)s crashed?",
1119
1122
def checked_ok(self):
1166
1169
command = self.checker_command % escaped_attrs
1167
1170
except TypeError as error:
1168
log.error('Could not format string "%s"',
1169
self.checker_command, exc_info=error)
1171
logger.error('Could not format string "%s"',
1172
self.checker_command,
1170
1174
return True # Try again later
1171
1175
self.current_checker_command = command
1172
log.info("Starting checker %r for %s", command, self.name)
1176
logger.info("Starting checker %r for %s", command,
1173
1178
# We don't need to redirect stdout and stderr, since
1174
1179
# in normal mode, that is already done by daemon(),
1175
1180
# and in debug mode we don't want to. (Stdin is
1204
1209
self.checker_callback_tag = None
1205
1210
if getattr(self, "checker", None) is None:
1207
log.debug("Stopping checker for %(name)s", vars(self))
1212
logger.debug("Stopping checker for %(name)s", vars(self))
1208
1213
self.checker.terminate()
1209
1214
self.checker = None
1237
1242
func._dbus_name = func.__name__
1238
1243
if func._dbus_name.endswith("_dbus_property"):
1239
1244
func._dbus_name = func._dbus_name[:-14]
1240
func._dbus_get_args_options = {"byte_arrays": byte_arrays}
1245
func._dbus_get_args_options = {'byte_arrays': byte_arrays}
1243
1248
return decorator
1333
1338
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1334
1339
out_signature="s",
1335
path_keyword="object_path",
1336
connection_keyword="connection")
1340
path_keyword='object_path',
1341
connection_keyword='connection')
1337
1342
def Introspect(self, object_path, connection):
1338
1343
"""Overloading of standard D-Bus method.
1388
1393
document.unlink()
1389
1394
except (AttributeError, xml.dom.DOMException,
1390
1395
xml.parsers.expat.ExpatError) as error:
1391
log.error("Failed to override Introspection method",
1396
logger.error("Failed to override Introspection method",
1393
1398
return xmlstring
1493
1498
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1494
1499
out_signature="s",
1495
path_keyword="object_path",
1496
connection_keyword="connection")
1500
path_keyword='object_path',
1501
connection_keyword='connection')
1497
1502
def Introspect(self, object_path, connection):
1498
1503
"""Overloading of standard D-Bus method.
1555
1560
document.unlink()
1556
1561
except (AttributeError, xml.dom.DOMException,
1557
1562
xml.parsers.expat.ExpatError) as error:
1558
log.error("Failed to override Introspection method",
1563
logger.error("Failed to override Introspection method",
1560
1565
return xmlstring
1595
1600
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1596
1601
out_signature="s",
1597
path_keyword="object_path",
1598
connection_keyword="connection")
1602
path_keyword='object_path',
1603
connection_keyword='connection')
1599
1604
def Introspect(self, object_path, connection):
1600
1605
"""Overloading of standard D-Bus method.
1626
1631
document.unlink()
1627
1632
except (AttributeError, xml.dom.DOMException,
1628
1633
xml.parsers.expat.ExpatError) as error:
1629
log.error("Failed to override Introspection method",
1634
logger.error("Failed to override Introspection method",
1631
1636
return xmlstring
2267
2272
class ProxyClient:
2268
2273
def __init__(self, child_pipe, key_id, fpr, address):
2269
2274
self._pipe = child_pipe
2270
self._pipe.send(("init", key_id, fpr, address))
2275
self._pipe.send(('init', key_id, fpr, address))
2271
2276
if not self._pipe.recv():
2272
2277
raise KeyError(key_id or fpr)
2274
2279
def __getattribute__(self, name):
2276
2281
return super(ProxyClient, self).__getattribute__(name)
2277
self._pipe.send(("getattr", name))
2282
self._pipe.send(('getattr', name))
2278
2283
data = self._pipe.recv()
2279
if data[0] == "data":
2284
if data[0] == 'data':
2281
if data[0] == "function":
2286
if data[0] == 'function':
2283
2288
def func(*args, **kwargs):
2284
self._pipe.send(("funcall", name, args, kwargs))
2289
self._pipe.send(('funcall', name, args, kwargs))
2285
2290
return self._pipe.recv()[1]
2289
2294
def __setattr__(self, name, value):
2291
2296
return super(ProxyClient, self).__setattr__(name, value)
2292
self._pipe.send(("setattr", name, value))
2297
self._pipe.send(('setattr', name, value))
2295
2300
class ClientHandler(socketserver.BaseRequestHandler, object):
2301
2306
def handle(self):
2302
2307
with contextlib.closing(self.server.child_pipe) as child_pipe:
2303
log.info("TCP connection from: %s",
2304
str(self.client_address))
2305
log.debug("Pipe FD: %d", self.server.child_pipe.fileno())
2308
logger.info("TCP connection from: %s",
2309
str(self.client_address))
2310
logger.debug("Pipe FD: %d",
2311
self.server.child_pipe.fileno())
2307
2313
session = gnutls.ClientSession(self.request)
2309
# priority = ":".join(("NONE", "+VERS-TLS1.1",
2315
# priority = ':'.join(("NONE", "+VERS-TLS1.1",
2310
2316
# "+AES-256-CBC", "+SHA1",
2311
2317
# "+COMP-NULL", "+CTYPE-OPENPGP",
2320
2326
# Start communication using the Mandos protocol
2321
2327
# Get protocol number
2322
2328
line = self.request.makefile().readline()
2323
log.debug("Protocol version: %r", line)
2329
logger.debug("Protocol version: %r", line)
2325
2331
if int(line.strip().split()[0]) > 1:
2326
2332
raise RuntimeError(line)
2327
2333
except (ValueError, IndexError, RuntimeError) as error:
2328
log.error("Unknown protocol version: %s", error)
2334
logger.error("Unknown protocol version: %s", error)
2331
2337
# Start GnuTLS connection
2333
2339
session.handshake()
2334
2340
except gnutls.Error as error:
2335
log.warning("Handshake failed: %s", error)
2341
logger.warning("Handshake failed: %s", error)
2336
2342
# Do not run session.bye() here: the session is not
2337
2343
# established. Just abandon the request.
2339
log.debug("Handshake succeeded")
2345
logger.debug("Handshake succeeded")
2341
2347
approval_required = False
2346
2352
key_id = self.key_id(
2347
2353
self.peer_certificate(session))
2348
2354
except (TypeError, gnutls.Error) as error:
2349
log.warning("Bad certificate: %s", error)
2355
logger.warning("Bad certificate: %s", error)
2351
log.debug("Key ID: %s",
2352
key_id.decode("utf-8",
2357
logger.debug("Key ID: %s",
2358
key_id.decode("utf-8",
2358
2364
fpr = self.fingerprint(
2359
2365
self.peer_certificate(session))
2360
2366
except (TypeError, gnutls.Error) as error:
2361
log.warning("Bad certificate: %s", error)
2367
logger.warning("Bad certificate: %s", error)
2363
log.debug("Fingerprint: %s", fpr)
2369
logger.debug("Fingerprint: %s", fpr)
2366
2372
client = ProxyClient(child_pipe, key_id, fpr,
2385
2392
# We are approved or approval is disabled
2387
2394
elif client.approved is None:
2388
log.info("Client %s needs approval",
2395
logger.info("Client %s needs approval",
2390
2397
if self.server.use_dbus:
2391
2398
# Emit D-Bus signal
2392
2399
client.NeedApproval(
2393
2400
client.approval_delay.total_seconds()
2394
2401
* 1000, client.approved_by_default)
2396
log.warning("Client %s was not approved",
2403
logger.warning("Client %s was not approved",
2398
2405
if self.server.use_dbus:
2399
2406
# Emit D-Bus signal
2400
2407
client.Rejected("Denied")
2408
2415
time2 = datetime.datetime.now()
2409
2416
if (time2 - time) >= delay:
2410
2417
if not client.approved_by_default:
2411
log.warning("Client %s timed out while"
2412
" waiting for approval",
2418
logger.warning("Client %s timed out while"
2419
" waiting for approval",
2414
2421
if self.server.use_dbus:
2415
2422
# Emit D-Bus signal
2416
2423
client.Rejected("Approval timed out")
2424
2431
session.send(client.secret)
2425
2432
except gnutls.Error as error:
2426
log.warning("gnutls send failed", exc_info=error)
2433
logger.warning("gnutls send failed",
2429
log.info("Sending secret to %s", client.name)
2437
logger.info("Sending secret to %s", client.name)
2430
2438
# bump the timeout using extended_timeout
2431
2439
client.bump_timeout(client.extended_timeout)
2432
2440
if self.server.use_dbus:
2441
2449
except gnutls.Error as error:
2442
log.warning("GnuTLS bye failed", exc_info=error)
2450
logger.warning("GnuTLS bye failed",
2445
2454
def peer_certificate(session):
2455
2464
valid_cert_types = frozenset((gnutls.CRT_OPENPGP,))
2456
2465
# If not a valid certificate type...
2457
2466
if cert_type not in valid_cert_types:
2458
log.info("Cert type %r not in %r", cert_type,
2467
logger.info("Cert type %r not in %r", cert_type,
2460
2469
# ...return invalid data
2462
2471
list_size = ctypes.c_uint(1)
2582
2591
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
2583
2592
socketserver.TCPServer):
2584
"""IPv6-capable TCP server. Accepts None as address and/or port
2593
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
2587
2596
enabled: Boolean; whether this server is activated yet
2638
2647
if SO_BINDTODEVICE is None:
2639
2648
# Fall back to a hard-coded value which seems to be
2640
2649
# common enough.
2641
log.warning("SO_BINDTODEVICE not found, trying 25")
2650
logger.warning("SO_BINDTODEVICE not found, trying 25")
2642
2651
SO_BINDTODEVICE = 25
2644
2653
self.socket.setsockopt(
2646
2655
(self.interface + "\0").encode("utf-8"))
2647
2656
except socket.error as error:
2648
2657
if error.errno == errno.EPERM:
2649
log.error("No permission to bind to interface %s",
2658
logger.error("No permission to bind to"
2659
" interface %s", self.interface)
2651
2660
elif error.errno == errno.ENOPROTOOPT:
2652
log.error("SO_BINDTODEVICE not available; cannot"
2653
" bind to interface %s", self.interface)
2661
logger.error("SO_BINDTODEVICE not available;"
2662
" cannot bind to interface %s",
2654
2664
elif error.errno == errno.ENODEV:
2655
log.error("Interface %s does not exist, cannot"
2656
" bind", self.interface)
2665
logger.error("Interface %s does not exist,"
2666
" cannot bind", self.interface)
2659
2669
# Only bind(2) the socket if we really need to.
2757
log.info("Client not found for key ID: %s, address:"
2758
" %s", key_id or fpr, address)
2767
logger.info("Client not found for key ID: %s, address"
2768
": %s", key_id or fpr, address)
2759
2769
if self.use_dbus:
2760
2770
# Emit D-Bus signal
2761
2771
mandos_dbus_service.ClientNotFound(key_id or fpr,
2774
2784
# remove the old hook in favor of the new above hook on
2777
if command == "funcall":
2787
if command == 'funcall':
2778
2788
funcname = request[1]
2779
2789
args = request[2]
2780
2790
kwargs = request[3]
2782
parent_pipe.send(("data", getattr(client_object,
2792
parent_pipe.send(('data', getattr(client_object,
2783
2793
funcname)(*args,
2786
if command == "getattr":
2796
if command == 'getattr':
2787
2797
attrname = request[1]
2788
2798
if isinstance(client_object.__getattribute__(attrname),
2789
2799
collections.abc.Callable):
2790
parent_pipe.send(("function", ))
2800
parent_pipe.send(('function', ))
2792
2802
parent_pipe.send((
2793
"data", client_object.__getattribute__(attrname)))
2803
'data', client_object.__getattribute__(attrname)))
2795
if command == "setattr":
2805
if command == 'setattr':
2796
2806
attrname = request[1]
2797
2807
value = request[2]
2798
2808
setattr(client_object, attrname, value)
2904
2914
def string_to_delta(interval):
2905
2915
"""Parse a string and return a datetime.timedelta
2907
>>> string_to_delta("7d") == datetime.timedelta(7)
2909
>>> string_to_delta("60s") == datetime.timedelta(0, 60)
2911
>>> string_to_delta("60m") == datetime.timedelta(0, 3600)
2913
>>> string_to_delta("24h") == datetime.timedelta(1)
2915
>>> string_to_delta("1w") == datetime.timedelta(7)
2917
>>> string_to_delta("5m 30s") == datetime.timedelta(0, 330)
2917
>>> string_to_delta('7d') == datetime.timedelta(7)
2919
>>> string_to_delta('60s') == datetime.timedelta(0, 60)
2921
>>> string_to_delta('60m') == datetime.timedelta(0, 3600)
2923
>>> string_to_delta('24h') == datetime.timedelta(1)
2925
>>> string_to_delta('1w') == datetime.timedelta(7)
2927
>>> string_to_delta('5m 30s') == datetime.timedelta(0, 330)
3125
3135
if server_settings["servicename"] != "Mandos":
3126
3136
syslogger.setFormatter(
3127
logging.Formatter("Mandos ({}) [%(process)d]:"
3128
" %(levelname)s: %(message)s".format(
3137
logging.Formatter('Mandos ({}) [%(process)d]:'
3138
' %(levelname)s: %(message)s'.format(
3129
3139
server_settings["servicename"])))
3131
3141
# Parse config file with clients
3156
3166
pidfile = codecs.open(pidfilename, "w", encoding="utf-8")
3157
3167
except IOError as e:
3158
log.error("Could not open file %r", pidfilename,
3168
logger.error("Could not open file %r", pidfilename,
3161
3171
for name, group in (("_mandos", "_mandos"),
3162
3172
("mandos", "mandos"),
3176
log.debug("Did setuid/setgid to %s:%s", uid, gid)
3187
logger.debug("Did setuid/setgid to {}:{}".format(uid,
3177
3189
except OSError as error:
3178
log.warning("Failed to setuid/setgid to %s:%s: %s", uid, gid,
3179
os.strerror(error.errno))
3190
logger.warning("Failed to setuid/setgid to {}:{}: {}"
3191
.format(uid, gid, os.strerror(error.errno)))
3180
3192
if error.errno != errno.EPERM:
3190
3202
@gnutls.log_func
3191
3203
def debug_gnutls(level, string):
3192
log.debug("GnuTLS: %s",
3193
string[:-1].decode("utf-8", errors="replace"))
3204
logger.debug("GnuTLS: %s",
3205
string[:-1].decode("utf-8",
3195
3208
gnutls.global_set_log_function(debug_gnutls)
3225
3238
"se.bsnet.fukt.Mandos", bus,
3226
3239
do_not_queue=True)
3227
3240
except dbus.exceptions.DBusException as e:
3228
log.error("Disabling D-Bus:", exc_info=e)
3241
logger.error("Disabling D-Bus:", exc_info=e)
3229
3242
use_dbus = False
3230
3243
server_settings["use_dbus"] = False
3231
3244
tcp_server.use_dbus = False
3316
3329
os.remove(stored_state_path)
3317
3330
except IOError as e:
3318
3331
if e.errno == errno.ENOENT:
3319
log.warning("Could not load persistent state:"
3320
" %s", os.strerror(e.errno))
3332
logger.warning("Could not load persistent state:"
3333
" {}".format(os.strerror(e.errno)))
3322
log.critical("Could not load persistent state:",
3335
logger.critical("Could not load persistent state:",
3325
3338
except EOFError as e:
3326
log.warning("Could not load persistent state: EOFError:",
3339
logger.warning("Could not load persistent state: "
3329
3343
with PGPEngine() as pgp:
3330
3344
for client_name, client in clients_data.items():
3357
3371
if client["enabled"]:
3358
3372
if datetime.datetime.utcnow() >= client["expires"]:
3359
3373
if not client["last_checked_ok"]:
3360
log.warning("disabling client %s - Client"
3361
" never performed a successful"
3362
" checker", client_name)
3375
"disabling client {} - Client never "
3376
"performed a successful checker".format(
3363
3378
client["enabled"] = False
3364
3379
elif client["last_checker_status"] != 0:
3365
log.warning("disabling client %s - Client"
3366
" last checker failed with error"
3367
" code %s", client_name,
3368
client["last_checker_status"])
3381
"disabling client {} - Client last"
3382
" checker failed with error code"
3385
client["last_checker_status"]))
3369
3386
client["enabled"] = False
3371
3388
client["expires"] = (
3372
3389
datetime.datetime.utcnow()
3373
3390
+ client["timeout"])
3374
log.debug("Last checker succeeded, keeping %s"
3375
" enabled", client_name)
3391
logger.debug("Last checker succeeded,"
3392
" keeping {} enabled".format(
3377
3395
client["secret"] = pgp.decrypt(
3378
3396
client["encrypted_secret"],
3379
3397
client_settings[client_name]["secret"])
3380
3398
except PGPError:
3381
3399
# If decryption fails, we use secret from new settings
3382
log.debug("Failed to decrypt %s old secret",
3400
logger.debug("Failed to decrypt {} old secret".format(
3384
3402
client["secret"] = (client_settings[client_name]
3568
3586
with tempfile.NamedTemporaryFile(
3570
3588
suffix=".pickle",
3572
3590
dir=os.path.dirname(stored_state_path),
3573
3591
delete=False) as stored_state:
3574
3592
pickle.dump((clients, client_settings), stored_state,
3582
3600
except NameError:
3584
3602
if e.errno in (errno.ENOENT, errno.EACCES, errno.EEXIST):
3585
log.warning("Could not save persistent state: %s",
3586
os.strerror(e.errno))
3603
logger.warning("Could not save persistent state: {}"
3604
.format(os.strerror(e.errno)))
3588
log.warning("Could not save persistent state:",
3606
logger.warning("Could not save persistent state:",
3592
3610
# Delete all clients, and settings from config
3619
3637
service.port = tcp_server.socket.getsockname()[1]
3621
log.info("Now listening on address %r, port %d, flowinfo %d,"
3622
" scope_id %d", *tcp_server.socket.getsockname())
3639
logger.info("Now listening on address %r, port %d,"
3640
" flowinfo %d, scope_id %d",
3641
*tcp_server.socket.getsockname())
3624
log.info("Now listening on address %r, port %d",
3625
*tcp_server.socket.getsockname())
3643
logger.info("Now listening on address %r, port %d",
3644
*tcp_server.socket.getsockname())
3627
3646
# service.interface = tcp_server.socket.getsockname()[3]
3633
3652
service.activate()
3634
3653
except dbus.exceptions.DBusException as error:
3635
log.critical("D-Bus Exception", exc_info=error)
3654
logger.critical("D-Bus Exception", exc_info=error)
3638
3657
# End of Avahi example code
3643
3662
lambda *args, **kwargs: (tcp_server.handle_request
3644
3663
(*args[2:], **kwargs) or True))
3646
log.debug("Starting main loop")
3665
logger.debug("Starting main loop")
3647
3666
main_loop.run()
3648
3667
except AvahiError as error:
3649
log.critical("Avahi Error", exc_info=error)
3668
logger.critical("Avahi Error", exc_info=error)
3652
3671
except KeyboardInterrupt:
3654
3673
print("", file=sys.stderr)
3655
log.debug("Server received KeyboardInterrupt")
3656
log.debug("Server exiting")
3674
logger.debug("Server received KeyboardInterrupt")
3675
logger.debug("Server exiting")
3657
3676
# Must run before the D-Bus bus name gets deregistered
3661
3680
def should_only_run_tests():
3662
3681
parser = argparse.ArgumentParser(add_help=False)
3663
parser.add_argument("--check", action="store_true")
3682
parser.add_argument("--check", action='store_true')
3664
3683
args, unknown_args = parser.parse_known_args()
3665
3684
run_tests = args.check
3674
3693
tests.addTests(doctest.DocTestSuite())
3677
if __name__ == "__main__":
3696
if __name__ == '__main__':
3679
3698
if should_only_run_tests():
3680
3699
# Call using ./mandos --check [--verbose]