239
107
max_renames: integer; maximum number of renames
240
108
rename_count: integer; counter so we only rename after collisions
241
109
a sensible number of times
242
group: D-Bus Entry Group
244
bus: dbus.SystemBus()
246
111
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
247
servicetype = None, port = None, TXT = None,
248
domain = "", host = "", max_renames = 32768,
249
protocol = avahi.PROTO_UNSPEC, bus = None):
112
servicetype = None, port = None, TXT = None, domain = "",
113
host = "", max_renames = 32768):
250
114
self.interface = interface
252
116
self.type = servicetype
254
self.TXT = TXT if TXT is not None else []
255
122
self.domain = domain
257
124
self.rename_count = 0
258
125
self.max_renames = max_renames
259
self.protocol = protocol
260
self.group = None # our entry group
263
self.entry_group_state_changed_match = None
264
126
def rename(self):
265
127
"""Derived from the Avahi example code"""
266
128
if self.rename_count >= self.max_renames:
267
logger.critical("No suitable Zeroconf service name found"
268
" after %i retries, exiting.",
129
logger.critical(u"No suitable Zeroconf service name found"
130
u" after %i retries, exiting.",
269
131
self.rename_count)
270
132
raise AvahiServiceError("Too many renames")
271
self.name = unicode(self.server
272
.GetAlternativeServiceName(self.name))
273
logger.info("Changing Zeroconf service name to %r ...",
133
self.name = server.GetAlternativeServiceName(self.name)
134
logger.info(u"Changing Zeroconf service name to %r ...",
136
syslogger.setFormatter(logging.Formatter\
137
('Mandos (%s): %%(levelname)s:'
138
' %%(message)s' % self.name))
278
except dbus.exceptions.DBusException as error:
279
logger.critical("DBusException: %s", error)
282
141
self.rename_count += 1
283
142
def remove(self):
284
143
"""Derived from the Avahi example code"""
285
if self.entry_group_state_changed_match is not None:
286
self.entry_group_state_changed_match.remove()
287
self.entry_group_state_changed_match = None
288
if self.group is not None:
144
if group is not None:
291
147
"""Derived from the Avahi example code"""
293
if self.group is None:
294
self.group = dbus.Interface(
295
self.bus.get_object(avahi.DBUS_NAME,
296
self.server.EntryGroupNew()),
297
avahi.DBUS_INTERFACE_ENTRY_GROUP)
298
self.entry_group_state_changed_match = (
299
self.group.connect_to_signal(
300
'StateChanged', self.entry_group_state_changed))
301
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
302
self.name, self.type)
303
self.group.AddService(
306
dbus.UInt32(0), # flags
307
self.name, self.type,
308
self.domain, self.host,
309
dbus.UInt16(self.port),
310
avahi.string_array_to_txt_array(self.TXT))
312
def entry_group_state_changed(self, state, error):
313
"""Derived from the Avahi example code"""
314
logger.debug("Avahi entry group state change: %i", state)
316
if state == avahi.ENTRY_GROUP_ESTABLISHED:
317
logger.debug("Zeroconf service established.")
318
elif state == avahi.ENTRY_GROUP_COLLISION:
319
logger.info("Zeroconf service name collision.")
321
elif state == avahi.ENTRY_GROUP_FAILURE:
322
logger.critical("Avahi: Error in group state changed %s",
324
raise AvahiGroupError("State changed: %s"
327
"""Derived from the Avahi example code"""
328
if self.group is not None:
331
except (dbus.exceptions.UnknownMethodException,
332
dbus.exceptions.DBusException):
336
def server_state_changed(self, state, error=None):
337
"""Derived from the Avahi example code"""
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)
353
elif state == avahi.SERVER_RUNNING:
357
logger.debug("Unknown state: %r", state)
359
logger.debug("Unknown state: %r: %r", state, error)
361
"""Derived from the Avahi example code"""
362
if self.server is None:
363
self.server = dbus.Interface(
364
self.bus.get_object(avahi.DBUS_NAME,
365
avahi.DBUS_PATH_SERVER,
366
follow_name_owner_changes=True),
367
avahi.DBUS_INTERFACE_SERVER)
368
self.server.connect_to_signal("StateChanged",
369
self.server_state_changed)
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))
150
group = dbus.Interface\
151
(bus.get_object(avahi.DBUS_NAME,
152
server.EntryGroupNew()),
153
avahi.DBUS_INTERFACE_ENTRY_GROUP)
154
group.connect_to_signal('StateChanged',
155
entry_group_state_changed)
156
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
157
service.name, service.type)
159
self.interface, # interface
160
avahi.PROTO_INET6, # protocol
161
dbus.UInt32(0), # flags
162
self.name, self.type,
163
self.domain, self.host,
164
dbus.UInt16(self.port),
165
avahi.string_array_to_txt_array(self.TXT))
168
# From the Avahi example code:
169
group = None # our entry group
170
# End of Avahi example code
388
173
class Client(object):
389
174
"""A representation of a client host served by this server.
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
176
name: string; from the config file, used in log messages
177
fingerprint: string (40 or 32 hexadecimal digits); used to
178
uniquely identify the client
179
secret: bytestring; sent verbatim (over TLS) to client
180
host: string; available for use by the checker command
181
created: datetime.datetime(); object creation, not client host
182
last_checked_ok: datetime.datetime() or None if not yet checked OK
183
timeout: datetime.timedelta(); How long from last_checked_ok
184
until this client is invalid
185
interval: datetime.timedelta(); How often to start a new checker
186
stop_hook: If set, called by stop() as stop_hook(self)
187
checker: subprocess.Popen(); a running checker process used
188
to see if the client lives.
189
'None' if no process is running.
190
checker_initiator_tag: a gobject event source tag, or None
191
stop_initiator_tag: - '' -
192
checker_callback_tag: - '' -
193
checker_command: string; External command which is run to check if
194
client lives. %() expansions are done at
401
195
runtime with vars(self) as dict, so that for
402
196
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
410
fingerprint: string (40 or 32 hexadecimal digits); used to
411
uniquely identify the client
412
host: string; available for use by the checker command
413
interval: datetime.timedelta(); How often to start a new checker
414
last_approval_request: datetime.datetime(); (UTC) or None
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
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
423
timeout: datetime.timedelta(); How long from last_checked_ok
424
until this client is disabled
425
extended_timeout: extra long timeout when password has been sent
426
runtime_expansions: Allowed attributes for runtime expansion.
427
expires: datetime.datetime(); time (UTC) when a client will be
198
_timeout: Real variable for 'timeout'
199
_interval: Real variable for 'interval'
200
_timeout_milliseconds: Used when calling gobject.timeout_add()
201
_interval_milliseconds: - '' -
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",
446
def timeout_milliseconds(self):
447
"Return the 'timeout' attribute in milliseconds"
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)
454
def interval_milliseconds(self):
455
"Return the 'interval' attribute in milliseconds"
456
return timedelta_to_milliseconds(self.interval)
458
def approval_delay_milliseconds(self):
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"""
467
for client_name in config.sections():
468
section = dict(config.items(client_name))
469
client = settings[client_name] = {}
471
client["host"] = section["host"]
472
# Reformat values from string types to Python types
473
client["approved_by_default"] = config.getboolean(
474
client_name, "approved_by_default")
475
client["enabled"] = config.getboolean(client_name, "enabled")
477
client["fingerprint"] = (section["fingerprint"].upper()
479
if "secret" in section:
480
client["secret"] = section["secret"].decode("base64")
481
elif "secfile" in section:
482
with open(os.path.expanduser(os.path.expandvars
483
(section["secfile"])),
485
client["secret"] = secfile.read()
487
raise TypeError("No secret or secfile for section %s"
489
client["timeout"] = string_to_delta(section["timeout"])
490
client["extended_timeout"] = string_to_delta(
491
section["extended_timeout"])
492
client["interval"] = string_to_delta(section["interval"])
493
client["approval_delay"] = string_to_delta(
494
section["approval_delay"])
495
client["approval_duration"] = string_to_delta(
496
section["approval_duration"])
497
client["checker_command"] = section["checker"]
498
client["last_approval_request"] = None
499
client["last_checked_ok"] = None
500
client["last_checker_status"] = None
501
if client["enabled"]:
502
client["last_enabled"] = datetime.datetime.utcnow()
503
client["expires"] = (datetime.datetime.utcnow()
506
client["last_enabled"] = None
507
client["expires"] = None
512
def __init__(self, settings, name = None):
203
def _set_timeout(self, timeout):
204
"Setter function for 'timeout' attribute"
205
self._timeout = timeout
206
self._timeout_milliseconds = ((self.timeout.days
207
* 24 * 60 * 60 * 1000)
208
+ (self.timeout.seconds * 1000)
209
+ (self.timeout.microseconds
211
timeout = property(lambda self: self._timeout,
214
def _set_interval(self, interval):
215
"Setter function for 'interval' attribute"
216
self._interval = interval
217
self._interval_milliseconds = ((self.interval.days
218
* 24 * 60 * 60 * 1000)
219
+ (self.interval.seconds
221
+ (self.interval.microseconds
223
interval = property(lambda self: self._interval,
226
def __init__(self, name = None, stop_hook=None, config=None):
513
227
"""Note: the 'checker' key in 'config' sets the
514
228
'checker_command' attribute and *not* the 'checker'
517
# adding all client settings
518
for setting, value in settings.iteritems():
519
setattr(self, setting, value)
521
logger.debug("Creating client %r", self.name)
233
logger.debug(u"Creating client %r", self.name)
522
234
# Uppercase and remove spaces from fingerprint for later
523
235
# comparison purposes with return value from the fingerprint()
525
logger.debug(" Fingerprint: %s", self.fingerprint)
526
self.created = settings.get("created", datetime.datetime.utcnow())
528
# attributes specific for this server instance
237
self.fingerprint = config["fingerprint"].upper()\
239
logger.debug(u" Fingerprint: %s", self.fingerprint)
240
if "secret" in config:
241
self.secret = config["secret"].decode(u"base64")
242
elif "secfile" in config:
243
secfile = open(config["secfile"])
244
self.secret = secfile.read()
247
raise TypeError(u"No secret or secfile for client %s"
249
self.host = config.get("host", "")
250
self.created = datetime.datetime.now()
251
self.last_checked_ok = None
252
self.timeout = string_to_delta(config["timeout"])
253
self.interval = string_to_delta(config["interval"])
254
self.stop_hook = stop_hook
529
255
self.checker = None
530
256
self.checker_initiator_tag = None
531
self.disable_initiator_tag = None
257
self.stop_initiator_tag = None
532
258
self.checker_callback_tag = None
533
self.current_checker_command = None
535
self.approvals_pending = 0
536
self.changedstate = (multiprocessing_manager
537
.Condition(multiprocessing_manager
539
self.client_structure = [attr for attr in
540
self.__dict__.iterkeys()
541
if not attr.startswith("_")]
542
self.client_structure.append("client_structure")
544
for name, t in inspect.getmembers(type(self),
548
if not name.startswith("_"):
549
self.client_structure.append(name)
551
# Send notice to process children that client state has changed
552
def send_changedstate(self):
553
with self.changedstate:
554
self.changedstate.notify_all()
259
self.check_command = config["checker"]
557
261
"""Start this client's checker and timeout hooks"""
558
if getattr(self, "enabled", False):
561
self.send_changedstate()
562
self.expires = datetime.datetime.utcnow() + self.timeout
564
self.last_enabled = datetime.datetime.utcnow()
567
def disable(self, quiet=True):
568
"""Disable this client."""
569
if not getattr(self, "enabled", False):
262
# Schedule a new checker to be started an 'interval' from now,
263
# and every interval from then on.
264
self.checker_initiator_tag = gobject.timeout_add\
265
(self._interval_milliseconds,
267
# Also start a new checker *right now*.
269
# Schedule a stop() when 'timeout' has passed
270
self.stop_initiator_tag = gobject.timeout_add\
271
(self._timeout_milliseconds,
275
The possibility that a client might be restarted is left open,
276
but not currently used."""
277
# If this client doesn't have a secret, it is already stopped.
278
if hasattr(self, "secret") and self.secret:
279
logger.info(u"Stopping client %s", self.name)
572
self.send_changedstate()
574
logger.info("Disabling client %s", self.name)
575
if getattr(self, "disable_initiator_tag", False):
576
gobject.source_remove(self.disable_initiator_tag)
577
self.disable_initiator_tag = None
283
if getattr(self, "stop_initiator_tag", False):
284
gobject.source_remove(self.stop_initiator_tag)
285
self.stop_initiator_tag = None
579
286
if getattr(self, "checker_initiator_tag", False):
580
287
gobject.source_remove(self.checker_initiator_tag)
581
288
self.checker_initiator_tag = None
582
289
self.stop_checker()
584
292
# Do not run this again if called by a gobject.timeout_add
587
294
def __del__(self):
590
def init_checker(self):
591
# Schedule a new checker to be started an 'interval' from now,
592
# and every interval from then on.
593
self.checker_initiator_tag = (gobject.timeout_add
594
(self.interval_milliseconds(),
596
# Schedule a disable() when 'timeout' has passed
597
self.disable_initiator_tag = (gobject.timeout_add
598
(self.timeout_milliseconds(),
600
# Also start a new checker *right now*.
603
def checker_callback(self, pid, condition, command):
295
self.stop_hook = None
297
def checker_callback(self, pid, condition):
604
298
"""The checker has completed, so take appropriate actions."""
299
now = datetime.datetime.now()
605
300
self.checker_callback_tag = None
606
301
self.checker = None
607
if os.WIFEXITED(condition):
608
self.last_checker_status = os.WEXITSTATUS(condition)
609
if self.last_checker_status == 0:
610
logger.info("Checker for %(name)s succeeded",
614
logger.info("Checker for %(name)s failed",
617
self.last_checker_status = -1
618
logger.warning("Checker for %(name)s crashed?",
302
if os.WIFEXITED(condition) \
303
and (os.WEXITSTATUS(condition) == 0):
304
logger.info(u"Checker for %(name)s succeeded",
306
self.last_checked_ok = now
307
gobject.source_remove(self.stop_initiator_tag)
308
self.stop_initiator_tag = gobject.timeout_add\
309
(self._timeout_milliseconds,
311
elif not os.WIFEXITED(condition):
312
logger.warning(u"Checker for %(name)s crashed?",
621
def checked_ok(self, timeout=None):
622
"""Bump up the timeout for this client.
624
This should only be called when the client has been seen,
628
timeout = self.timeout
629
self.last_checked_ok = datetime.datetime.utcnow()
630
if self.disable_initiator_tag is not None:
631
gobject.source_remove(self.disable_initiator_tag)
632
if getattr(self, "enabled", False):
633
self.disable_initiator_tag = (gobject.timeout_add
634
(timedelta_to_milliseconds
635
(timeout), self.disable))
636
self.expires = datetime.datetime.utcnow() + timeout
638
def need_approval(self):
639
self.last_approval_request = datetime.datetime.utcnow()
315
logger.info(u"Checker for %(name)s failed",
641
317
def start_checker(self):
642
318
"""Start a new checker subprocess if one is not running.
644
319
If a checker already exists, leave it running and do
646
321
# The reason for not killing a running checker is that if we
720
366
self.checker_callback_tag = None
721
367
if getattr(self, "checker", None) is None:
723
logger.debug("Stopping checker for %(name)s", vars(self))
369
logger.debug(u"Stopping checker for %(name)s", vars(self))
725
371
os.kill(self.checker.pid, signal.SIGTERM)
727
373
#if self.checker.poll() is None:
728
374
# os.kill(self.checker.pid, signal.SIGKILL)
729
except OSError as error:
375
except OSError, error:
730
376
if error.errno != errno.ESRCH: # No such process
732
378
self.checker = None
735
def dbus_service_property(dbus_interface, signature="v",
736
access="readwrite", byte_arrays=False):
737
"""Decorators for marking methods of a DBusObjectWithProperties to
738
become properties on the D-Bus.
740
The decorated method will be called with no arguments by "Get"
741
and with one argument by "Set".
743
The parameters, where they are supported, are the same as
744
dbus.service.method, except there is only "signature", since the
745
type from Get() and the type sent to Set() is the same.
747
# Encoding deeply encoded byte arrays is not supported yet by the
748
# "Set" method, so we fail early here:
749
if byte_arrays and signature != "ay":
750
raise ValueError("Byte arrays not supported for non-'ay'"
751
" signature %r" % signature)
753
func._dbus_is_property = True
754
func._dbus_interface = dbus_interface
755
func._dbus_signature = signature
756
func._dbus_access = access
757
func._dbus_name = func.__name__
758
if func._dbus_name.endswith("_dbus_property"):
759
func._dbus_name = func._dbus_name[:-14]
760
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
765
class DBusPropertyException(dbus.exceptions.DBusException):
766
"""A base class for D-Bus property-related exceptions
768
def __unicode__(self):
769
return unicode(str(self))
772
class DBusPropertyAccessException(DBusPropertyException):
773
"""A property's access permissions disallows an operation.
778
class DBusPropertyNotFound(DBusPropertyException):
779
"""An attempt was made to access a non-existing property.
784
class DBusObjectWithProperties(dbus.service.Object):
785
"""A D-Bus object with properties.
787
Classes inheriting from this can use the dbus_service_property
788
decorator to expose methods as D-Bus properties. It exposes the
789
standard Get(), Set(), and GetAll() methods on the D-Bus.
793
def _is_dbus_property(obj):
794
return getattr(obj, "_dbus_is_property", False)
796
def _get_all_dbus_properties(self):
797
"""Returns a generator of (name, attribute) pairs
799
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
800
for cls in self.__class__.__mro__
802
inspect.getmembers(cls, self._is_dbus_property))
804
def _get_dbus_property(self, interface_name, property_name):
805
"""Returns a bound method if one exists which is a D-Bus
806
property with the specified name and interface.
808
for cls in self.__class__.__mro__:
809
for name, value in (inspect.getmembers
810
(cls, self._is_dbus_property)):
811
if (value._dbus_name == property_name
812
and value._dbus_interface == interface_name):
813
return value.__get__(self)
816
raise DBusPropertyNotFound(self.dbus_object_path + ":"
817
+ interface_name + "."
820
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
822
def Get(self, interface_name, property_name):
823
"""Standard D-Bus property Get() method, see D-Bus standard.
825
prop = self._get_dbus_property(interface_name, property_name)
826
if prop._dbus_access == "write":
827
raise DBusPropertyAccessException(property_name)
829
if not hasattr(value, "variant_level"):
831
return type(value)(value, variant_level=value.variant_level+1)
833
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
834
def Set(self, interface_name, property_name, value):
835
"""Standard D-Bus property Set() method, see D-Bus standard.
837
prop = self._get_dbus_property(interface_name, property_name)
838
if prop._dbus_access == "read":
839
raise DBusPropertyAccessException(property_name)
840
if prop._dbus_get_args_options["byte_arrays"]:
841
# The byte_arrays option is not supported yet on
842
# signatures other than "ay".
843
if prop._dbus_signature != "ay":
845
value = dbus.ByteArray(''.join(unichr(byte)
849
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
850
out_signature="a{sv}")
851
def GetAll(self, interface_name):
852
"""Standard D-Bus property GetAll() method, see D-Bus
855
Note: Will not include properties with access="write".
858
for name, prop in self._get_all_dbus_properties():
860
and interface_name != prop._dbus_interface):
861
# Interface non-empty but did not match
863
# Ignore write-only properties
864
if prop._dbus_access == "write":
867
if not hasattr(value, "variant_level"):
868
properties[name] = value
870
properties[name] = type(value)(value, variant_level=
871
value.variant_level+1)
872
return dbus.Dictionary(properties, signature="sv")
874
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
876
path_keyword='object_path',
877
connection_keyword='connection')
878
def Introspect(self, object_path, connection):
879
"""Standard D-Bus method, overloaded to insert property tags.
881
xmlstring = dbus.service.Object.Introspect(self, object_path,
884
document = xml.dom.minidom.parseString(xmlstring)
885
def make_tag(document, name, prop):
886
e = document.createElement("property")
887
e.setAttribute("name", name)
888
e.setAttribute("type", prop._dbus_signature)
889
e.setAttribute("access", prop._dbus_access)
891
for if_tag in document.getElementsByTagName("interface"):
892
for tag in (make_tag(document, name, prop)
894
in self._get_all_dbus_properties()
895
if prop._dbus_interface
896
== if_tag.getAttribute("name")):
897
if_tag.appendChild(tag)
898
# Add the names to the return values for the
899
# "org.freedesktop.DBus.Properties" methods
900
if (if_tag.getAttribute("name")
901
== "org.freedesktop.DBus.Properties"):
902
for cn in if_tag.getElementsByTagName("method"):
903
if cn.getAttribute("name") == "Get":
904
for arg in cn.getElementsByTagName("arg"):
905
if (arg.getAttribute("direction")
907
arg.setAttribute("name", "value")
908
elif cn.getAttribute("name") == "GetAll":
909
for arg in cn.getElementsByTagName("arg"):
910
if (arg.getAttribute("direction")
912
arg.setAttribute("name", "props")
913
xmlstring = document.toxml("utf-8")
915
except (AttributeError, xml.dom.DOMException,
916
xml.parsers.expat.ExpatError) as error:
917
logger.error("Failed to override Introspection method",
922
def datetime_to_dbus (dt, variant_level=0):
923
"""Convert a UTC datetime.datetime() to a D-Bus type."""
925
return dbus.String("", variant_level = variant_level)
926
return dbus.String(dt.isoformat(),
927
variant_level=variant_level)
930
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
932
"""Applied to an empty subclass of a D-Bus object, this metaclass
933
will add additional D-Bus attributes matching a certain pattern.
935
def __new__(mcs, name, bases, attr):
936
# Go through all the base classes which could have D-Bus
937
# methods, signals, or properties in them
938
for base in (b for b in bases
939
if issubclass(b, dbus.service.Object)):
940
# Go though all attributes of the base class
941
for attrname, attribute in inspect.getmembers(base):
942
# Ignore non-D-Bus attributes, and D-Bus attributes
943
# with the wrong interface name
944
if (not hasattr(attribute, "_dbus_interface")
945
or not attribute._dbus_interface
946
.startswith("se.recompile.Mandos")):
948
# Create an alternate D-Bus interface name based on
950
alt_interface = (attribute._dbus_interface
951
.replace("se.recompile.Mandos",
952
"se.bsnet.fukt.Mandos"))
953
# Is this a D-Bus signal?
954
if getattr(attribute, "_dbus_is_signal", False):
955
# Extract the original non-method function by
957
nonmethod_func = (dict(
958
zip(attribute.func_code.co_freevars,
959
attribute.__closure__))["func"]
961
# Create a new, but exactly alike, function
962
# object, and decorate it to be a new D-Bus signal
963
# with the alternate D-Bus interface name
964
new_function = (dbus.service.signal
966
attribute._dbus_signature)
968
nonmethod_func.func_code,
969
nonmethod_func.func_globals,
970
nonmethod_func.func_name,
971
nonmethod_func.func_defaults,
972
nonmethod_func.func_closure)))
973
# Define a creator of a function to call both the
974
# old and new functions, so both the old and new
975
# signals gets sent when the function is called
976
def fixscope(func1, func2):
977
"""This function is a scope container to pass
978
func1 and func2 to the "call_both" function
979
outside of its arguments"""
980
def call_both(*args, **kwargs):
981
"""This function will emit two D-Bus
982
signals by calling func1 and func2"""
983
func1(*args, **kwargs)
984
func2(*args, **kwargs)
986
# Create the "call_both" function and add it to
988
attr[attrname] = fixscope(attribute,
990
# Is this a D-Bus method?
991
elif getattr(attribute, "_dbus_is_method", False):
992
# Create a new, but exactly alike, function
993
# object. Decorate it to be a new D-Bus method
994
# with the alternate D-Bus interface name. Add it
996
attr[attrname] = (dbus.service.method
998
attribute._dbus_in_signature,
999
attribute._dbus_out_signature)
1001
(attribute.func_code,
1002
attribute.func_globals,
1003
attribute.func_name,
1004
attribute.func_defaults,
1005
attribute.func_closure)))
1006
# Is this a D-Bus property?
1007
elif getattr(attribute, "_dbus_is_property", False):
1008
# Create a new, but exactly alike, function
1009
# object, and decorate it to be a new D-Bus
1010
# property with the alternate D-Bus interface
1011
# name. Add it to the class.
1012
attr[attrname] = (dbus_service_property
1014
attribute._dbus_signature,
1015
attribute._dbus_access,
1017
._dbus_get_args_options
1020
(attribute.func_code,
1021
attribute.func_globals,
1022
attribute.func_name,
1023
attribute.func_defaults,
1024
attribute.func_closure)))
1025
return type.__new__(mcs, name, bases, attr)
1028
class ClientDBus(Client, DBusObjectWithProperties):
1029
"""A Client class using D-Bus
1032
dbus_object_path: dbus.ObjectPath
1033
bus: dbus.SystemBus()
1036
runtime_expansions = (Client.runtime_expansions
1037
+ ("dbus_object_path",))
1039
# dbus.service.Object doesn't use super(), so we can't either.
1041
def __init__(self, bus = None, *args, **kwargs):
1043
Client.__init__(self, *args, **kwargs)
1044
self._approvals_pending = 0
1046
self._approvals_pending = 0
1047
# Only now, when this client is initialized, can it show up on
1049
client_object_name = unicode(self.name).translate(
1050
{ord("."): ord("_"),
1051
ord("-"): ord("_")})
1052
self.dbus_object_path = (dbus.ObjectPath
1053
("/clients/" + client_object_name))
1054
DBusObjectWithProperties.__init__(self, self.bus,
1055
self.dbus_object_path)
1057
def notifychangeproperty(transform_func,
1058
dbus_name, type_func=lambda x: x,
1060
""" Modify a variable so that it's a property which announces
1061
its changes to DBus.
1063
transform_fun: Function that takes a value and a variant_level
1064
and transforms it to a D-Bus type.
1065
dbus_name: D-Bus name of the variable
1066
type_func: Function that transform the value before sending it
1067
to the D-Bus. Default: no transform
1068
variant_level: D-Bus variant level. Default: 1
1070
attrname = "_{0}".format(dbus_name)
1071
def setter(self, value):
1072
if hasattr(self, "dbus_object_path"):
1073
if (not hasattr(self, attrname) or
1074
type_func(getattr(self, attrname, None))
1075
!= type_func(value)):
1076
dbus_value = transform_func(type_func(value),
1079
self.PropertyChanged(dbus.String(dbus_name),
1081
setattr(self, attrname, value)
1083
return property(lambda self: getattr(self, attrname), setter)
1086
expires = notifychangeproperty(datetime_to_dbus, "Expires")
1087
approvals_pending = notifychangeproperty(dbus.Boolean,
1090
enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1091
last_enabled = notifychangeproperty(datetime_to_dbus,
1093
checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
1094
type_func = lambda checker:
1095
checker is not None)
1096
last_checked_ok = notifychangeproperty(datetime_to_dbus,
1098
last_approval_request = notifychangeproperty(
1099
datetime_to_dbus, "LastApprovalRequest")
1100
approved_by_default = notifychangeproperty(dbus.Boolean,
1101
"ApprovedByDefault")
1102
approval_delay = notifychangeproperty(dbus.UInt64,
1105
timedelta_to_milliseconds)
1106
approval_duration = notifychangeproperty(
1107
dbus.UInt64, "ApprovalDuration",
1108
type_func = timedelta_to_milliseconds)
1109
host = notifychangeproperty(dbus.String, "Host")
1110
timeout = notifychangeproperty(dbus.UInt64, "Timeout",
1112
timedelta_to_milliseconds)
1113
extended_timeout = notifychangeproperty(
1114
dbus.UInt64, "ExtendedTimeout",
1115
type_func = timedelta_to_milliseconds)
1116
interval = notifychangeproperty(dbus.UInt64,
1119
timedelta_to_milliseconds)
1120
checker_command = notifychangeproperty(dbus.String, "Checker")
1122
del notifychangeproperty
1124
def __del__(self, *args, **kwargs):
1126
self.remove_from_connection()
1129
if hasattr(DBusObjectWithProperties, "__del__"):
1130
DBusObjectWithProperties.__del__(self, *args, **kwargs)
1131
Client.__del__(self, *args, **kwargs)
1133
def checker_callback(self, pid, condition, command,
1135
self.checker_callback_tag = None
1137
if os.WIFEXITED(condition):
1138
exitstatus = os.WEXITSTATUS(condition)
1140
self.CheckerCompleted(dbus.Int16(exitstatus),
1141
dbus.Int64(condition),
1142
dbus.String(command))
1145
self.CheckerCompleted(dbus.Int16(-1),
1146
dbus.Int64(condition),
1147
dbus.String(command))
1149
return Client.checker_callback(self, pid, condition, command,
1152
def start_checker(self, *args, **kwargs):
1153
old_checker = self.checker
1154
if self.checker is not None:
1155
old_checker_pid = self.checker.pid
1157
old_checker_pid = None
1158
r = Client.start_checker(self, *args, **kwargs)
1159
# Only if new checker process was started
1160
if (self.checker is not None
1161
and old_checker_pid != self.checker.pid):
1163
self.CheckerStarted(self.current_checker_command)
1166
def _reset_approved(self):
1167
self.approved = None
1170
def approve(self, value=True):
1171
self.send_changedstate()
1172
self.approved = value
1173
gobject.timeout_add(timedelta_to_milliseconds
1174
(self.approval_duration),
1175
self._reset_approved)
1178
## D-Bus methods, signals & properties
1179
_interface = "se.recompile.Mandos.Client"
1183
# CheckerCompleted - signal
1184
@dbus.service.signal(_interface, signature="nxs")
1185
def CheckerCompleted(self, exitcode, waitstatus, command):
1189
# CheckerStarted - signal
1190
@dbus.service.signal(_interface, signature="s")
1191
def CheckerStarted(self, command):
1195
# PropertyChanged - signal
1196
@dbus.service.signal(_interface, signature="sv")
1197
def PropertyChanged(self, property, value):
1201
# GotSecret - signal
1202
@dbus.service.signal(_interface)
1203
def GotSecret(self):
1205
Is sent after a successful transfer of secret from the Mandos
1206
server to mandos-client
1211
@dbus.service.signal(_interface, signature="s")
1212
def Rejected(self, reason):
1216
# NeedApproval - signal
1217
@dbus.service.signal(_interface, signature="tb")
1218
def NeedApproval(self, timeout, default):
1220
return self.need_approval()
1222
# NeRwequest - signal
1223
@dbus.service.signal(_interface, signature="s")
1224
def NewRequest(self, ip):
1226
Is sent after a client request a password.
1233
@dbus.service.method(_interface, in_signature="b")
1234
def Approve(self, value):
1237
# CheckedOK - method
1238
@dbus.service.method(_interface)
1239
def CheckedOK(self):
1243
@dbus.service.method(_interface)
1248
# StartChecker - method
1249
@dbus.service.method(_interface)
1250
def StartChecker(self):
1252
self.start_checker()
1255
@dbus.service.method(_interface)
1260
# StopChecker - method
1261
@dbus.service.method(_interface)
1262
def StopChecker(self):
1267
# ApprovalPending - property
1268
@dbus_service_property(_interface, signature="b", access="read")
1269
def ApprovalPending_dbus_property(self):
1270
return dbus.Boolean(bool(self.approvals_pending))
1272
# ApprovedByDefault - property
1273
@dbus_service_property(_interface, signature="b",
1275
def ApprovedByDefault_dbus_property(self, value=None):
1276
if value is None: # get
1277
return dbus.Boolean(self.approved_by_default)
1278
self.approved_by_default = bool(value)
1280
# ApprovalDelay - property
1281
@dbus_service_property(_interface, signature="t",
1283
def ApprovalDelay_dbus_property(self, value=None):
1284
if value is None: # get
1285
return dbus.UInt64(self.approval_delay_milliseconds())
1286
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1288
# ApprovalDuration - property
1289
@dbus_service_property(_interface, signature="t",
1291
def ApprovalDuration_dbus_property(self, value=None):
1292
if value is None: # get
1293
return dbus.UInt64(timedelta_to_milliseconds(
1294
self.approval_duration))
1295
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1298
@dbus_service_property(_interface, signature="s", access="read")
1299
def Name_dbus_property(self):
1300
return dbus.String(self.name)
1302
# Fingerprint - property
1303
@dbus_service_property(_interface, signature="s", access="read")
1304
def Fingerprint_dbus_property(self):
1305
return dbus.String(self.fingerprint)
1308
@dbus_service_property(_interface, signature="s",
1310
def Host_dbus_property(self, value=None):
1311
if value is None: # get
1312
return dbus.String(self.host)
1313
self.host = unicode(value)
1315
# Created - property
1316
@dbus_service_property(_interface, signature="s", access="read")
1317
def Created_dbus_property(self):
1318
return datetime_to_dbus(self.created)
1320
# LastEnabled - property
1321
@dbus_service_property(_interface, signature="s", access="read")
1322
def LastEnabled_dbus_property(self):
1323
return datetime_to_dbus(self.last_enabled)
1325
# Enabled - property
1326
@dbus_service_property(_interface, signature="b",
1328
def Enabled_dbus_property(self, value=None):
1329
if value is None: # get
1330
return dbus.Boolean(self.enabled)
1336
# LastCheckedOK - property
1337
@dbus_service_property(_interface, signature="s",
1339
def LastCheckedOK_dbus_property(self, value=None):
1340
if value is not None:
1343
return datetime_to_dbus(self.last_checked_ok)
1345
# Expires - property
1346
@dbus_service_property(_interface, signature="s", access="read")
1347
def Expires_dbus_property(self):
1348
return datetime_to_dbus(self.expires)
1350
# LastApprovalRequest - property
1351
@dbus_service_property(_interface, signature="s", access="read")
1352
def LastApprovalRequest_dbus_property(self):
1353
return datetime_to_dbus(self.last_approval_request)
1355
# Timeout - property
1356
@dbus_service_property(_interface, signature="t",
1358
def Timeout_dbus_property(self, value=None):
1359
if value is None: # get
1360
return dbus.UInt64(self.timeout_milliseconds())
1361
self.timeout = datetime.timedelta(0, 0, 0, value)
1362
if getattr(self, "disable_initiator_tag", None) is None:
1364
# Reschedule timeout
1365
gobject.source_remove(self.disable_initiator_tag)
1366
self.disable_initiator_tag = None
1368
time_to_die = timedelta_to_milliseconds((self
1373
if time_to_die <= 0:
1374
# The timeout has passed
1377
self.expires = (datetime.datetime.utcnow()
1378
+ datetime.timedelta(milliseconds =
1380
self.disable_initiator_tag = (gobject.timeout_add
1381
(time_to_die, self.disable))
1383
# ExtendedTimeout - property
1384
@dbus_service_property(_interface, signature="t",
1386
def ExtendedTimeout_dbus_property(self, value=None):
1387
if value is None: # get
1388
return dbus.UInt64(self.extended_timeout_milliseconds())
1389
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1391
# Interval - property
1392
@dbus_service_property(_interface, signature="t",
1394
def Interval_dbus_property(self, value=None):
1395
if value is None: # get
1396
return dbus.UInt64(self.interval_milliseconds())
1397
self.interval = datetime.timedelta(0, 0, 0, value)
1398
if getattr(self, "checker_initiator_tag", None) is None:
1401
# Reschedule checker run
1402
gobject.source_remove(self.checker_initiator_tag)
1403
self.checker_initiator_tag = (gobject.timeout_add
1404
(value, self.start_checker))
1405
self.start_checker() # Start one now, too
1407
# Checker - property
1408
@dbus_service_property(_interface, signature="s",
1410
def Checker_dbus_property(self, value=None):
1411
if value is None: # get
1412
return dbus.String(self.checker_command)
1413
self.checker_command = unicode(value)
1415
# CheckerRunning - property
1416
@dbus_service_property(_interface, signature="b",
1418
def CheckerRunning_dbus_property(self, value=None):
1419
if value is None: # get
1420
return dbus.Boolean(self.checker is not None)
1422
self.start_checker()
1426
# ObjectPath - property
1427
@dbus_service_property(_interface, signature="o", access="read")
1428
def ObjectPath_dbus_property(self):
1429
return self.dbus_object_path # is already a dbus.ObjectPath
1432
@dbus_service_property(_interface, signature="ay",
1433
access="write", byte_arrays=True)
1434
def Secret_dbus_property(self, value):
1435
self.secret = str(value)
1440
class ProxyClient(object):
1441
def __init__(self, child_pipe, fpr, address):
1442
self._pipe = child_pipe
1443
self._pipe.send(('init', fpr, address))
1444
if not self._pipe.recv():
1447
def __getattribute__(self, name):
1449
return super(ProxyClient, self).__getattribute__(name)
1450
self._pipe.send(('getattr', name))
1451
data = self._pipe.recv()
1452
if data[0] == 'data':
1454
if data[0] == 'function':
1455
def func(*args, **kwargs):
1456
self._pipe.send(('funcall', name, args, kwargs))
1457
return self._pipe.recv()[1]
1460
def __setattr__(self, name, value):
1462
return super(ProxyClient, self).__setattr__(name, value)
1463
self._pipe.send(('setattr', name, value))
1466
class ClientDBusTransitional(ClientDBus):
1467
__metaclass__ = AlternateDBusNamesMetaclass
1470
class ClientHandler(socketserver.BaseRequestHandler, object):
1471
"""A class to handle client connections.
1473
Instantiated once for each connection to handle it.
379
def still_valid(self):
380
"""Has the timeout not yet passed for this client?"""
381
now = datetime.datetime.now()
382
if self.last_checked_ok is None:
383
return now < (self.created + self.timeout)
385
return now < (self.last_checked_ok + self.timeout)
388
def peer_certificate(session):
389
"Return the peer's OpenPGP certificate as a bytestring"
390
# If not an OpenPGP certificate...
391
if gnutls.library.functions.gnutls_certificate_type_get\
392
(session._c_object) \
393
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
394
# ...do the normal thing
395
return session.peer_certificate
396
list_size = ctypes.c_uint()
397
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
398
(session._c_object, ctypes.byref(list_size))
399
if list_size.value == 0:
402
return ctypes.string_at(cert.data, cert.size)
405
def fingerprint(openpgp):
406
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
407
# New GnuTLS "datum" with the OpenPGP public key
408
datum = gnutls.library.types.gnutls_datum_t\
409
(ctypes.cast(ctypes.c_char_p(openpgp),
410
ctypes.POINTER(ctypes.c_ubyte)),
411
ctypes.c_uint(len(openpgp)))
412
# New empty GnuTLS certificate
413
crt = gnutls.library.types.gnutls_openpgp_crt_t()
414
gnutls.library.functions.gnutls_openpgp_crt_init\
416
# Import the OpenPGP public key into the certificate
417
gnutls.library.functions.gnutls_openpgp_crt_import\
418
(crt, ctypes.byref(datum),
419
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
420
# Verify the self signature in the key
421
crtverify = ctypes.c_uint()
422
gnutls.library.functions.gnutls_openpgp_crt_verify_self\
423
(crt, 0, ctypes.byref(crtverify))
424
if crtverify.value != 0:
425
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
426
raise gnutls.errors.CertificateSecurityError("Verify failed")
427
# New buffer for the fingerprint
428
buf = ctypes.create_string_buffer(20)
429
buf_len = ctypes.c_size_t()
430
# Get the fingerprint from the certificate into the buffer
431
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
432
(crt, ctypes.byref(buf), ctypes.byref(buf_len))
433
# Deinit the certificate
434
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
435
# Convert the buffer to a Python bytestring
436
fpr = ctypes.string_at(buf, buf_len.value)
437
# Convert the bytestring to hexadecimal notation
438
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
442
class TCP_handler(SocketServer.BaseRequestHandler, object):
443
"""A TCP request handler class.
444
Instantiated by IPv6_TCPServer for each request to handle it.
1474
445
Note: This will run in its own forked process."""
1476
447
def handle(self):
1477
with contextlib.closing(self.server.child_pipe) as child_pipe:
1478
logger.info("TCP connection from: %s",
1479
unicode(self.client_address))
1480
logger.debug("Pipe FD: %d",
1481
self.server.child_pipe.fileno())
1483
session = (gnutls.connection
1484
.ClientSession(self.request,
1486
.X509Credentials()))
1488
# Note: gnutls.connection.X509Credentials is really a
1489
# generic GnuTLS certificate credentials object so long as
1490
# no X.509 keys are added to it. Therefore, we can use it
1491
# here despite using OpenPGP certificates.
1493
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1494
# "+AES-256-CBC", "+SHA1",
1495
# "+COMP-NULL", "+CTYPE-OPENPGP",
1497
# Use a fallback default, since this MUST be set.
1498
priority = self.server.gnutls_priority
1499
if priority is None:
1501
(gnutls.library.functions
1502
.gnutls_priority_set_direct(session._c_object,
1505
# Start communication using the Mandos protocol
1506
# Get protocol number
1507
line = self.request.makefile().readline()
1508
logger.debug("Protocol version: %r", line)
1510
if int(line.strip().split()[0]) > 1:
1512
except (ValueError, IndexError, RuntimeError) as error:
1513
logger.error("Unknown protocol version: %s", error)
1516
# Start GnuTLS connection
1519
except gnutls.errors.GNUTLSError as error:
1520
logger.warning("Handshake failed: %s", error)
1521
# Do not run session.bye() here: the session is not
1522
# established. Just abandon the request.
1524
logger.debug("Handshake succeeded")
1526
approval_required = False
1529
fpr = self.fingerprint(self.peer_certificate
1532
gnutls.errors.GNUTLSError) as error:
1533
logger.warning("Bad certificate: %s", error)
1535
logger.debug("Fingerprint: %s", fpr)
1538
client = ProxyClient(child_pipe, fpr,
1539
self.client_address)
1543
if self.server.use_dbus:
1545
client.NewRequest(str(self.client_address))
1547
if client.approval_delay:
1548
delay = client.approval_delay
1549
client.approvals_pending += 1
1550
approval_required = True
1553
if not client.enabled:
1554
logger.info("Client %s is disabled",
1556
if self.server.use_dbus:
1558
client.Rejected("Disabled")
1561
if client.approved or not client.approval_delay:
1562
#We are approved or approval is disabled
1564
elif client.approved is None:
1565
logger.info("Client %s needs approval",
1567
if self.server.use_dbus:
1569
client.NeedApproval(
1570
client.approval_delay_milliseconds(),
1571
client.approved_by_default)
1573
logger.warning("Client %s was not approved",
1575
if self.server.use_dbus:
1577
client.Rejected("Denied")
1580
#wait until timeout or approved
1581
time = datetime.datetime.now()
1582
client.changedstate.acquire()
1583
(client.changedstate.wait
1584
(float(client.timedelta_to_milliseconds(delay)
1586
client.changedstate.release()
1587
time2 = datetime.datetime.now()
1588
if (time2 - time) >= delay:
1589
if not client.approved_by_default:
1590
logger.warning("Client %s timed out while"
1591
" waiting for approval",
1593
if self.server.use_dbus:
1595
client.Rejected("Approval timed out")
1600
delay -= time2 - time
1603
while sent_size < len(client.secret):
1605
sent = session.send(client.secret[sent_size:])
1606
except gnutls.errors.GNUTLSError as error:
1607
logger.warning("gnutls send failed")
1609
logger.debug("Sent: %d, remaining: %d",
1610
sent, len(client.secret)
1611
- (sent_size + sent))
1614
logger.info("Sending secret to %s", client.name)
1615
# bump the timeout using extended_timeout
1616
client.checked_ok(client.extended_timeout)
1617
if self.server.use_dbus:
1622
if approval_required:
1623
client.approvals_pending -= 1
1626
except gnutls.errors.GNUTLSError as error:
1627
logger.warning("GnuTLS bye failed")
1630
def peer_certificate(session):
1631
"Return the peer's OpenPGP certificate as a bytestring"
1632
# If not an OpenPGP certificate...
1633
if (gnutls.library.functions
1634
.gnutls_certificate_type_get(session._c_object)
1635
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1636
# ...do the normal thing
1637
return session.peer_certificate
1638
list_size = ctypes.c_uint(1)
1639
cert_list = (gnutls.library.functions
1640
.gnutls_certificate_get_peers
1641
(session._c_object, ctypes.byref(list_size)))
1642
if not bool(cert_list) and list_size.value != 0:
1643
raise gnutls.errors.GNUTLSError("error getting peer"
1645
if list_size.value == 0:
1648
return ctypes.string_at(cert.data, cert.size)
1651
def fingerprint(openpgp):
1652
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1653
# New GnuTLS "datum" with the OpenPGP public key
1654
datum = (gnutls.library.types
1655
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1658
ctypes.c_uint(len(openpgp))))
1659
# New empty GnuTLS certificate
1660
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1661
(gnutls.library.functions
1662
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1663
# Import the OpenPGP public key into the certificate
1664
(gnutls.library.functions
1665
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1666
gnutls.library.constants
1667
.GNUTLS_OPENPGP_FMT_RAW))
1668
# Verify the self signature in the key
1669
crtverify = ctypes.c_uint()
1670
(gnutls.library.functions
1671
.gnutls_openpgp_crt_verify_self(crt, 0,
1672
ctypes.byref(crtverify)))
1673
if crtverify.value != 0:
1674
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1675
raise (gnutls.errors.CertificateSecurityError
1677
# New buffer for the fingerprint
1678
buf = ctypes.create_string_buffer(20)
1679
buf_len = ctypes.c_size_t()
1680
# Get the fingerprint from the certificate into the buffer
1681
(gnutls.library.functions
1682
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1683
ctypes.byref(buf_len)))
1684
# Deinit the certificate
1685
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1686
# Convert the buffer to a Python bytestring
1687
fpr = ctypes.string_at(buf, buf_len.value)
1688
# Convert the bytestring to hexadecimal notation
1689
hex_fpr = binascii.hexlify(fpr).upper()
1693
class MultiprocessingMixIn(object):
1694
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1695
def sub_process_main(self, request, address):
1697
self.finish_request(request, address)
1699
self.handle_error(request, address)
1700
self.close_request(request)
1702
def process_request(self, request, address):
1703
"""Start a new process to process the request."""
1704
proc = multiprocessing.Process(target = self.sub_process_main,
1711
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1712
""" adds a pipe to the MixIn """
1713
def process_request(self, request, client_address):
1714
"""Overrides and wraps the original process_request().
1716
This function creates a new pipe in self.pipe
1718
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1720
proc = MultiprocessingMixIn.process_request(self, request,
1722
self.child_pipe.close()
1723
self.add_pipe(parent_pipe, proc)
1725
def add_pipe(self, parent_pipe, proc):
1726
"""Dummy function; override as necessary"""
1727
raise NotImplementedError
1730
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1731
socketserver.TCPServer, object):
1732
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
448
logger.info(u"TCP connection from: %s",
449
unicode(self.client_address))
450
session = gnutls.connection.ClientSession\
451
(self.request, gnutls.connection.X509Credentials())
453
line = self.request.makefile().readline()
454
logger.debug(u"Protocol version: %r", line)
456
if int(line.strip().split()[0]) > 1:
458
except (ValueError, IndexError, RuntimeError), error:
459
logger.error(u"Unknown protocol version: %s", error)
462
# Note: gnutls.connection.X509Credentials is really a generic
463
# GnuTLS certificate credentials object so long as no X.509
464
# keys are added to it. Therefore, we can use it here despite
465
# using OpenPGP certificates.
467
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
468
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
470
priority = "NORMAL" # Fallback default, since this
472
if self.server.settings["priority"]:
473
priority = self.server.settings["priority"]
474
gnutls.library.functions.gnutls_priority_set_direct\
475
(session._c_object, priority, None)
479
except gnutls.errors.GNUTLSError, error:
480
logger.warning(u"Handshake failed: %s", error)
481
# Do not run session.bye() here: the session is not
482
# established. Just abandon the request.
485
fpr = fingerprint(peer_certificate(session))
486
except (TypeError, gnutls.errors.GNUTLSError), error:
487
logger.warning(u"Bad certificate: %s", error)
490
logger.debug(u"Fingerprint: %s", fpr)
492
for c in self.server.clients:
493
if c.fingerprint == fpr:
497
logger.warning(u"Client not found for fingerprint: %s",
501
# Have to check if client.still_valid(), since it is possible
502
# that the client timed out while establishing the GnuTLS
504
if not client.still_valid():
505
logger.warning(u"Client %(name)s is invalid",
510
while sent_size < len(client.secret):
511
sent = session.send(client.secret[sent_size:])
512
logger.debug(u"Sent: %d, remaining: %d",
513
sent, len(client.secret)
514
- (sent_size + sent))
519
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
520
"""IPv6 TCP server. Accepts 'None' as address and/or port.
522
settings: Server settings
523
clients: Set() of Client objects
1735
524
enabled: Boolean; whether this server is activated yet
1736
interface: None or a network interface name (string)
1737
use_ipv6: Boolean; to use IPv6 or not
1739
def __init__(self, server_address, RequestHandlerClass,
1740
interface=None, use_ipv6=True):
1741
self.interface = interface
1743
self.address_family = socket.AF_INET6
1744
socketserver.TCPServer.__init__(self, server_address,
1745
RequestHandlerClass)
526
address_family = socket.AF_INET6
527
def __init__(self, *args, **kwargs):
528
if "settings" in kwargs:
529
self.settings = kwargs["settings"]
530
del kwargs["settings"]
531
if "clients" in kwargs:
532
self.clients = kwargs["clients"]
533
del kwargs["clients"]
535
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
1746
536
def server_bind(self):
1747
537
"""This overrides the normal server_bind() function
1748
538
to bind to an interface if one was specified, and also NOT to
1749
539
bind to an address or port if they were not specified."""
1750
if self.interface is not None:
1751
if SO_BINDTODEVICE is None:
1752
logger.error("SO_BINDTODEVICE does not exist;"
1753
" cannot bind to interface %s",
1757
self.socket.setsockopt(socket.SOL_SOCKET,
1761
except socket.error as error:
1762
if error[0] == errno.EPERM:
1763
logger.error("No permission to"
1764
" bind to interface %s",
1766
elif error[0] == errno.ENOPROTOOPT:
1767
logger.error("SO_BINDTODEVICE not available;"
1768
" cannot bind to interface %s",
540
if self.settings["interface"]:
541
# 25 is from /usr/include/asm-i486/socket.h
542
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
544
self.socket.setsockopt(socket.SOL_SOCKET,
546
self.settings["interface"])
547
except socket.error, error:
548
if error[0] == errno.EPERM:
549
logger.error(u"No permission to"
550
u" bind to interface %s",
551
self.settings["interface"])
1772
554
# Only bind(2) the socket if we really need to.
1773
555
if self.server_address[0] or self.server_address[1]:
1774
556
if not self.server_address[0]:
1775
if self.address_family == socket.AF_INET6:
1776
any_address = "::" # in6addr_any
1778
any_address = socket.INADDR_ANY
1779
self.server_address = (any_address,
558
self.server_address = (in6addr_any,
1780
559
self.server_address[1])
1781
560
elif not self.server_address[1]:
1782
561
self.server_address = (self.server_address[0],
1784
# if self.interface:
563
# if self.settings["interface"]:
1785
564
# self.server_address = (self.server_address[0],
1788
567
# if_nametoindex
1790
return socketserver.TCPServer.server_bind(self)
1793
class MandosServer(IPv6_TCPServer):
1797
clients: set of Client objects
1798
gnutls_priority GnuTLS priority string
1799
use_dbus: Boolean; to emit D-Bus signals or not
1801
Assumes a gobject.MainLoop event loop.
1803
def __init__(self, server_address, RequestHandlerClass,
1804
interface=None, use_ipv6=True, clients=None,
1805
gnutls_priority=None, use_dbus=True):
1806
self.enabled = False
1807
self.clients = clients
1808
if self.clients is None:
1810
self.use_dbus = use_dbus
1811
self.gnutls_priority = gnutls_priority
1812
IPv6_TCPServer.__init__(self, server_address,
1813
RequestHandlerClass,
1814
interface = interface,
1815
use_ipv6 = use_ipv6)
570
return super(IPv6_TCPServer, self).server_bind()
1816
571
def server_activate(self):
1817
572
if self.enabled:
1818
return socketserver.TCPServer.server_activate(self)
573
return super(IPv6_TCPServer, self).server_activate()
1820
574
def enable(self):
1821
575
self.enabled = True
1823
def add_pipe(self, parent_pipe, proc):
1824
# Call "handle_ipc" for both data and EOF events
1825
gobject.io_add_watch(parent_pipe.fileno(),
1826
gobject.IO_IN | gobject.IO_HUP,
1827
functools.partial(self.handle_ipc,
1832
def handle_ipc(self, source, condition, parent_pipe=None,
1833
proc = None, client_object=None):
1835
gobject.IO_IN: "IN", # There is data to read.
1836
gobject.IO_OUT: "OUT", # Data can be written (without
1838
gobject.IO_PRI: "PRI", # There is urgent data to read.
1839
gobject.IO_ERR: "ERR", # Error condition.
1840
gobject.IO_HUP: "HUP" # Hung up (the connection has been
1841
# broken, usually for pipes and
1844
conditions_string = ' | '.join(name
1846
condition_names.iteritems()
1847
if cond & condition)
1848
# error, or the other end of multiprocessing.Pipe has closed
1849
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1850
# Wait for other process to exit
1854
# Read a request from the child
1855
request = parent_pipe.recv()
1856
command = request[0]
1858
if command == 'init':
1860
address = request[2]
1862
for c in self.clients.itervalues():
1863
if c.fingerprint == fpr:
1867
logger.info("Client not found for fingerprint: %s, ad"
1868
"dress: %s", fpr, address)
1871
mandos_dbus_service.ClientNotFound(fpr,
1873
parent_pipe.send(False)
1876
gobject.io_add_watch(parent_pipe.fileno(),
1877
gobject.IO_IN | gobject.IO_HUP,
1878
functools.partial(self.handle_ipc,
1884
parent_pipe.send(True)
1885
# remove the old hook in favor of the new above hook on
1888
if command == 'funcall':
1889
funcname = request[1]
1893
parent_pipe.send(('data', getattr(client_object,
1897
if command == 'getattr':
1898
attrname = request[1]
1899
if callable(client_object.__getattribute__(attrname)):
1900
parent_pipe.send(('function',))
1902
parent_pipe.send(('data', client_object
1903
.__getattribute__(attrname)))
1905
if command == 'setattr':
1906
attrname = request[1]
1908
setattr(client_object, attrname, value)
1913
578
def string_to_delta(interval):
1914
579
"""Parse a string and return a datetime.timedelta
1916
581
>>> string_to_delta('7d')
1917
582
datetime.timedelta(7)
1918
583
>>> string_to_delta('60s')
2031
720
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2032
721
"servicename": "Mandos",
2037
"statedir": "/var/lib/mandos"
2040
724
# Parse config file for server-global settings
2041
server_config = configparser.SafeConfigParser(server_defaults)
725
server_config = ConfigParser.SafeConfigParser(server_defaults)
2042
726
del server_defaults
2043
server_config.read(os.path.join(options.configdir,
727
server_config.read(os.path.join(options.configdir, "mandos.conf"))
2045
728
# Convert the SafeConfigParser object to a dict
2046
729
server_settings = server_config.defaults()
2047
# Use the appropriate methods on the non-string config options
2048
for option in ("debug", "use_dbus", "use_ipv6"):
2049
server_settings[option] = server_config.getboolean("DEFAULT",
2051
if server_settings["port"]:
2052
server_settings["port"] = server_config.getint("DEFAULT",
730
# Use getboolean on the boolean config option
731
server_settings["debug"] = server_config.getboolean\
2054
733
del server_config
2056
735
# Override the settings from the config file with command line
2057
736
# options, if set.
2058
737
for option in ("interface", "address", "port", "debug",
2059
"priority", "servicename", "configdir",
2060
"use_dbus", "use_ipv6", "debuglevel", "restore",
738
"priority", "servicename", "configdir"):
2062
739
value = getattr(options, option)
2063
740
if value is not None:
2064
741
server_settings[option] = value
2066
# Force all strings to be unicode
2067
for option in server_settings.keys():
2068
if type(server_settings[option]) is str:
2069
server_settings[option] = unicode(server_settings[option])
2070
743
# Now we have our good server settings in "server_settings"
2072
##################################################################
2075
745
debug = server_settings["debug"]
2076
debuglevel = server_settings["debuglevel"]
2077
use_dbus = server_settings["use_dbus"]
2078
use_ipv6 = server_settings["use_ipv6"]
2079
stored_state_path = os.path.join(server_settings["statedir"],
2083
initlogger(debug, logging.DEBUG)
2088
level = getattr(logging, debuglevel.upper())
2089
initlogger(debug, level)
748
syslogger.setLevel(logging.WARNING)
749
console.setLevel(logging.WARNING)
2091
751
if server_settings["servicename"] != "Mandos":
2092
syslogger.setFormatter(logging.Formatter
2093
('Mandos (%s) [%%(process)d]:'
2094
' %%(levelname)s: %%(message)s'
752
syslogger.setFormatter(logging.Formatter\
753
('Mandos (%s): %%(levelname)s:'
2095
755
% server_settings["servicename"]))
2097
757
# Parse config file with clients
2098
client_config = configparser.SafeConfigParser(Client.client_defaults)
758
client_defaults = { "timeout": "1h",
760
"checker": "fping -q -- %(host)s",
763
client_config = ConfigParser.SafeConfigParser(client_defaults)
2099
764
client_config.read(os.path.join(server_settings["configdir"],
2100
765
"clients.conf"))
2102
global mandos_dbus_service
2103
mandos_dbus_service = None
2105
tcp_server = MandosServer((server_settings["address"],
2106
server_settings["port"]),
2108
interface=(server_settings["interface"]
2112
server_settings["priority"],
2115
pidfilename = "/var/run/mandos.pid"
2117
pidfile = open(pidfilename, "w")
2119
logger.error("Could not open file %r", pidfilename)
2122
uid = pwd.getpwnam("_mandos").pw_uid
2123
gid = pwd.getpwnam("_mandos").pw_gid
2126
uid = pwd.getpwnam("mandos").pw_uid
2127
gid = pwd.getpwnam("mandos").pw_gid
2130
uid = pwd.getpwnam("nobody").pw_uid
2131
gid = pwd.getpwnam("nobody").pw_gid
768
tcp_server = IPv6_TCPServer((server_settings["address"],
769
server_settings["port"]),
771
settings=server_settings,
773
pidfilename = "/var/run/mandos.pid"
775
pidfile = open(pidfilename, "w")
776
except IOError, error:
777
logger.error("Could not open file %r", pidfilename)
782
uid = pwd.getpwnam("mandos").pw_uid
785
uid = pwd.getpwnam("nobody").pw_uid
789
gid = pwd.getpwnam("mandos").pw_gid
792
gid = pwd.getpwnam("nogroup").pw_gid
2138
except OSError as error:
798
except OSError, error:
2139
799
if error[0] != errno.EPERM:
2143
# Enable all possible GnuTLS debugging
2145
# "Use a log level over 10 to enable all debugging options."
2147
gnutls.library.functions.gnutls_global_set_log_level(11)
2149
@gnutls.library.types.gnutls_log_func
2150
def debug_gnutls(level, string):
2151
logger.debug("GnuTLS: %s", string[:-1])
2153
(gnutls.library.functions
2154
.gnutls_global_set_log_function(debug_gnutls))
2156
# Redirect stdin so all checkers get /dev/null
2157
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2158
os.dup2(null, sys.stdin.fileno())
2162
# Need to fork before connecting to D-Bus
2164
# Close all input and output, do double fork, etc.
2167
gobject.threads_init()
803
service = AvahiService(name = server_settings["servicename"],
804
servicetype = "_mandos._tcp", )
805
if server_settings["interface"]:
806
service.interface = if_nametoindex\
807
(server_settings["interface"])
2169
809
global main_loop
2170
812
# From the Avahi example code
2171
813
DBusGMainLoop(set_as_default=True )
2172
814
main_loop = gobject.MainLoop()
2173
815
bus = dbus.SystemBus()
816
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
817
avahi.DBUS_PATH_SERVER),
818
avahi.DBUS_INTERFACE_SERVER)
2174
819
# End of Avahi example code
2177
bus_name = dbus.service.BusName("se.recompile.Mandos",
2178
bus, do_not_queue=True)
2179
old_bus_name = (dbus.service.BusName
2180
("se.bsnet.fukt.Mandos", bus,
2182
except dbus.exceptions.NameExistsException as e:
2183
logger.error(unicode(e) + ", disabling D-Bus")
2185
server_settings["use_dbus"] = False
2186
tcp_server.use_dbus = False
2187
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2188
service = AvahiServiceToSyslog(name =
2189
server_settings["servicename"],
2190
servicetype = "_mandos._tcp",
2191
protocol = protocol, bus = bus)
2192
if server_settings["interface"]:
2193
service.interface = (if_nametoindex
2194
(str(server_settings["interface"])))
2196
global multiprocessing_manager
2197
multiprocessing_manager = multiprocessing.Manager()
2199
client_class = Client
2201
client_class = functools.partial(ClientDBusTransitional,
2204
client_settings = Client.config_parser(client_config)
2205
old_client_settings = {}
2208
# Get client data and settings from last running state.
2209
if server_settings["restore"]:
2211
with open(stored_state_path, "rb") as stored_state:
2212
clients_data, old_client_settings = (pickle.load
2214
os.remove(stored_state_path)
2215
except IOError as e:
2216
logger.warning("Could not load persistent state: {0}"
2218
if e.errno != errno.ENOENT:
2220
except EOFError as e:
2221
logger.warning("Could not load persistent state: "
2222
"EOFError: {0}".format(e))
2224
with PGPEngine() as pgp:
2225
for client_name, client in clients_data.iteritems():
2226
# Decide which value to use after restoring saved state.
2227
# We have three different values: Old config file,
2228
# new config file, and saved state.
2229
# New config value takes precedence if it differs from old
2230
# config value, otherwise use saved state.
2231
for name, value in client_settings[client_name].items():
2233
# For each value in new config, check if it
2234
# differs from the old config value (Except for
2235
# the "secret" attribute)
2236
if (name != "secret" and
2237
value != old_client_settings[client_name]
2239
client[name] = value
2243
# Clients who has passed its expire date can still be
2244
# enabled if its last checker was successful. Clients
2245
# whose checker failed before we stored its state is
2246
# assumed to have failed all checkers during downtime.
2247
if client["enabled"]:
2248
if datetime.datetime.utcnow() >= client["expires"]:
2249
if not client["last_checked_ok"]:
2251
"disabling client {0} - Client never "
2252
"performed a successfull checker"
2253
.format(client["name"]))
2254
client["enabled"] = False
2255
elif client["last_checker_status"] != 0:
2257
"disabling client {0} - Client "
2258
"last checker failed with error code {1}"
2259
.format(client["name"],
2260
client["last_checker_status"]))
2261
client["enabled"] = False
2263
client["expires"] = (datetime.datetime
2265
+ client["timeout"])
2268
client["secret"] = (
2269
pgp.decrypt(client["encrypted_secret"],
2270
client_settings[client_name]
2273
# If decryption fails, we use secret from new settings
2274
logger.debug("Failed to decrypt {0} old secret"
2275
.format(client_name))
2276
client["secret"] = (
2277
client_settings[client_name]["secret"])
2280
# Add/remove clients based on new changes made to config
2281
for client_name in set(old_client_settings) - set(client_settings):
2282
del clients_data[client_name]
2283
for client_name in set(client_settings) - set(old_client_settings):
2284
clients_data[client_name] = client_settings[client_name]
2286
# Create clients all clients
2287
for client_name, client in clients_data.iteritems():
2288
tcp_server.clients[client_name] = client_class(
2289
name = client_name, settings = client)
2291
if not tcp_server.clients:
2292
logger.warning("No clients defined")
821
def remove_from_clients(client):
822
clients.remove(client)
824
logger.critical(u"No clients left, exiting")
827
clients.update(Set(Client(name = section,
828
stop_hook = remove_from_clients,
830
= dict(client_config.items(section)))
831
for section in client_config.sections()))
833
logger.critical(u"No clients defined")
837
# Redirect stdin so all checkers get /dev/null
838
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
839
os.dup2(null, sys.stdin.fileno())
844
logger.removeHandler(console)
845
# Close all input and output, do double fork, etc.
850
pidfile.write(str(pid) + "\n")
854
logger.error(u"Could not write to file %r with PID %d",
857
# "pidfile" was never created
862
"Cleanup function; run on exit"
864
# From the Avahi example code
865
if not group is None:
868
# End of Avahi example code
871
client = clients.pop()
872
client.stop_hook = None
875
atexit.register(cleanup)
2298
pidfile.write(str(pid) + "\n".encode("utf-8"))
2301
logger.error("Could not write to file %r with PID %d",
2304
# "pidfile" was never created
2307
878
signal.signal(signal.SIGINT, signal.SIG_IGN)
2309
879
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2310
880
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2313
class MandosDBusService(dbus.service.Object):
2314
"""A D-Bus proxy object"""
2316
dbus.service.Object.__init__(self, bus, "/")
2317
_interface = "se.recompile.Mandos"
2319
@dbus.service.signal(_interface, signature="o")
2320
def ClientAdded(self, objpath):
2324
@dbus.service.signal(_interface, signature="ss")
2325
def ClientNotFound(self, fingerprint, address):
2329
@dbus.service.signal(_interface, signature="os")
2330
def ClientRemoved(self, objpath, name):
2334
@dbus.service.method(_interface, out_signature="ao")
2335
def GetAllClients(self):
2337
return dbus.Array(c.dbus_object_path
2339
tcp_server.clients.itervalues())
2341
@dbus.service.method(_interface,
2342
out_signature="a{oa{sv}}")
2343
def GetAllClientsWithProperties(self):
2345
return dbus.Dictionary(
2346
((c.dbus_object_path, c.GetAll(""))
2347
for c in tcp_server.clients.itervalues()),
2350
@dbus.service.method(_interface, in_signature="o")
2351
def RemoveClient(self, object_path):
2353
for c in tcp_server.clients.itervalues():
2354
if c.dbus_object_path == object_path:
2355
del tcp_server.clients[c.name]
2356
c.remove_from_connection()
2357
# Don't signal anything except ClientRemoved
2358
c.disable(quiet=True)
2360
self.ClientRemoved(object_path, c.name)
2362
raise KeyError(object_path)
2366
class MandosDBusServiceTransitional(MandosDBusService):
2367
__metaclass__ = AlternateDBusNamesMetaclass
2368
mandos_dbus_service = MandosDBusServiceTransitional()
2371
"Cleanup function; run on exit"
2374
multiprocessing.active_children()
2375
if not (tcp_server.clients or client_settings):
2378
# Store client before exiting. Secrets are encrypted with key
2379
# based on what config file has. If config file is
2380
# removed/edited, old secret will thus be unrecovable.
2382
with PGPEngine() as pgp:
2383
for client in tcp_server.clients.itervalues():
2384
key = client_settings[client.name]["secret"]
2385
client.encrypted_secret = pgp.encrypt(client.secret,
2389
# A list of attributes that can not be pickled
2391
exclude = set(("bus", "changedstate", "secret",
2393
for name, typ in (inspect.getmembers
2394
(dbus.service.Object)):
2397
client_dict["encrypted_secret"] = (client
2399
for attr in client.client_structure:
2400
if attr not in exclude:
2401
client_dict[attr] = getattr(client, attr)
2403
clients[client.name] = client_dict
2404
del client_settings[client.name]["secret"]
2407
tempfd, tempname = tempfile.mkstemp(suffix=".pickle",
2410
(stored_state_path))
2411
with os.fdopen(tempfd, "wb") as stored_state:
2412
pickle.dump((clients, client_settings), stored_state)
2413
os.rename(tempname, stored_state_path)
2414
except (IOError, OSError) as e:
2415
logger.warning("Could not save persistent state: {0}"
2422
if e.errno not in set((errno.ENOENT, errno.EACCES,
2426
# Delete all clients, and settings from config
2427
while tcp_server.clients:
2428
name, client = tcp_server.clients.popitem()
2430
client.remove_from_connection()
2431
# Don't signal anything except ClientRemoved
2432
client.disable(quiet=True)
2435
mandos_dbus_service.ClientRemoved(client
2438
client_settings.clear()
2440
atexit.register(cleanup)
2442
for client in tcp_server.clients.itervalues():
2445
mandos_dbus_service.ClientAdded(client.dbus_object_path)
2446
# Need to initiate checking of clients
2448
client.init_checker()
882
for client in clients:
2450
885
tcp_server.enable()
2451
886
tcp_server.server_activate()
2453
888
# Find out what port we got
2454
889
service.port = tcp_server.socket.getsockname()[1]
2456
logger.info("Now listening on address %r, port %d,"
2457
" flowinfo %d, scope_id %d"
2458
% tcp_server.socket.getsockname())
2460
logger.info("Now listening on address %r, port %d"
2461
% tcp_server.socket.getsockname())
890
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
891
u" scope_id %d" % tcp_server.socket.getsockname())
2463
893
#service.interface = tcp_server.socket.getsockname()[3]
2466
896
# From the Avahi example code
897
server.connect_to_signal("StateChanged", server_state_changed)
2469
except dbus.exceptions.DBusException as error:
2470
logger.critical("DBusException: %s", error)
899
server_state_changed(server.GetState())
900
except dbus.exceptions.DBusException, error:
901
logger.critical(u"DBusException: %s", error)
2473
903
# End of Avahi example code
2475
905
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
2476
906
lambda *args, **kwargs:
2477
(tcp_server.handle_request
2478
(*args[2:], **kwargs) or True))
907
tcp_server.handle_request\
908
(*args[2:], **kwargs) or True)
2480
logger.debug("Starting main loop")
910
logger.debug(u"Starting main loop")
2482
except AvahiError as error:
2483
logger.critical("AvahiError: %s", error)
912
except AvahiError, error:
913
logger.critical(u"AvahiError: %s" + unicode(error))
2486
915
except KeyboardInterrupt:
2488
print("", file=sys.stderr)
2489
logger.debug("Server received KeyboardInterrupt")
2490
logger.debug("Server exiting")
2491
# Must run before the D-Bus bus name gets deregistered
2494
919
if __name__ == '__main__':