239
106
max_renames: integer; maximum number of renames
240
107
rename_count: integer; counter so we only rename after collisions
241
108
a sensible number of times
242
group: D-Bus Entry Group
244
bus: dbus.SystemBus()
246
110
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):
111
type = None, port = None, TXT = None, domain = "",
112
host = "", max_renames = 32768):
250
113
self.interface = interface
252
self.type = servicetype
254
self.TXT = TXT if TXT is not None else []
255
121
self.domain = domain
257
123
self.rename_count = 0
258
124
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
125
def rename(self):
265
126
"""Derived from the Avahi example code"""
266
127
if self.rename_count >= self.max_renames:
267
logger.critical("No suitable Zeroconf service name found"
268
" after %i retries, exiting.",
128
logger.critical(u"No suitable Zeroconf service name found"
129
u" after %i retries, exiting.",
270
131
raise AvahiServiceError("Too many renames")
271
self.name = unicode(self.server
272
.GetAlternativeServiceName(self.name))
273
logger.info("Changing Zeroconf service name to %r ...",
132
self.name = server.GetAlternativeServiceName(self.name)
133
logger.info(u"Changing Zeroconf service name to %r ...",
135
syslogger.setFormatter(logging.Formatter\
136
('Mandos (%s): %%(levelname)s:'
137
' %%(message)s' % self.name))
278
except dbus.exceptions.DBusException as error:
279
logger.critical("DBusException: %s", error)
282
140
self.rename_count += 1
283
141
def remove(self):
284
142
"""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:
143
if group is not None:
291
146
"""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))
149
group = dbus.Interface\
150
(bus.get_object(avahi.DBUS_NAME,
151
server.EntryGroupNew()),
152
avahi.DBUS_INTERFACE_ENTRY_GROUP)
153
group.connect_to_signal('StateChanged',
154
entry_group_state_changed)
155
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
156
service.name, service.type)
158
self.interface, # interface
159
avahi.PROTO_INET6, # protocol
160
dbus.UInt32(0), # flags
161
self.name, self.type,
162
self.domain, self.host,
163
dbus.UInt16(self.port),
164
avahi.string_array_to_txt_array(self.TXT))
167
# From the Avahi example code:
168
group = None # our entry group
169
# End of Avahi example code
388
172
class Client(object):
389
173
"""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
175
name: string; from the config file, used in log messages
176
fingerprint: string (40 or 32 hexadecimal digits); used to
177
uniquely identify the client
178
secret: bytestring; sent verbatim (over TLS) to client
179
host: string; available for use by the checker command
180
created: datetime.datetime(); object creation, not client host
181
last_checked_ok: datetime.datetime() or None if not yet checked OK
182
timeout: datetime.timedelta(); How long from last_checked_ok
183
until this client is invalid
184
interval: datetime.timedelta(); How often to start a new checker
185
stop_hook: If set, called by stop() as stop_hook(self)
186
checker: subprocess.Popen(); a running checker process used
187
to see if the client lives.
188
'None' if no process is running.
189
checker_initiator_tag: a gobject event source tag, or None
190
stop_initiator_tag: - '' -
191
checker_callback_tag: - '' -
192
checker_command: string; External command which is run to check if
193
client lives. %() expansions are done at
401
194
runtime with vars(self) as dict, so that for
402
195
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
197
_timeout: Real variable for 'timeout'
198
_interval: Real variable for 'interval'
199
_timeout_milliseconds: Used when calling gobject.timeout_add()
200
_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):
202
def _set_timeout(self, timeout):
203
"Setter function for 'timeout' attribute"
204
self._timeout = timeout
205
self._timeout_milliseconds = ((self.timeout.days
206
* 24 * 60 * 60 * 1000)
207
+ (self.timeout.seconds * 1000)
208
+ (self.timeout.microseconds
210
timeout = property(lambda self: self._timeout,
213
def _set_interval(self, interval):
214
"Setter function for 'interval' attribute"
215
self._interval = interval
216
self._interval_milliseconds = ((self.interval.days
217
* 24 * 60 * 60 * 1000)
218
+ (self.interval.seconds
220
+ (self.interval.microseconds
222
interval = property(lambda self: self._interval,
225
def __init__(self, name = None, stop_hook=None, config={}):
513
226
"""Note: the 'checker' key in 'config' sets the
514
227
'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)
230
logger.debug(u"Creating client %r", self.name)
522
231
# Uppercase and remove spaces from fingerprint for later
523
232
# 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
234
self.fingerprint = config["fingerprint"].upper()\
236
logger.debug(u" Fingerprint: %s", self.fingerprint)
237
if "secret" in config:
238
self.secret = config["secret"].decode(u"base64")
239
elif "secfile" in config:
240
sf = open(config["secfile"])
241
self.secret = sf.read()
244
raise TypeError(u"No secret or secfile for client %s"
246
self.host = config.get("host", "")
247
self.created = datetime.datetime.now()
248
self.last_checked_ok = None
249
self.timeout = string_to_delta(config["timeout"])
250
self.interval = string_to_delta(config["interval"])
251
self.stop_hook = stop_hook
529
252
self.checker = None
530
253
self.checker_initiator_tag = None
531
self.disable_initiator_tag = None
254
self.stop_initiator_tag = None
532
255
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()
256
self.check_command = config["checker"]
557
258
"""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):
259
# Schedule a new checker to be started an 'interval' from now,
260
# and every interval from then on.
261
self.checker_initiator_tag = gobject.timeout_add\
262
(self._interval_milliseconds,
264
# Also start a new checker *right now*.
266
# Schedule a stop() when 'timeout' has passed
267
self.stop_initiator_tag = gobject.timeout_add\
268
(self._timeout_milliseconds,
272
The possibility that a client might be restarted is left open,
273
but not currently used."""
274
# If this client doesn't have a secret, it is already stopped.
275
if hasattr(self, "secret") and self.secret:
276
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
280
if getattr(self, "stop_initiator_tag", False):
281
gobject.source_remove(self.stop_initiator_tag)
282
self.stop_initiator_tag = None
579
283
if getattr(self, "checker_initiator_tag", False):
580
284
gobject.source_remove(self.checker_initiator_tag)
581
285
self.checker_initiator_tag = None
582
286
self.stop_checker()
584
289
# Do not run this again if called by a gobject.timeout_add
587
291
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):
292
self.stop_hook = None
294
def checker_callback(self, pid, condition):
604
295
"""The checker has completed, so take appropriate actions."""
296
now = datetime.datetime.now()
605
297
self.checker_callback_tag = None
606
298
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?",
299
if os.WIFEXITED(condition) \
300
and (os.WEXITSTATUS(condition) == 0):
301
logger.info(u"Checker for %(name)s succeeded",
303
self.last_checked_ok = now
304
gobject.source_remove(self.stop_initiator_tag)
305
self.stop_initiator_tag = gobject.timeout_add\
306
(self._timeout_milliseconds,
308
elif not os.WIFEXITED(condition):
309
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()
312
logger.info(u"Checker for %(name)s failed",
641
314
def start_checker(self):
642
315
"""Start a new checker subprocess if one is not running.
644
316
If a checker already exists, leave it running and do
646
318
# The reason for not killing a running checker is that if we
720
363
self.checker_callback_tag = None
721
364
if getattr(self, "checker", None) is None:
723
logger.debug("Stopping checker for %(name)s", vars(self))
366
logger.debug(u"Stopping checker for %(name)s", vars(self))
725
368
os.kill(self.checker.pid, signal.SIGTERM)
727
370
#if self.checker.poll() is None:
728
371
# os.kill(self.checker.pid, signal.SIGKILL)
729
except OSError as error:
372
except OSError, error:
730
373
if error.errno != errno.ESRCH: # No such process
732
375
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.
376
def still_valid(self):
377
"""Has the timeout not yet passed for this client?"""
378
now = datetime.datetime.now()
379
if self.last_checked_ok is None:
380
return now < (self.created + self.timeout)
382
return now < (self.last_checked_ok + self.timeout)
385
def peer_certificate(session):
386
"Return the peer's OpenPGP certificate as a bytestring"
387
# If not an OpenPGP certificate...
388
if gnutls.library.functions.gnutls_certificate_type_get\
389
(session._c_object) \
390
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
391
# ...do the normal thing
392
return session.peer_certificate
393
list_size = ctypes.c_uint()
394
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
395
(session._c_object, ctypes.byref(list_size))
396
if list_size.value == 0:
399
return ctypes.string_at(cert.data, cert.size)
402
def fingerprint(openpgp):
403
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
404
# New GnuTLS "datum" with the OpenPGP public key
405
datum = gnutls.library.types.gnutls_datum_t\
406
(ctypes.cast(ctypes.c_char_p(openpgp),
407
ctypes.POINTER(ctypes.c_ubyte)),
408
ctypes.c_uint(len(openpgp)))
409
# New empty GnuTLS certificate
410
crt = gnutls.library.types.gnutls_openpgp_crt_t()
411
gnutls.library.functions.gnutls_openpgp_crt_init\
413
# Import the OpenPGP public key into the certificate
414
gnutls.library.functions.gnutls_openpgp_crt_import\
415
(crt, ctypes.byref(datum),
416
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
417
# Verify the self signature in the key
418
crtverify = ctypes.c_uint();
419
gnutls.library.functions.gnutls_openpgp_crt_verify_self\
420
(crt, 0, ctypes.byref(crtverify))
421
if crtverify.value != 0:
422
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
423
raise gnutls.errors.CertificateSecurityError("Verify failed")
424
# New buffer for the fingerprint
425
buffer = ctypes.create_string_buffer(20)
426
buffer_length = ctypes.c_size_t()
427
# Get the fingerprint from the certificate into the buffer
428
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
429
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
430
# Deinit the certificate
431
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
432
# Convert the buffer to a Python bytestring
433
fpr = ctypes.string_at(buffer, buffer_length.value)
434
# Convert the bytestring to hexadecimal notation
435
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
439
class tcp_handler(SocketServer.BaseRequestHandler, object):
440
"""A TCP request handler class.
441
Instantiated by IPv6_TCPServer for each request to handle it.
1474
442
Note: This will run in its own forked process."""
1476
444
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
445
logger.info(u"TCP connection from: %s",
446
unicode(self.client_address))
447
session = gnutls.connection.ClientSession\
448
(self.request, gnutls.connection.X509Credentials())
450
line = self.request.makefile().readline()
451
logger.debug(u"Protocol version: %r", line)
453
if int(line.strip().split()[0]) > 1:
455
except (ValueError, IndexError, RuntimeError), error:
456
logger.error(u"Unknown protocol version: %s", error)
459
# Note: gnutls.connection.X509Credentials is really a generic
460
# GnuTLS certificate credentials object so long as no X.509
461
# keys are added to it. Therefore, we can use it here despite
462
# using OpenPGP certificates.
464
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
465
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
467
priority = "NORMAL" # Fallback default, since this
469
if self.server.settings["priority"]:
470
priority = self.server.settings["priority"]
471
gnutls.library.functions.gnutls_priority_set_direct\
472
(session._c_object, priority, None);
476
except gnutls.errors.GNUTLSError, error:
477
logger.warning(u"Handshake failed: %s", error)
478
# Do not run session.bye() here: the session is not
479
# established. Just abandon the request.
482
fpr = fingerprint(peer_certificate(session))
483
except (TypeError, gnutls.errors.GNUTLSError), error:
484
logger.warning(u"Bad certificate: %s", error)
487
logger.debug(u"Fingerprint: %s", fpr)
489
for c in self.server.clients:
490
if c.fingerprint == fpr:
494
logger.warning(u"Client not found for fingerprint: %s",
498
# Have to check if client.still_valid(), since it is possible
499
# that the client timed out while establishing the GnuTLS
501
if not client.still_valid():
502
logger.warning(u"Client %(name)s is invalid",
507
while sent_size < len(client.secret):
508
sent = session.send(client.secret[sent_size:])
509
logger.debug(u"Sent: %d, remaining: %d",
510
sent, len(client.secret)
511
- (sent_size + sent))
516
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
517
"""IPv6 TCP server. Accepts 'None' as address and/or port.
519
settings: Server settings
520
clients: Set() of Client objects
1735
521
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)
523
address_family = socket.AF_INET6
524
def __init__(self, *args, **kwargs):
525
if "settings" in kwargs:
526
self.settings = kwargs["settings"]
527
del kwargs["settings"]
528
if "clients" in kwargs:
529
self.clients = kwargs["clients"]
530
del kwargs["clients"]
532
return super(type(self), self).__init__(*args, **kwargs)
1746
533
def server_bind(self):
1747
534
"""This overrides the normal server_bind() function
1748
535
to bind to an interface if one was specified, and also NOT to
1749
536
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",
537
if self.settings["interface"]:
538
# 25 is from /usr/include/asm-i486/socket.h
539
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
541
self.socket.setsockopt(socket.SOL_SOCKET,
543
self.settings["interface"])
544
except socket.error, error:
545
if error[0] == errno.EPERM:
546
logger.error(u"No permission to"
547
u" bind to interface %s",
548
self.settings["interface"])
1772
551
# Only bind(2) the socket if we really need to.
1773
552
if self.server_address[0] or self.server_address[1]:
1774
553
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,
555
self.server_address = (in6addr_any,
1780
556
self.server_address[1])
1781
557
elif not self.server_address[1]:
1782
558
self.server_address = (self.server_address[0],
1784
# if self.interface:
560
# if self.settings["interface"]:
1785
561
# self.server_address = (self.server_address[0],
1788
564
# 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)
567
return super(type(self), self).server_bind()
1816
568
def server_activate(self):
1817
569
if self.enabled:
1818
return socketserver.TCPServer.server_activate(self)
570
return super(type(self), self).server_activate()
1820
571
def enable(self):
1821
572
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
575
def string_to_delta(interval):
1914
576
"""Parse a string and return a datetime.timedelta
1916
578
>>> string_to_delta('7d')
1917
579
datetime.timedelta(7)
1918
580
>>> string_to_delta('60s')
2031
722
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
2032
723
"servicename": "Mandos",
2037
"statedir": "/var/lib/mandos"
2040
726
# Parse config file for server-global settings
2041
server_config = configparser.SafeConfigParser(server_defaults)
727
server_config = ConfigParser.SafeConfigParser(server_defaults)
2042
728
del server_defaults
2043
server_config.read(os.path.join(options.configdir,
729
server_config.read(os.path.join(options.configdir, "mandos.conf"))
2045
730
# Convert the SafeConfigParser object to a dict
2046
731
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",
732
# Use getboolean on the boolean config option
733
server_settings["debug"] = server_config.getboolean\
2054
735
del server_config
2056
737
# Override the settings from the config file with command line
2057
738
# options, if set.
2058
739
for option in ("interface", "address", "port", "debug",
2059
"priority", "servicename", "configdir",
2060
"use_dbus", "use_ipv6", "debuglevel", "restore",
740
"priority", "servicename", "configdir"):
2062
741
value = getattr(options, option)
2063
742
if value is not None:
2064
743
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
745
# Now we have our good server settings in "server_settings"
2072
##################################################################
2075
747
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)
750
syslogger.setLevel(logging.WARNING)
751
console.setLevel(logging.WARNING)
2091
753
if server_settings["servicename"] != "Mandos":
2092
syslogger.setFormatter(logging.Formatter
2093
('Mandos (%s) [%%(process)d]:'
2094
' %%(levelname)s: %%(message)s'
754
syslogger.setFormatter(logging.Formatter\
755
('Mandos (%s): %%(levelname)s:'
2095
757
% server_settings["servicename"]))
2097
759
# Parse config file with clients
2098
client_config = configparser.SafeConfigParser(Client.client_defaults)
760
client_defaults = { "timeout": "1h",
762
"checker": "fping -q -- %(host)s",
765
client_config = ConfigParser.SafeConfigParser(client_defaults)
2099
766
client_config.read(os.path.join(server_settings["configdir"],
2100
767
"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
770
tcp_server = IPv6_TCPServer((server_settings["address"],
771
server_settings["port"]),
773
settings=server_settings,
775
pidfilename = "/var/run/mandos.pid"
777
pidfile = open(pidfilename, "w")
778
except IOError, error:
779
logger.error("Could not open file %r", pidfilename)
784
uid = pwd.getpwnam("mandos").pw_uid
787
uid = pwd.getpwnam("nobody").pw_uid
791
gid = pwd.getpwnam("mandos").pw_gid
794
gid = pwd.getpwnam("nogroup").pw_gid
2138
except OSError as error:
800
except OSError, error:
2139
801
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()
805
service = AvahiService(name = server_settings["servicename"],
806
type = "_mandos._tcp", );
807
if server_settings["interface"]:
808
service.interface = if_nametoindex\
809
(server_settings["interface"])
2169
811
global main_loop
2170
814
# From the Avahi example code
2171
815
DBusGMainLoop(set_as_default=True )
2172
816
main_loop = gobject.MainLoop()
2173
817
bus = dbus.SystemBus()
818
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
819
avahi.DBUS_PATH_SERVER),
820
avahi.DBUS_INTERFACE_SERVER)
2174
821
# 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")
823
def remove_from_clients(client):
824
clients.remove(client)
826
logger.critical(u"No clients left, exiting")
829
clients.update(Set(Client(name = section,
830
stop_hook = remove_from_clients,
832
= dict(client_config.items(section)))
833
for section in client_config.sections()))
835
logger.critical(u"No clients defined")
839
# Redirect stdin so all checkers get /dev/null
840
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
841
os.dup2(null, sys.stdin.fileno())
846
logger.removeHandler(console)
847
# Close all input and output, do double fork, etc.
852
pidfile.write(str(pid) + "\n")
856
logger.error(u"Could not write to file %r with PID %d",
859
# "pidfile" was never created
864
"Cleanup function; run on exit"
866
# From the Avahi example code
867
if not group is None:
870
# End of Avahi example code
873
client = clients.pop()
874
client.stop_hook = None
877
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
880
signal.signal(signal.SIGINT, signal.SIG_IGN)
2309
881
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2310
882
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()
884
for client in clients:
2450
887
tcp_server.enable()
2451
888
tcp_server.server_activate()
2453
890
# Find out what port we got
2454
891
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())
892
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
893
u" scope_id %d" % tcp_server.socket.getsockname())
2463
895
#service.interface = tcp_server.socket.getsockname()[3]
2466
898
# From the Avahi example code
899
server.connect_to_signal("StateChanged", server_state_changed)
2469
except dbus.exceptions.DBusException as error:
2470
logger.critical("DBusException: %s", error)
901
server_state_changed(server.GetState())
902
except dbus.exceptions.DBusException, error:
903
logger.critical(u"DBusException: %s", error)
2473
905
# End of Avahi example code
2475
907
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
2476
908
lambda *args, **kwargs:
2477
(tcp_server.handle_request
2478
(*args[2:], **kwargs) or True))
909
tcp_server.handle_request\
910
(*args[2:], **kwargs) or True)
2480
logger.debug("Starting main loop")
912
logger.debug(u"Starting main loop")
913
main_loop_started = True
2482
except AvahiError as error:
2483
logger.critical("AvahiError: %s", error)
915
except AvahiError, error:
916
logger.critical(u"AvahiError: %s" + unicode(error))
2486
918
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
922
if __name__ == '__main__':