80
85
except ImportError:
81
86
SO_BINDTODEVICE = None
86
#logger = logging.getLogger(u'mandos')
87
logger = logging.Logger(u'mandos')
89
stored_state_file = "clients.pickle"
91
logger = logging.getLogger()
88
92
syslogger = (logging.handlers.SysLogHandler
89
93
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
90
address = "/dev/log"))
91
syslogger.setFormatter(logging.Formatter
92
(u'Mandos [%(process)d]: %(levelname)s:'
94
logger.addHandler(syslogger)
96
console = logging.StreamHandler()
97
console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:'
100
logger.addHandler(console)
94
address = str("/dev/log")))
97
if_nametoindex = (ctypes.cdll.LoadLibrary
98
(ctypes.util.find_library("c"))
100
except (OSError, AttributeError):
101
def if_nametoindex(interface):
102
"Get an interface index the hard way, i.e. using fcntl()"
103
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
104
with contextlib.closing(socket.socket()) as s:
105
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
106
struct.pack(str("16s16x"),
108
interface_index = struct.unpack(str("I"),
110
return interface_index
113
def initlogger(debug, level=logging.WARNING):
114
"""init logger and add loglevel"""
116
syslogger.setFormatter(logging.Formatter
117
('Mandos [%(process)d]: %(levelname)s:'
119
logger.addHandler(syslogger)
122
console = logging.StreamHandler()
123
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
127
logger.addHandler(console)
128
logger.setLevel(level)
131
class PGPError(Exception):
132
"""Exception if encryption/decryption fails"""
136
class PGPEngine(object):
137
"""A simple class for OpenPGP symmetric encryption & decryption"""
139
self.gnupg = GnuPGInterface.GnuPG()
140
self.tempdir = tempfile.mkdtemp(prefix="mandos-")
141
self.gnupg = GnuPGInterface.GnuPG()
142
self.gnupg.options.meta_interactive = False
143
self.gnupg.options.homedir = self.tempdir
144
self.gnupg.options.extra_args.extend(['--force-mdc',
151
def __exit__ (self, exc_type, exc_value, traceback):
159
if self.tempdir is not None:
160
# Delete contents of tempdir
161
for root, dirs, files in os.walk(self.tempdir,
163
for filename in files:
164
os.remove(os.path.join(root, filename))
166
os.rmdir(os.path.join(root, dirname))
168
os.rmdir(self.tempdir)
171
def password_encode(self, password):
172
# Passphrase can not be empty and can not contain newlines or
173
# NUL bytes. So we prefix it and hex encode it.
174
return b"mandos" + binascii.hexlify(password)
176
def encrypt(self, data, password):
177
self.gnupg.passphrase = self.password_encode(password)
178
with open(os.devnull) as devnull:
180
proc = self.gnupg.run(['--symmetric'],
181
create_fhs=['stdin', 'stdout'],
182
attach_fhs={'stderr': devnull})
183
with contextlib.closing(proc.handles['stdin']) as f:
185
with contextlib.closing(proc.handles['stdout']) as f:
186
ciphertext = f.read()
190
self.gnupg.passphrase = None
193
def decrypt(self, data, password):
194
self.gnupg.passphrase = self.password_encode(password)
195
with open(os.devnull) as devnull:
197
proc = self.gnupg.run(['--decrypt'],
198
create_fhs=['stdin', 'stdout'],
199
attach_fhs={'stderr': devnull})
200
with contextlib.closing(proc.handles['stdin'] ) as f:
202
with contextlib.closing(proc.handles['stdout']) as f:
203
decrypted_plaintext = f.read()
207
self.gnupg.passphrase = None
208
return decrypted_plaintext
102
212
class AvahiError(Exception):
103
213
def __init__(self, value, *args, **kwargs):
199
311
self.group.Commit()
200
312
def entry_group_state_changed(self, state, error):
201
313
"""Derived from the Avahi example code"""
202
logger.debug(u"Avahi entry group state change: %i", state)
314
logger.debug("Avahi entry group state change: %i", state)
204
316
if state == avahi.ENTRY_GROUP_ESTABLISHED:
205
logger.debug(u"Zeroconf service established.")
317
logger.debug("Zeroconf service established.")
206
318
elif state == avahi.ENTRY_GROUP_COLLISION:
207
logger.warning(u"Zeroconf service name collision.")
319
logger.info("Zeroconf service name collision.")
209
321
elif state == avahi.ENTRY_GROUP_FAILURE:
210
logger.critical(u"Avahi: Error in group state changed %s",
322
logger.critical("Avahi: Error in group state changed %s",
212
raise AvahiGroupError(u"State changed: %s"
324
raise AvahiGroupError("State changed: %s"
213
325
% unicode(error))
214
326
def cleanup(self):
215
327
"""Derived from the Avahi example code"""
216
328
if self.group is not None:
331
except (dbus.exceptions.UnknownMethodException,
332
dbus.exceptions.DBusException):
218
334
self.group = None
219
def server_state_changed(self, state):
336
def server_state_changed(self, state, error=None):
220
337
"""Derived from the Avahi example code"""
221
logger.debug(u"Avahi server state change: %i", state)
222
if state == avahi.SERVER_COLLISION:
223
logger.error(u"Zeroconf server name collision")
338
logger.debug("Avahi server state change: %i", state)
339
bad_states = { avahi.SERVER_INVALID:
340
"Zeroconf server invalid",
341
avahi.SERVER_REGISTERING: None,
342
avahi.SERVER_COLLISION:
343
"Zeroconf server name collision",
344
avahi.SERVER_FAILURE:
345
"Zeroconf server failure" }
346
if state in bad_states:
347
if bad_states[state] is not None:
349
logger.error(bad_states[state])
351
logger.error(bad_states[state] + ": %r", error)
225
353
elif state == avahi.SERVER_RUNNING:
357
logger.debug("Unknown state: %r", state)
359
logger.debug("Unknown state: %r: %r", state, error)
227
360
def activate(self):
228
361
"""Derived from the Avahi example code"""
229
362
if self.server is None:
230
363
self.server = dbus.Interface(
231
364
self.bus.get_object(avahi.DBUS_NAME,
232
avahi.DBUS_PATH_SERVER),
365
avahi.DBUS_PATH_SERVER,
366
follow_name_owner_changes=True),
233
367
avahi.DBUS_INTERFACE_SERVER)
234
self.server.connect_to_signal(u"StateChanged",
368
self.server.connect_to_signal("StateChanged",
235
369
self.server_state_changed)
236
370
self.server_state_changed(self.server.GetState())
372
class AvahiServiceToSyslog(AvahiService):
374
"""Add the new name to the syslog messages"""
375
ret = AvahiService.rename(self)
376
syslogger.setFormatter(logging.Formatter
377
('Mandos (%s) [%%(process)d]:'
378
' %%(levelname)s: %%(message)s'
382
def timedelta_to_milliseconds(td):
383
"Convert a datetime.timedelta() to milliseconds"
384
return ((td.days * 24 * 60 * 60 * 1000)
385
+ (td.seconds * 1000)
386
+ (td.microseconds // 1000))
239
388
class Client(object):
240
389
"""A representation of a client host served by this server.
243
name: string; from the config file, used in log messages and
392
approved: bool(); 'None' if not yet approved/disapproved
393
approval_delay: datetime.timedelta(); Time to wait for approval
394
approval_duration: datetime.timedelta(); Duration of one approval
395
checker: subprocess.Popen(); a running checker process used
396
to see if the client lives.
397
'None' if no process is running.
398
checker_callback_tag: a gobject event source tag, or None
399
checker_command: string; External command which is run to check
400
if client lives. %() expansions are done at
401
runtime with vars(self) as dict, so that for
402
instance %(name)s can be used in the command.
403
checker_initiator_tag: a gobject event source tag, or None
404
created: datetime.datetime(); (UTC) object creation
405
client_structure: Object describing what attributes a client has
406
and is used for storing the client at exit
407
current_checker_command: string; current running checker_command
408
disable_initiator_tag: a gobject event source tag, or None
245
410
fingerprint: string (40 or 32 hexadecimal digits); used to
246
411
uniquely identify the client
247
secret: bytestring; sent verbatim (over TLS) to client
248
412
host: string; available for use by the checker command
249
created: datetime.datetime(); (UTC) object creation
250
last_enabled: datetime.datetime(); (UTC)
413
interval: datetime.timedelta(); How often to start a new checker
414
last_approval_request: datetime.datetime(); (UTC) or None
252
415
last_checked_ok: datetime.datetime(); (UTC) or None
416
last_checker_status: integer between 0 and 255 reflecting exit
417
status of last checker. -1 reflects crashed
418
checker, -2 means no checker completed yet.
419
last_enabled: datetime.datetime(); (UTC) or None
420
name: string; from the config file, used in log messages and
422
secret: bytestring; sent verbatim (over TLS) to client
253
423
timeout: datetime.timedelta(); How long from last_checked_ok
254
424
until this client is disabled
255
interval: datetime.timedelta(); How often to start a new checker
256
disable_hook: If set, called by disable() as disable_hook(self)
257
checker: subprocess.Popen(); a running checker process used
258
to see if the client lives.
259
'None' if no process is running.
260
checker_initiator_tag: a gobject event source tag, or None
261
disable_initiator_tag: - '' -
262
checker_callback_tag: - '' -
263
checker_command: string; External command which is run to check if
264
client lives. %() expansions are done at
265
runtime with vars(self) as dict, so that for
266
instance %(name)s can be used in the command.
267
current_checker_command: string; current running checker_command
268
approval_delay: datetime.timedelta(); Time to wait for approval
269
_approved: bool(); 'None' if not yet approved/disapproved
270
approval_duration: datetime.timedelta(); Duration of one approval
425
extended_timeout: extra long timeout when secret has been sent
426
runtime_expansions: Allowed attributes for runtime expansion.
427
expires: datetime.datetime(); time (UTC) when a client will be
274
def _timedelta_to_milliseconds(td):
275
"Convert a datetime.timedelta() to milliseconds"
276
return ((td.days * 24 * 60 * 60 * 1000)
277
+ (td.seconds * 1000)
278
+ (td.microseconds // 1000))
431
runtime_expansions = ("approval_delay", "approval_duration",
432
"created", "enabled", "fingerprint",
433
"host", "interval", "last_checked_ok",
434
"last_enabled", "name", "timeout")
435
client_defaults = { "timeout": "5m",
436
"extended_timeout": "15m",
438
"checker": "fping -q -- %%(host)s",
440
"approval_delay": "0s",
441
"approval_duration": "1s",
442
"approved_by_default": "True",
280
446
def timeout_milliseconds(self):
281
447
"Return the 'timeout' attribute in milliseconds"
282
return self._timedelta_to_milliseconds(self.timeout)
448
return timedelta_to_milliseconds(self.timeout)
450
def extended_timeout_milliseconds(self):
451
"Return the 'extended_timeout' attribute in milliseconds"
452
return timedelta_to_milliseconds(self.extended_timeout)
284
454
def interval_milliseconds(self):
285
455
"Return the 'interval' attribute in milliseconds"
286
return self._timedelta_to_milliseconds(self.interval)
456
return timedelta_to_milliseconds(self.interval)
288
458
def approval_delay_milliseconds(self):
289
return self._timedelta_to_milliseconds(self.approval_delay)
291
def __init__(self, name = None, disable_hook=None, config=None):
459
return timedelta_to_milliseconds(self.approval_delay)
462
def config_parser(config):
463
"""Construct a new dict of client settings of this form:
464
{ 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.
470
for client_name in config.sections():
471
section = dict(config.items(client_name))
472
client = settings[client_name] = {}
474
client["host"] = section["host"]
475
# Reformat values from string types to Python types
476
client["approved_by_default"] = config.getboolean(
477
client_name, "approved_by_default")
478
client["enabled"] = config.getboolean(client_name,
481
client["fingerprint"] = (section["fingerprint"].upper()
483
if "secret" in section:
484
client["secret"] = section["secret"].decode("base64")
485
elif "secfile" in section:
486
with open(os.path.expanduser(os.path.expandvars
487
(section["secfile"])),
489
client["secret"] = secfile.read()
491
raise TypeError("No secret or secfile for section %s"
493
client["timeout"] = string_to_delta(section["timeout"])
494
client["extended_timeout"] = string_to_delta(
495
section["extended_timeout"])
496
client["interval"] = string_to_delta(section["interval"])
497
client["approval_delay"] = string_to_delta(
498
section["approval_delay"])
499
client["approval_duration"] = string_to_delta(
500
section["approval_duration"])
501
client["checker_command"] = section["checker"]
502
client["last_approval_request"] = None
503
client["last_checked_ok"] = None
504
client["last_checker_status"] = -2
509
def __init__(self, settings, name = None):
292
510
"""Note: the 'checker' key in 'config' sets the
293
511
'checker_command' attribute and *not* the 'checker'
298
logger.debug(u"Creating client %r", self.name)
514
# adding all client settings
515
for setting, value in settings.iteritems():
516
setattr(self, setting, value)
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()
525
self.last_enabled = None
528
logger.debug("Creating client %r", self.name)
299
529
# Uppercase and remove spaces from fingerprint for later
300
530
# comparison purposes with return value from the fingerprint()
302
self.fingerprint = (config[u"fingerprint"].upper()
304
logger.debug(u" Fingerprint: %s", self.fingerprint)
305
if u"secret" in config:
306
self.secret = config[u"secret"].decode(u"base64")
307
elif u"secfile" in config:
308
with open(os.path.expanduser(os.path.expandvars
309
(config[u"secfile"])),
311
self.secret = secfile.read()
313
raise TypeError(u"No secret or secfile for client %s"
315
self.host = config.get(u"host", u"")
316
self.created = datetime.datetime.utcnow()
318
self.last_enabled = None
319
self.last_checked_ok = None
320
self.timeout = string_to_delta(config[u"timeout"])
321
self.interval = string_to_delta(config[u"interval"])
322
self.disable_hook = disable_hook
532
logger.debug(" Fingerprint: %s", self.fingerprint)
533
self.created = settings.get("created",
534
datetime.datetime.utcnow())
536
# attributes specific for this server instance
323
537
self.checker = None
324
538
self.checker_initiator_tag = None
325
539
self.disable_initiator_tag = None
326
540
self.checker_callback_tag = None
327
self.checker_command = config[u"checker"]
328
541
self.current_checker_command = None
329
self.last_connect = None
330
self._approved = None
331
self.approved_by_default = config.get(u"approved_by_default",
333
543
self.approvals_pending = 0
334
self.approval_delay = string_to_delta(
335
config[u"approval_delay"])
336
self.approval_duration = string_to_delta(
337
config[u"approval_duration"])
338
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
544
self.changedstate = (multiprocessing_manager
545
.Condition(multiprocessing_manager
547
self.client_structure = [attr for attr in
548
self.__dict__.iterkeys()
549
if not attr.startswith("_")]
550
self.client_structure.append("client_structure")
552
for name, t in inspect.getmembers(type(self),
556
if not name.startswith("_"):
557
self.client_structure.append(name)
559
# Send notice to process children that client state has changed
340
560
def send_changedstate(self):
341
self.changedstate.acquire()
342
self.changedstate.notify_all()
343
self.changedstate.release()
561
with self.changedstate:
562
self.changedstate.notify_all()
345
564
def enable(self):
346
565
"""Start this client's checker and timeout hooks"""
347
if getattr(self, u"enabled", False):
566
if getattr(self, "enabled", False):
348
567
# Already enabled
350
569
self.send_changedstate()
570
self.expires = datetime.datetime.utcnow() + self.timeout
351
572
self.last_enabled = datetime.datetime.utcnow()
575
def disable(self, quiet=True):
576
"""Disable this client."""
577
if not getattr(self, "enabled", False):
580
self.send_changedstate()
582
logger.info("Disabling client %s", self.name)
583
if getattr(self, "disable_initiator_tag", False):
584
gobject.source_remove(self.disable_initiator_tag)
585
self.disable_initiator_tag = None
587
if getattr(self, "checker_initiator_tag", False):
588
gobject.source_remove(self.checker_initiator_tag)
589
self.checker_initiator_tag = None
592
# Do not run this again if called by a gobject.timeout_add
598
def init_checker(self):
352
599
# Schedule a new checker to be started an 'interval' from now,
353
600
# and every interval from then on.
354
601
self.checker_initiator_tag = (gobject.timeout_add
358
605
self.disable_initiator_tag = (gobject.timeout_add
359
606
(self.timeout_milliseconds(),
362
608
# Also start a new checker *right now*.
363
609
self.start_checker()
365
def disable(self, quiet=True):
366
"""Disable this client."""
367
if not getattr(self, "enabled", False):
370
self.send_changedstate()
372
logger.info(u"Disabling client %s", self.name)
373
if getattr(self, u"disable_initiator_tag", False):
374
gobject.source_remove(self.disable_initiator_tag)
375
self.disable_initiator_tag = None
376
if getattr(self, u"checker_initiator_tag", False):
377
gobject.source_remove(self.checker_initiator_tag)
378
self.checker_initiator_tag = None
380
if self.disable_hook:
381
self.disable_hook(self)
383
# Do not run this again if called by a gobject.timeout_add
387
self.disable_hook = None
390
611
def checker_callback(self, pid, condition, command):
391
612
"""The checker has completed, so take appropriate actions."""
392
613
self.checker_callback_tag = None
393
614
self.checker = None
394
615
if os.WIFEXITED(condition):
395
exitstatus = os.WEXITSTATUS(condition)
397
logger.info(u"Checker for %(name)s succeeded",
616
self.last_checker_status = os.WEXITSTATUS(condition)
617
if self.last_checker_status == 0:
618
logger.info("Checker for %(name)s succeeded",
399
620
self.checked_ok()
401
logger.info(u"Checker for %(name)s failed",
622
logger.info("Checker for %(name)s failed",
404
logger.warning(u"Checker for %(name)s crashed?",
625
self.last_checker_status = -1
626
logger.warning("Checker for %(name)s crashed?",
407
629
def checked_ok(self):
408
"""Bump up the timeout for this client.
410
This should only be called when the client has been seen,
630
"""Assert that the client has been seen, alive and well."""
413
631
self.last_checked_ok = datetime.datetime.utcnow()
414
gobject.source_remove(self.disable_initiator_tag)
415
self.disable_initiator_tag = (gobject.timeout_add
416
(self.timeout_milliseconds(),
632
self.last_checker_status = 0
635
def bump_timeout(self, timeout=None):
636
"""Bump up the timeout for this client."""
638
timeout = self.timeout
639
if self.disable_initiator_tag is not None:
640
gobject.source_remove(self.disable_initiator_tag)
641
if getattr(self, "enabled", False):
642
self.disable_initiator_tag = (gobject.timeout_add
643
(timedelta_to_milliseconds
644
(timeout), self.disable))
645
self.expires = datetime.datetime.utcnow() + timeout
647
def need_approval(self):
648
self.last_approval_request = datetime.datetime.utcnow()
419
650
def start_checker(self):
420
651
"""Start a new checker subprocess if one is not running.
568
802
def _is_dbus_property(obj):
569
return getattr(obj, u"_dbus_is_property", False)
803
return getattr(obj, "_dbus_is_property", False)
571
805
def _get_all_dbus_properties(self):
572
806
"""Returns a generator of (name, attribute) pairs
574
return ((prop._dbus_name, prop)
808
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
809
for cls in self.__class__.__mro__
575
810
for name, prop in
576
inspect.getmembers(self, self._is_dbus_property))
811
inspect.getmembers(cls, self._is_dbus_property))
578
813
def _get_dbus_property(self, interface_name, property_name):
579
814
"""Returns a bound method if one exists which is a D-Bus
580
815
property with the specified name and interface.
582
for name in (property_name,
583
property_name + u"_dbus_property"):
584
prop = getattr(self, name, None)
586
or not self._is_dbus_property(prop)
587
or prop._dbus_name != property_name
588
or (interface_name and prop._dbus_interface
589
and interface_name != prop._dbus_interface)):
817
for cls in self.__class__.__mro__:
818
for name, value in (inspect.getmembers
819
(cls, self._is_dbus_property)):
820
if (value._dbus_name == property_name
821
and value._dbus_interface == interface_name):
822
return value.__get__(self)
592
824
# No such property
593
raise DBusPropertyNotFound(self.dbus_object_path + u":"
594
+ interface_name + u"."
825
raise DBusPropertyNotFound(self.dbus_object_path + ":"
826
+ interface_name + "."
597
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
829
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
599
831
def Get(self, interface_name, property_name):
600
832
"""Standard D-Bus property Get() method, see D-Bus standard.
602
834
prop = self._get_dbus_property(interface_name, property_name)
603
if prop._dbus_access == u"write":
835
if prop._dbus_access == "write":
604
836
raise DBusPropertyAccessException(property_name)
606
if not hasattr(value, u"variant_level"):
838
if not hasattr(value, "variant_level"):
608
840
return type(value)(value, variant_level=value.variant_level+1)
610
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
842
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
611
843
def Set(self, interface_name, property_name, value):
612
844
"""Standard D-Bus property Set() method, see D-Bus standard.
614
846
prop = self._get_dbus_property(interface_name, property_name)
615
if prop._dbus_access == u"read":
847
if prop._dbus_access == "read":
616
848
raise DBusPropertyAccessException(property_name)
617
if prop._dbus_get_args_options[u"byte_arrays"]:
849
if prop._dbus_get_args_options["byte_arrays"]:
618
850
# The byte_arrays option is not supported yet on
619
851
# signatures other than "ay".
620
if prop._dbus_signature != u"ay":
852
if prop._dbus_signature != "ay":
622
value = dbus.ByteArray(''.join(unichr(byte)
854
value = dbus.ByteArray(b''.join(chr(byte)
626
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
627
out_signature=u"a{sv}")
858
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
859
out_signature="a{sv}")
628
860
def GetAll(self, interface_name):
629
861
"""Standard D-Bus property GetAll() method, see D-Bus
632
864
Note: Will not include properties with access="write".
635
867
for name, prop in self._get_all_dbus_properties():
636
868
if (interface_name
637
869
and interface_name != prop._dbus_interface):
638
870
# Interface non-empty but did not match
640
872
# Ignore write-only properties
641
if prop._dbus_access == u"write":
873
if prop._dbus_access == "write":
644
if not hasattr(value, u"variant_level"):
876
if not hasattr(value, "variant_level"):
877
properties[name] = value
647
all[name] = type(value)(value, variant_level=
648
value.variant_level+1)
649
return dbus.Dictionary(all, signature=u"sv")
879
properties[name] = type(value)(value, variant_level=
880
value.variant_level+1)
881
return dbus.Dictionary(properties, signature="sv")
651
883
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
653
885
path_keyword='object_path',
654
886
connection_keyword='connection')
655
887
def Introspect(self, object_path, connection):
661
893
document = xml.dom.minidom.parseString(xmlstring)
662
894
def make_tag(document, name, prop):
663
e = document.createElement(u"property")
664
e.setAttribute(u"name", name)
665
e.setAttribute(u"type", prop._dbus_signature)
666
e.setAttribute(u"access", prop._dbus_access)
895
e = document.createElement("property")
896
e.setAttribute("name", name)
897
e.setAttribute("type", prop._dbus_signature)
898
e.setAttribute("access", prop._dbus_access)
668
for if_tag in document.getElementsByTagName(u"interface"):
900
for if_tag in document.getElementsByTagName("interface"):
669
901
for tag in (make_tag(document, name, prop)
671
903
in self._get_all_dbus_properties()
672
904
if prop._dbus_interface
673
== if_tag.getAttribute(u"name")):
905
== if_tag.getAttribute("name")):
674
906
if_tag.appendChild(tag)
675
907
# Add the names to the return values for the
676
908
# "org.freedesktop.DBus.Properties" methods
677
if (if_tag.getAttribute(u"name")
678
== u"org.freedesktop.DBus.Properties"):
679
for cn in if_tag.getElementsByTagName(u"method"):
680
if cn.getAttribute(u"name") == u"Get":
681
for arg in cn.getElementsByTagName(u"arg"):
682
if (arg.getAttribute(u"direction")
684
arg.setAttribute(u"name", u"value")
685
elif cn.getAttribute(u"name") == u"GetAll":
686
for arg in cn.getElementsByTagName(u"arg"):
687
if (arg.getAttribute(u"direction")
689
arg.setAttribute(u"name", u"props")
690
xmlstring = document.toxml(u"utf-8")
909
if (if_tag.getAttribute("name")
910
== "org.freedesktop.DBus.Properties"):
911
for cn in if_tag.getElementsByTagName("method"):
912
if cn.getAttribute("name") == "Get":
913
for arg in cn.getElementsByTagName("arg"):
914
if (arg.getAttribute("direction")
916
arg.setAttribute("name", "value")
917
elif cn.getAttribute("name") == "GetAll":
918
for arg in cn.getElementsByTagName("arg"):
919
if (arg.getAttribute("direction")
921
arg.setAttribute("name", "props")
922
xmlstring = document.toxml("utf-8")
691
923
document.unlink()
692
924
except (AttributeError, xml.dom.DOMException,
693
xml.parsers.expat.ExpatError), error:
694
logger.error(u"Failed to override Introspection method",
925
xml.parsers.expat.ExpatError) as error:
926
logger.error("Failed to override Introspection method",
931
def datetime_to_dbus (dt, variant_level=0):
932
"""Convert a UTC datetime.datetime() to a D-Bus type."""
934
return dbus.String("", variant_level = variant_level)
935
return dbus.String(dt.isoformat(),
936
variant_level=variant_level)
939
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
941
"""Applied to an empty subclass of a D-Bus object, this metaclass
942
will add additional D-Bus attributes matching a certain pattern.
944
def __new__(mcs, name, bases, attr):
945
# Go through all the base classes which could have D-Bus
946
# methods, signals, or properties in them
947
for base in (b for b in bases
948
if issubclass(b, dbus.service.Object)):
949
# Go though all attributes of the base class
950
for attrname, attribute in inspect.getmembers(base):
951
# Ignore non-D-Bus attributes, and D-Bus attributes
952
# with the wrong interface name
953
if (not hasattr(attribute, "_dbus_interface")
954
or not attribute._dbus_interface
955
.startswith("se.recompile.Mandos")):
957
# Create an alternate D-Bus interface name based on
959
alt_interface = (attribute._dbus_interface
960
.replace("se.recompile.Mandos",
961
"se.bsnet.fukt.Mandos"))
962
# Is this a D-Bus signal?
963
if getattr(attribute, "_dbus_is_signal", False):
964
# Extract the original non-method function by
966
nonmethod_func = (dict(
967
zip(attribute.func_code.co_freevars,
968
attribute.__closure__))["func"]
970
# Create a new, but exactly alike, function
971
# object, and decorate it to be a new D-Bus signal
972
# with the alternate D-Bus interface name
973
new_function = (dbus.service.signal
975
attribute._dbus_signature)
977
nonmethod_func.func_code,
978
nonmethod_func.func_globals,
979
nonmethod_func.func_name,
980
nonmethod_func.func_defaults,
981
nonmethod_func.func_closure)))
982
# Define a creator of a function to call both the
983
# old and new functions, so both the old and new
984
# signals gets sent when the function is called
985
def fixscope(func1, func2):
986
"""This function is a scope container to pass
987
func1 and func2 to the "call_both" function
988
outside of its arguments"""
989
def call_both(*args, **kwargs):
990
"""This function will emit two D-Bus
991
signals by calling func1 and func2"""
992
func1(*args, **kwargs)
993
func2(*args, **kwargs)
995
# Create the "call_both" function and add it to
997
attr[attrname] = fixscope(attribute,
999
# Is this a D-Bus method?
1000
elif getattr(attribute, "_dbus_is_method", False):
1001
# Create a new, but exactly alike, function
1002
# object. Decorate it to be a new D-Bus method
1003
# with the alternate D-Bus interface name. Add it
1005
attr[attrname] = (dbus.service.method
1007
attribute._dbus_in_signature,
1008
attribute._dbus_out_signature)
1010
(attribute.func_code,
1011
attribute.func_globals,
1012
attribute.func_name,
1013
attribute.func_defaults,
1014
attribute.func_closure)))
1015
# Is this a D-Bus property?
1016
elif getattr(attribute, "_dbus_is_property", False):
1017
# Create a new, but exactly alike, function
1018
# object, and decorate it to be a new D-Bus
1019
# property with the alternate D-Bus interface
1020
# name. Add it to the class.
1021
attr[attrname] = (dbus_service_property
1023
attribute._dbus_signature,
1024
attribute._dbus_access,
1026
._dbus_get_args_options
1029
(attribute.func_code,
1030
attribute.func_globals,
1031
attribute.func_name,
1032
attribute.func_defaults,
1033
attribute.func_closure)))
1034
return type.__new__(mcs, name, bases, attr)
699
1037
class ClientDBus(Client, DBusObjectWithProperties):
700
1038
"""A Client class using D-Bus
703
1041
dbus_object_path: dbus.ObjectPath
704
1042
bus: dbus.SystemBus()
1045
runtime_expansions = (Client.runtime_expansions
1046
+ ("dbus_object_path",))
706
1048
# dbus.service.Object doesn't use super(), so we can't either.
708
1050
def __init__(self, bus = None, *args, **kwargs):
709
self._approvals_pending = 0
711
1052
Client.__init__(self, *args, **kwargs)
712
1053
# Only now, when this client is initialized, can it show up on
1055
client_object_name = unicode(self.name).translate(
1056
{ord("."): ord("_"),
1057
ord("-"): ord("_")})
714
1058
self.dbus_object_path = (dbus.ObjectPath
716
+ self.name.replace(u".", u"_")))
1059
("/clients/" + client_object_name))
717
1060
DBusObjectWithProperties.__init__(self, self.bus,
718
1061
self.dbus_object_path)
720
def _get_approvals_pending(self):
721
return self._approvals_pending
722
def _set_approvals_pending(self, value):
723
old_value = self._approvals_pending
724
self._approvals_pending = value
726
if (hasattr(self, "dbus_object_path")
727
and bval is not bool(old_value)):
728
dbus_bool = dbus.Boolean(bval, variant_level=1)
729
self.PropertyChanged(dbus.String(u"ApprovalPending"),
732
approvals_pending = property(_get_approvals_pending,
733
_set_approvals_pending)
734
del _get_approvals_pending, _set_approvals_pending
737
def _datetime_to_dbus(dt, variant_level=0):
738
"""Convert a UTC datetime.datetime() to a D-Bus type."""
739
return dbus.String(dt.isoformat(),
740
variant_level=variant_level)
743
oldstate = getattr(self, u"enabled", False)
744
r = Client.enable(self)
745
if oldstate != self.enabled:
747
self.PropertyChanged(dbus.String(u"Enabled"),
748
dbus.Boolean(True, variant_level=1))
749
self.PropertyChanged(
750
dbus.String(u"LastEnabled"),
751
self._datetime_to_dbus(self.last_enabled,
755
def disable(self, quiet = False):
756
oldstate = getattr(self, u"enabled", False)
757
r = Client.disable(self, quiet=quiet)
758
if not quiet and oldstate != self.enabled:
760
self.PropertyChanged(dbus.String(u"Enabled"),
761
dbus.Boolean(False, variant_level=1))
1063
def notifychangeproperty(transform_func,
1064
dbus_name, type_func=lambda x: x,
1066
""" Modify a variable so that it's a property which announces
1067
its changes to DBus.
1069
transform_fun: Function that takes a value and a variant_level
1070
and transforms it to a D-Bus type.
1071
dbus_name: D-Bus name of the variable
1072
type_func: Function that transform the value before sending it
1073
to the D-Bus. Default: no transform
1074
variant_level: D-Bus variant level. Default: 1
1076
attrname = "_{0}".format(dbus_name)
1077
def setter(self, value):
1078
if hasattr(self, "dbus_object_path"):
1079
if (not hasattr(self, attrname) or
1080
type_func(getattr(self, attrname, None))
1081
!= type_func(value)):
1082
dbus_value = transform_func(type_func(value),
1085
self.PropertyChanged(dbus.String(dbus_name),
1087
setattr(self, attrname, value)
1089
return property(lambda self: getattr(self, attrname), setter)
1092
expires = notifychangeproperty(datetime_to_dbus, "Expires")
1093
approvals_pending = notifychangeproperty(dbus.Boolean,
1096
enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1097
last_enabled = notifychangeproperty(datetime_to_dbus,
1099
checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
1100
type_func = lambda checker:
1101
checker is not None)
1102
last_checked_ok = notifychangeproperty(datetime_to_dbus,
1104
last_checker_status = notifychangeproperty(dbus.Int16,
1105
"LastCheckerStatus")
1106
last_approval_request = notifychangeproperty(
1107
datetime_to_dbus, "LastApprovalRequest")
1108
approved_by_default = notifychangeproperty(dbus.Boolean,
1109
"ApprovedByDefault")
1110
approval_delay = notifychangeproperty(dbus.UInt64,
1113
timedelta_to_milliseconds)
1114
approval_duration = notifychangeproperty(
1115
dbus.UInt64, "ApprovalDuration",
1116
type_func = timedelta_to_milliseconds)
1117
host = notifychangeproperty(dbus.String, "Host")
1118
timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1120
timedelta_to_milliseconds)
1121
extended_timeout = notifychangeproperty(
1122
dbus.UInt64, "ExtendedTimeout",
1123
type_func = timedelta_to_milliseconds)
1124
interval = notifychangeproperty(dbus.UInt64,
1127
timedelta_to_milliseconds)
1128
checker_command = notifychangeproperty(dbus.String, "Checker")
1130
del notifychangeproperty
764
1132
def __del__(self, *args, **kwargs):
766
1134
self.remove_from_connection()
767
1135
except LookupError:
769
if hasattr(DBusObjectWithProperties, u"__del__"):
1137
if hasattr(DBusObjectWithProperties, "__del__"):
770
1138
DBusObjectWithProperties.__del__(self, *args, **kwargs)
771
1139
Client.__del__(self, *args, **kwargs)
923
1267
# ApprovalPending - property
924
@dbus_service_property(_interface, signature=u"b", access=u"read")
1268
@dbus_service_property(_interface, signature="b", access="read")
925
1269
def ApprovalPending_dbus_property(self):
926
1270
return dbus.Boolean(bool(self.approvals_pending))
928
1272
# ApprovedByDefault - property
929
@dbus_service_property(_interface, signature=u"b",
1273
@dbus_service_property(_interface, signature="b",
931
1275
def ApprovedByDefault_dbus_property(self, value=None):
932
1276
if value is None: # get
933
1277
return dbus.Boolean(self.approved_by_default)
934
1278
self.approved_by_default = bool(value)
936
self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
937
dbus.Boolean(value, variant_level=1))
939
1280
# ApprovalDelay - property
940
@dbus_service_property(_interface, signature=u"t",
1281
@dbus_service_property(_interface, signature="t",
942
1283
def ApprovalDelay_dbus_property(self, value=None):
943
1284
if value is None: # get
944
1285
return dbus.UInt64(self.approval_delay_milliseconds())
945
1286
self.approval_delay = datetime.timedelta(0, 0, 0, value)
947
self.PropertyChanged(dbus.String(u"ApprovalDelay"),
948
dbus.UInt64(value, variant_level=1))
950
1288
# ApprovalDuration - property
951
@dbus_service_property(_interface, signature=u"t",
1289
@dbus_service_property(_interface, signature="t",
953
1291
def ApprovalDuration_dbus_property(self, value=None):
954
1292
if value is None: # get
955
return dbus.UInt64(self._timedelta_to_milliseconds(
1293
return dbus.UInt64(timedelta_to_milliseconds(
956
1294
self.approval_duration))
957
1295
self.approval_duration = datetime.timedelta(0, 0, 0, value)
959
self.PropertyChanged(dbus.String(u"ApprovalDuration"),
960
dbus.UInt64(value, variant_level=1))
962
1297
# Name - property
963
@dbus_service_property(_interface, signature=u"s", access=u"read")
1298
@dbus_service_property(_interface, signature="s", access="read")
964
1299
def Name_dbus_property(self):
965
1300
return dbus.String(self.name)
967
1302
# Fingerprint - property
968
@dbus_service_property(_interface, signature=u"s", access=u"read")
1303
@dbus_service_property(_interface, signature="s", access="read")
969
1304
def Fingerprint_dbus_property(self):
970
1305
return dbus.String(self.fingerprint)
972
1307
# Host - property
973
@dbus_service_property(_interface, signature=u"s",
1308
@dbus_service_property(_interface, signature="s",
975
1310
def Host_dbus_property(self, value=None):
976
1311
if value is None: # get
977
1312
return dbus.String(self.host)
980
self.PropertyChanged(dbus.String(u"Host"),
981
dbus.String(value, variant_level=1))
1313
self.host = unicode(value)
983
1315
# Created - property
984
@dbus_service_property(_interface, signature=u"s", access=u"read")
1316
@dbus_service_property(_interface, signature="s", access="read")
985
1317
def Created_dbus_property(self):
986
return dbus.String(self._datetime_to_dbus(self.created))
1318
return datetime_to_dbus(self.created)
988
1320
# LastEnabled - property
989
@dbus_service_property(_interface, signature=u"s", access=u"read")
1321
@dbus_service_property(_interface, signature="s", access="read")
990
1322
def LastEnabled_dbus_property(self):
991
if self.last_enabled is None:
992
return dbus.String(u"")
993
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1323
return datetime_to_dbus(self.last_enabled)
995
1325
# Enabled - property
996
@dbus_service_property(_interface, signature=u"b",
1326
@dbus_service_property(_interface, signature="b",
998
1328
def Enabled_dbus_property(self, value=None):
999
1329
if value is None: # get
1000
1330
return dbus.Boolean(self.enabled)
1006
1336
# LastCheckedOK - property
1007
@dbus_service_property(_interface, signature=u"s",
1008
access=u"readwrite")
1337
@dbus_service_property(_interface, signature="s",
1009
1339
def LastCheckedOK_dbus_property(self, value=None):
1010
1340
if value is not None:
1011
1341
self.checked_ok()
1013
if self.last_checked_ok is None:
1014
return dbus.String(u"")
1015
return dbus.String(self._datetime_to_dbus(self
1343
return datetime_to_dbus(self.last_checked_ok)
1345
# LastCheckerStatus - property
1346
@dbus_service_property(_interface, signature="n",
1348
def LastCheckerStatus_dbus_property(self):
1349
return dbus.Int16(self.last_checker_status)
1351
# Expires - property
1352
@dbus_service_property(_interface, signature="s", access="read")
1353
def Expires_dbus_property(self):
1354
return datetime_to_dbus(self.expires)
1356
# LastApprovalRequest - property
1357
@dbus_service_property(_interface, signature="s", access="read")
1358
def LastApprovalRequest_dbus_property(self):
1359
return datetime_to_dbus(self.last_approval_request)
1018
1361
# Timeout - property
1019
@dbus_service_property(_interface, signature=u"t",
1020
access=u"readwrite")
1362
@dbus_service_property(_interface, signature="t",
1021
1364
def Timeout_dbus_property(self, value=None):
1022
1365
if value is None: # get
1023
1366
return dbus.UInt64(self.timeout_milliseconds())
1024
1367
self.timeout = datetime.timedelta(0, 0, 0, value)
1026
self.PropertyChanged(dbus.String(u"Timeout"),
1027
dbus.UInt64(value, variant_level=1))
1028
if getattr(self, u"disable_initiator_tag", None) is None:
1030
1368
# Reschedule timeout
1031
gobject.source_remove(self.disable_initiator_tag)
1032
self.disable_initiator_tag = None
1033
time_to_die = (self.
1034
_timedelta_to_milliseconds((self
1039
if time_to_die <= 0:
1040
# The timeout has passed
1043
self.disable_initiator_tag = (gobject.timeout_add
1044
(time_to_die, self.disable))
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
1377
self.expires = (now +
1378
datetime.timedelta(milliseconds =
1380
if (getattr(self, "disable_initiator_tag", None)
1383
gobject.source_remove(self.disable_initiator_tag)
1384
self.disable_initiator_tag = (gobject.timeout_add
1388
# ExtendedTimeout - property
1389
@dbus_service_property(_interface, signature="t",
1391
def ExtendedTimeout_dbus_property(self, value=None):
1392
if value is None: # get
1393
return dbus.UInt64(self.extended_timeout_milliseconds())
1394
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1046
1396
# Interval - property
1047
@dbus_service_property(_interface, signature=u"t",
1048
access=u"readwrite")
1397
@dbus_service_property(_interface, signature="t",
1049
1399
def Interval_dbus_property(self, value=None):
1050
1400
if value is None: # get
1051
1401
return dbus.UInt64(self.interval_milliseconds())
1052
1402
self.interval = datetime.timedelta(0, 0, 0, value)
1054
self.PropertyChanged(dbus.String(u"Interval"),
1055
dbus.UInt64(value, variant_level=1))
1056
if getattr(self, u"checker_initiator_tag", None) is None:
1403
if getattr(self, "checker_initiator_tag", None) is None:
1058
# Reschedule checker run
1059
gobject.source_remove(self.checker_initiator_tag)
1060
self.checker_initiator_tag = (gobject.timeout_add
1061
(value, self.start_checker))
1062
self.start_checker() # Start one now, too
1406
# Reschedule checker run
1407
gobject.source_remove(self.checker_initiator_tag)
1408
self.checker_initiator_tag = (gobject.timeout_add
1409
(value, self.start_checker))
1410
self.start_checker() # Start one now, too
1064
1412
# Checker - property
1065
@dbus_service_property(_interface, signature=u"s",
1066
access=u"readwrite")
1413
@dbus_service_property(_interface, signature="s",
1067
1415
def Checker_dbus_property(self, value=None):
1068
1416
if value is None: # get
1069
1417
return dbus.String(self.checker_command)
1070
self.checker_command = value
1072
self.PropertyChanged(dbus.String(u"Checker"),
1073
dbus.String(self.checker_command,
1418
self.checker_command = unicode(value)
1076
1420
# CheckerRunning - property
1077
@dbus_service_property(_interface, signature=u"b",
1078
access=u"readwrite")
1421
@dbus_service_property(_interface, signature="b",
1079
1423
def CheckerRunning_dbus_property(self, value=None):
1080
1424
if value is None: # get
1081
1425
return dbus.Boolean(self.checker is not None)
1133
1481
def handle(self):
1134
1482
with contextlib.closing(self.server.child_pipe) as child_pipe:
1135
logger.info(u"TCP connection from: %s",
1483
logger.info("TCP connection from: %s",
1136
1484
unicode(self.client_address))
1137
logger.debug(u"Pipe FD: %d",
1485
logger.debug("Pipe FD: %d",
1138
1486
self.server.child_pipe.fileno())
1140
1488
session = (gnutls.connection
1141
1489
.ClientSession(self.request,
1142
1490
gnutls.connection
1143
1491
.X509Credentials()))
1145
1493
# Note: gnutls.connection.X509Credentials is really a
1146
1494
# generic GnuTLS certificate credentials object so long as
1147
1495
# no X.509 keys are added to it. Therefore, we can use it
1148
1496
# here despite using OpenPGP certificates.
1150
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1151
# u"+AES-256-CBC", u"+SHA1",
1152
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1498
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1499
# "+AES-256-CBC", "+SHA1",
1500
# "+COMP-NULL", "+CTYPE-OPENPGP",
1154
1502
# Use a fallback default, since this MUST be set.
1155
1503
priority = self.server.gnutls_priority
1156
1504
if priority is None:
1157
priority = u"NORMAL"
1158
1506
(gnutls.library.functions
1159
1507
.gnutls_priority_set_direct(session._c_object,
1160
1508
priority, None))
1162
1510
# Start communication using the Mandos protocol
1163
1511
# Get protocol number
1164
1512
line = self.request.makefile().readline()
1165
logger.debug(u"Protocol version: %r", line)
1513
logger.debug("Protocol version: %r", line)
1167
1515
if int(line.strip().split()[0]) > 1:
1168
1516
raise RuntimeError
1169
except (ValueError, IndexError, RuntimeError), error:
1170
logger.error(u"Unknown protocol version: %s", error)
1517
except (ValueError, IndexError, RuntimeError) as error:
1518
logger.error("Unknown protocol version: %s", error)
1173
1521
# Start GnuTLS connection
1175
1523
session.handshake()
1176
except gnutls.errors.GNUTLSError, error:
1177
logger.warning(u"Handshake failed: %s", error)
1524
except gnutls.errors.GNUTLSError as error:
1525
logger.warning("Handshake failed: %s", error)
1178
1526
# Do not run session.bye() here: the session is not
1179
1527
# established. Just abandon the request.
1181
logger.debug(u"Handshake succeeded")
1529
logger.debug("Handshake succeeded")
1183
1531
approval_required = False
1186
1534
fpr = self.fingerprint(self.peer_certificate
1188
except (TypeError, gnutls.errors.GNUTLSError), error:
1189
logger.warning(u"Bad certificate: %s", error)
1537
gnutls.errors.GNUTLSError) as error:
1538
logger.warning("Bad certificate: %s", error)
1191
logger.debug(u"Fingerprint: %s", fpr)
1540
logger.debug("Fingerprint: %s", fpr)
1194
1543
client = ProxyClient(child_pipe, fpr,
1195
1544
self.client_address)
1635
1980
##################################################################
1636
1981
# Parsing of options, both command line and config file
1638
parser = optparse.OptionParser(version = "%%prog %s" % version)
1639
parser.add_option("-i", u"--interface", type=u"string",
1640
metavar="IF", help=u"Bind to interface IF")
1641
parser.add_option("-a", u"--address", type=u"string",
1642
help=u"Address to listen for requests on")
1643
parser.add_option("-p", u"--port", type=u"int",
1644
help=u"Port number to receive requests on")
1645
parser.add_option("--check", action=u"store_true",
1646
help=u"Run self-test")
1647
parser.add_option("--debug", action=u"store_true",
1648
help=u"Debug mode; run in foreground and log to"
1650
parser.add_option("--debuglevel", type=u"string", metavar="Level",
1651
help=u"Debug level for stdout output")
1652
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1653
u" priority string (see GnuTLS documentation)")
1654
parser.add_option("--servicename", type=u"string",
1655
metavar=u"NAME", help=u"Zeroconf service name")
1656
parser.add_option("--configdir", type=u"string",
1657
default=u"/etc/mandos", metavar=u"DIR",
1658
help=u"Directory to search for configuration"
1660
parser.add_option("--no-dbus", action=u"store_false",
1661
dest=u"use_dbus", help=u"Do not provide D-Bus"
1662
u" system bus interface")
1663
parser.add_option("--no-ipv6", action=u"store_false",
1664
dest=u"use_ipv6", help=u"Do not use IPv6")
1665
options = parser.parse_args()[0]
1983
parser = argparse.ArgumentParser()
1984
parser.add_argument("-v", "--version", action="version",
1985
version = "%%(prog)s %s" % version,
1986
help="show version number and exit")
1987
parser.add_argument("-i", "--interface", metavar="IF",
1988
help="Bind to interface IF")
1989
parser.add_argument("-a", "--address",
1990
help="Address to listen for requests on")
1991
parser.add_argument("-p", "--port", type=int,
1992
help="Port number to receive requests on")
1993
parser.add_argument("--check", action="store_true",
1994
help="Run self-test")
1995
parser.add_argument("--debug", action="store_true",
1996
help="Debug mode; run in foreground and log"
1998
parser.add_argument("--debuglevel", metavar="LEVEL",
1999
help="Debug level for stdout output")
2000
parser.add_argument("--priority", help="GnuTLS"
2001
" priority string (see GnuTLS documentation)")
2002
parser.add_argument("--servicename",
2003
metavar="NAME", help="Zeroconf service name")
2004
parser.add_argument("--configdir",
2005
default="/etc/mandos", metavar="DIR",
2006
help="Directory to search for configuration"
2008
parser.add_argument("--no-dbus", action="store_false",
2009
dest="use_dbus", help="Do not provide D-Bus"
2010
" system bus interface")
2011
parser.add_argument("--no-ipv6", action="store_false",
2012
dest="use_ipv6", help="Do not use IPv6")
2013
parser.add_argument("--no-restore", action="store_false",
2014
dest="restore", help="Do not restore stored"
2016
parser.add_argument("--statedir", metavar="DIR",
2017
help="Directory to save/restore state in")
2019
options = parser.parse_args()
1667
2021
if options.check:
1716
2073
##################################################################
1718
2075
# For convenience
1719
debug = server_settings[u"debug"]
1720
debuglevel = server_settings[u"debuglevel"]
1721
use_dbus = server_settings[u"use_dbus"]
1722
use_ipv6 = server_settings[u"use_ipv6"]
1724
if server_settings[u"servicename"] != u"Mandos":
2076
debug = server_settings["debug"]
2077
debuglevel = server_settings["debuglevel"]
2078
use_dbus = server_settings["use_dbus"]
2079
use_ipv6 = server_settings["use_ipv6"]
2080
stored_state_path = os.path.join(server_settings["statedir"],
2084
initlogger(debug, logging.DEBUG)
2089
level = getattr(logging, debuglevel.upper())
2090
initlogger(debug, level)
2092
if server_settings["servicename"] != "Mandos":
1725
2093
syslogger.setFormatter(logging.Formatter
1726
(u'Mandos (%s) [%%(process)d]:'
1727
u' %%(levelname)s: %%(message)s'
1728
% server_settings[u"servicename"]))
2094
('Mandos (%s) [%%(process)d]:'
2095
' %%(levelname)s: %%(message)s'
2096
% server_settings["servicename"]))
1730
2098
# Parse config file with clients
1731
client_defaults = { u"timeout": u"1h",
1733
u"checker": u"fping -q -- %%(host)s",
1735
u"approval_delay": u"0s",
1736
u"approval_duration": u"1s",
1738
client_config = configparser.SafeConfigParser(client_defaults)
1739
client_config.read(os.path.join(server_settings[u"configdir"],
2099
client_config = configparser.SafeConfigParser(Client
2101
client_config.read(os.path.join(server_settings["configdir"],
1742
2104
global mandos_dbus_service
1743
2105
mandos_dbus_service = None
1745
tcp_server = MandosServer((server_settings[u"address"],
1746
server_settings[u"port"]),
2107
tcp_server = MandosServer((server_settings["address"],
2108
server_settings["port"]),
1748
interface=(server_settings[u"interface"]
2110
interface=(server_settings["interface"]
1750
2112
use_ipv6=use_ipv6,
1751
2113
gnutls_priority=
1752
server_settings[u"priority"],
2114
server_settings["priority"],
1753
2115
use_dbus=use_dbus)
1754
pidfilename = u"/var/run/mandos.pid"
1756
pidfile = open(pidfilename, u"w")
1758
logger.error(u"Could not open file %r", pidfilename)
2117
pidfilename = "/var/run/mandos.pid"
2119
pidfile = open(pidfilename, "w")
2121
logger.error("Could not open file %r", pidfilename)
1761
uid = pwd.getpwnam(u"_mandos").pw_uid
1762
gid = pwd.getpwnam(u"_mandos").pw_gid
2124
uid = pwd.getpwnam("_mandos").pw_uid
2125
gid = pwd.getpwnam("_mandos").pw_gid
1763
2126
except KeyError:
1765
uid = pwd.getpwnam(u"mandos").pw_uid
1766
gid = pwd.getpwnam(u"mandos").pw_gid
2128
uid = pwd.getpwnam("mandos").pw_uid
2129
gid = pwd.getpwnam("mandos").pw_gid
1767
2130
except KeyError:
1769
uid = pwd.getpwnam(u"nobody").pw_uid
1770
gid = pwd.getpwnam(u"nobody").pw_gid
2132
uid = pwd.getpwnam("nobody").pw_uid
2133
gid = pwd.getpwnam("nobody").pw_gid
1771
2134
except KeyError:
1777
except OSError, error:
2140
except OSError as error:
1778
2141
if error[0] != errno.EPERM:
1781
if not debug and not debuglevel:
1782
syslogger.setLevel(logging.WARNING)
1783
console.setLevel(logging.WARNING)
1785
level = getattr(logging, debuglevel.upper())
1786
syslogger.setLevel(level)
1787
console.setLevel(level)
1790
2145
# Enable all possible GnuTLS debugging
1818
2176
# End of Avahi example code
1821
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
2179
bus_name = dbus.service.BusName("se.recompile.Mandos",
1822
2180
bus, do_not_queue=True)
1823
except dbus.exceptions.NameExistsException, e:
1824
logger.error(unicode(e) + u", disabling D-Bus")
2181
old_bus_name = (dbus.service.BusName
2182
("se.bsnet.fukt.Mandos", bus,
2184
except dbus.exceptions.NameExistsException as e:
2185
logger.error(unicode(e) + ", disabling D-Bus")
1825
2186
use_dbus = False
1826
server_settings[u"use_dbus"] = False
2187
server_settings["use_dbus"] = False
1827
2188
tcp_server.use_dbus = False
1828
2189
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1829
service = AvahiService(name = server_settings[u"servicename"],
1830
servicetype = u"_mandos._tcp",
1831
protocol = protocol, bus = bus)
2190
service = AvahiServiceToSyslog(name =
2191
server_settings["servicename"],
2192
servicetype = "_mandos._tcp",
2193
protocol = protocol, bus = bus)
1832
2194
if server_settings["interface"]:
1833
2195
service.interface = (if_nametoindex
1834
(str(server_settings[u"interface"])))
1837
# Close all input and output, do double fork, etc.
2196
(str(server_settings["interface"])))
1840
2198
global multiprocessing_manager
1841
2199
multiprocessing_manager = multiprocessing.Manager()
1843
2201
client_class = Client
1845
client_class = functools.partial(ClientDBus, bus = bus)
1846
def client_config_items(config, section):
1847
special_settings = {
1848
"approved_by_default":
1849
lambda: config.getboolean(section,
1850
"approved_by_default"),
1852
for name, value in config.items(section):
2203
client_class = functools.partial(ClientDBusTransitional,
2206
client_settings = Client.config_parser(client_config)
2207
old_client_settings = {}
2210
# Get client data and settings from last running state.
2211
if server_settings["restore"]:
2213
with open(stored_state_path, "rb") as stored_state:
2214
clients_data, old_client_settings = (pickle.load
2216
os.remove(stored_state_path)
2217
except IOError as e:
2218
logger.warning("Could not load persistent state: {0}"
2220
if e.errno != errno.ENOENT:
2222
except EOFError as e:
2223
logger.warning("Could not load persistent state: "
2224
"EOFError: {0}".format(e))
2226
with PGPEngine() as pgp:
2227
for client_name, client in clients_data.iteritems():
2228
# Decide which value to use after restoring saved state.
2229
# We have three different values: Old config file,
2230
# new config file, and saved state.
2231
# New config value takes precedence if it differs from old
2232
# config value, otherwise use saved state.
2233
for name, value in client_settings[client_name].items():
2235
# For each value in new config, check if it
2236
# differs from the old config value (Except for
2237
# the "secret" attribute)
2238
if (name != "secret" and
2239
value != old_client_settings[client_name]
2241
client[name] = value
2245
# Clients who has passed its expire date can still be
2246
# 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
2250
if client["enabled"]:
2251
if datetime.datetime.utcnow() >= client["expires"]:
2252
if not client["last_checked_ok"]:
2254
"disabling client {0} - Client never "
2255
"performed a successful checker"
2256
.format(client_name))
2257
client["enabled"] = False
2258
elif client["last_checker_status"] != 0:
2260
"disabling client {0} - Client "
2261
"last checker failed with error code {1}"
2262
.format(client_name,
2263
client["last_checker_status"]))
2264
client["enabled"] = False
2266
client["expires"] = (datetime.datetime
2268
+ client["timeout"])
2269
logger.debug("Last checker succeeded,"
2270
" keeping {0} enabled"
2271
.format(client_name))
1854
yield (name, special_settings[name]())
1858
tcp_server.clients.update(set(
1859
client_class(name = section,
1860
config= dict(client_config_items(
1861
client_config, section)))
1862
for section in client_config.sections()))
2273
client["secret"] = (
2274
pgp.decrypt(client["encrypted_secret"],
2275
client_settings[client_name]
2278
# If decryption fails, we use secret from new settings
2279
logger.debug("Failed to decrypt {0} old secret"
2280
.format(client_name))
2281
client["secret"] = (
2282
client_settings[client_name]["secret"])
2285
# Add/remove clients based on new changes made to config
2286
for client_name in (set(old_client_settings)
2287
- set(client_settings)):
2288
del clients_data[client_name]
2289
for client_name in (set(client_settings)
2290
- set(old_client_settings)):
2291
clients_data[client_name] = client_settings[client_name]
2293
# Create all client objects
2294
for client_name, client in clients_data.iteritems():
2295
tcp_server.clients[client_name] = client_class(
2296
name = client_name, settings = client)
1863
2298
if not tcp_server.clients:
1864
logger.warning(u"No clients defined")
2299
logger.warning("No clients defined")
1869
pidfile.write(str(pid) + "\n")
1872
logger.error(u"Could not write to file %r with PID %d",
1875
# "pidfile" was never created
2305
pidfile.write(str(pid) + "\n".encode("utf-8"))
2308
logger.error("Could not write to file %r with PID %d",
2311
# "pidfile" was never created
1880
2314
signal.signal(signal.SIGINT, signal.SIG_IGN)
1881
2316
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1882
2317
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())