104
239
max_renames: integer; maximum number of renames
105
240
rename_count: integer; counter so we only rename after collisions
106
241
a sensible number of times
242
group: D-Bus Entry Group
244
bus: dbus.SystemBus()
108
246
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
109
type = None, port = None, TXT = None, domain = "",
110
host = "", max_renames = 32768):
247
servicetype = None, port = None, TXT = None,
248
domain = "", host = "", max_renames = 32768,
249
protocol = avahi.PROTO_UNSPEC, bus = None):
111
250
self.interface = interface
252
self.type = servicetype
254
self.TXT = TXT if TXT is not None else []
119
255
self.domain = domain
121
257
self.rename_count = 0
122
258
self.max_renames = max_renames
259
self.protocol = protocol
260
self.group = None # our entry group
263
self.entry_group_state_changed_match = None
123
264
def rename(self):
124
265
"""Derived from the Avahi example code"""
125
266
if self.rename_count >= self.max_renames:
126
logger.critical(u"No suitable service name found after %i"
127
u" retries, exiting.", rename_count)
267
logger.critical("No suitable Zeroconf service name found"
268
" after %i retries, exiting.",
128
270
raise AvahiServiceError("Too many renames")
129
self.name = server.GetAlternativeServiceName(self.name)
130
logger.info(u"Changing name to %r ...", str(self.name))
131
syslogger.setFormatter(logging.Formatter\
132
('Mandos (%s): %%(levelname)s:'
133
' %%(message)s' % self.name))
271
self.name = unicode(self.server
272
.GetAlternativeServiceName(self.name))
273
logger.info("Changing Zeroconf service name to %r ...",
278
except dbus.exceptions.DBusException as error:
279
logger.critical("DBusException: %s", error)
136
282
self.rename_count += 1
137
283
def remove(self):
138
284
"""Derived from the Avahi example code"""
139
if group is not None:
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:
142
291
"""Derived from the Avahi example code"""
145
group = dbus.Interface\
146
(bus.get_object(avahi.DBUS_NAME,
147
server.EntryGroupNew()),
148
avahi.DBUS_INTERFACE_ENTRY_GROUP)
149
group.connect_to_signal('StateChanged',
150
entry_group_state_changed)
151
logger.debug(u"Adding service '%s' of type '%s' ...",
152
service.name, service.type)
154
self.interface, # interface
155
avahi.PROTO_INET6, # protocol
156
dbus.UInt32(0), # flags
157
self.name, self.type,
158
self.domain, self.host,
159
dbus.UInt16(self.port),
160
avahi.string_array_to_txt_array(self.TXT))
163
# From the Avahi example code:
164
group = None # our entry group
165
# End of 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))
168
388
class Client(object):
169
389
"""A representation of a client host served by this server.
171
name: string; from the config file, used in log messages
392
approved: bool(); 'None' if not yet approved/disapproved
393
approval_delay: datetime.timedelta(); Time to wait for approval
394
approval_duration: datetime.timedelta(); Duration of one approval
395
checker: subprocess.Popen(); a running checker process used
396
to see if the client lives.
397
'None' if no process is running.
398
checker_callback_tag: a gobject event source tag, or None
399
checker_command: string; External command which is run to check
400
if client lives. %() expansions are done at
401
runtime with vars(self) as dict, so that for
402
instance %(name)s can be used in the command.
403
checker_initiator_tag: a gobject event source tag, or None
404
created: datetime.datetime(); (UTC) object creation
405
client_structure: Object describing what attributes a client has
406
and is used for storing the client at exit
407
current_checker_command: string; current running checker_command
408
disable_initiator_tag: a gobject event source tag, or None
172
410
fingerprint: string (40 or 32 hexadecimal digits); used to
173
411
uniquely identify the client
174
secret: bytestring; sent verbatim (over TLS) to client
175
host: string; available for use by the checker command
176
created: datetime.datetime(); object creation, not client host
177
last_checked_ok: datetime.datetime() or None if not yet checked OK
178
timeout: datetime.timedelta(); How long from last_checked_ok
179
until this client is invalid
180
interval: datetime.timedelta(); How often to start a new checker
181
stop_hook: If set, called by stop() as stop_hook(self)
182
checker: subprocess.Popen(); a running checker process used
183
to see if the client lives.
184
'None' if no process is running.
185
checker_initiator_tag: a gobject event source tag, or None
186
stop_initiator_tag: - '' -
187
checker_callback_tag: - '' -
188
checker_command: string; External command which is run to check if
189
client lives. %() expansions are done at
190
runtime with vars(self) as dict, so that for
191
instance %(name)s can be used in the command.
193
_timeout: Real variable for 'timeout'
194
_interval: Real variable for 'interval'
195
_timeout_milliseconds: Used when calling gobject.timeout_add()
196
_interval_milliseconds: - '' -
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
def _set_timeout(self, timeout):
199
"Setter function for 'timeout' attribute"
200
self._timeout = timeout
201
self._timeout_milliseconds = ((self.timeout.days
202
* 24 * 60 * 60 * 1000)
203
+ (self.timeout.seconds * 1000)
204
+ (self.timeout.microseconds
206
timeout = property(lambda self: self._timeout,
209
def _set_interval(self, interval):
210
"Setter function for 'interval' attribute"
211
self._interval = interval
212
self._interval_milliseconds = ((self.interval.days
213
* 24 * 60 * 60 * 1000)
214
+ (self.interval.seconds
216
+ (self.interval.microseconds
218
interval = property(lambda self: self._interval,
221
def __init__(self, name = None, stop_hook=None, config={}):
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):
222
513
"""Note: the 'checker' key in 'config' sets the
223
514
'checker_command' attribute and *not* the 'checker'
226
logger.debug(u"Creating client %r", self.name)
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)
227
522
# Uppercase and remove spaces from fingerprint for later
228
523
# comparison purposes with return value from the fingerprint()
230
self.fingerprint = config["fingerprint"].upper()\
232
logger.debug(u" Fingerprint: %s", self.fingerprint)
233
if "secret" in config:
234
self.secret = config["secret"].decode(u"base64")
235
elif "secfile" in config:
236
sf = open(config["secfile"])
237
self.secret = sf.read()
240
raise TypeError(u"No secret or secfile for client %s"
242
self.host = config.get("host", "")
243
self.created = datetime.datetime.now()
244
self.last_checked_ok = None
245
self.timeout = string_to_delta(config["timeout"])
246
self.interval = string_to_delta(config["interval"])
247
self.stop_hook = stop_hook
525
logger.debug(" Fingerprint: %s", self.fingerprint)
526
self.created = settings.get("created", datetime.datetime.utcnow())
528
# attributes specific for this server instance
248
529
self.checker = None
249
530
self.checker_initiator_tag = None
250
self.stop_initiator_tag = None
531
self.disable_initiator_tag = None
251
532
self.checker_callback_tag = None
252
self.check_command = config["checker"]
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()
254
557
"""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):
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
579
if getattr(self, "checker_initiator_tag", False):
580
gobject.source_remove(self.checker_initiator_tag)
581
self.checker_initiator_tag = None
584
# Do not run this again if called by a gobject.timeout_add
590
def init_checker(self):
255
591
# Schedule a new checker to be started an 'interval' from now,
256
592
# and every interval from then on.
257
self.checker_initiator_tag = gobject.timeout_add\
258
(self._interval_milliseconds,
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(),
260
600
# Also start a new checker *right now*.
261
601
self.start_checker()
262
# Schedule a stop() when 'timeout' has passed
263
self.stop_initiator_tag = gobject.timeout_add\
264
(self._timeout_milliseconds,
268
The possibility that a client might be restarted is left open,
269
but not currently used."""
270
# If this client doesn't have a secret, it is already stopped.
271
if hasattr(self, "secret") and self.secret:
272
logger.info(u"Stopping client %s", self.name)
276
if getattr(self, "stop_initiator_tag", False):
277
gobject.source_remove(self.stop_initiator_tag)
278
self.stop_initiator_tag = None
279
if getattr(self, "checker_initiator_tag", False):
280
gobject.source_remove(self.checker_initiator_tag)
281
self.checker_initiator_tag = None
285
# Do not run this again if called by a gobject.timeout_add
288
self.stop_hook = None
290
def checker_callback(self, pid, condition):
603
def checker_callback(self, pid, condition, command):
291
604
"""The checker has completed, so take appropriate actions."""
292
now = datetime.datetime.now()
293
605
self.checker_callback_tag = None
294
606
self.checker = None
295
if os.WIFEXITED(condition) \
296
and (os.WEXITSTATUS(condition) == 0):
297
logger.info(u"Checker for %(name)s succeeded",
299
self.last_checked_ok = now
300
gobject.source_remove(self.stop_initiator_tag)
301
self.stop_initiator_tag = gobject.timeout_add\
302
(self._timeout_milliseconds,
304
elif not os.WIFEXITED(condition):
305
logger.warning(u"Checker for %(name)s crashed?",
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?",
308
logger.info(u"Checker for %(name)s failed",
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()
310
641
def start_checker(self):
311
642
"""Start a new checker subprocess if one is not running.
312
644
If a checker already exists, leave it running and do
314
646
# The reason for not killing a running checker is that if we
359
720
self.checker_callback_tag = None
360
721
if getattr(self, "checker", None) is None:
362
logger.debug(u"Stopping checker for %(name)s", vars(self))
723
logger.debug("Stopping checker for %(name)s", vars(self))
364
725
os.kill(self.checker.pid, signal.SIGTERM)
366
727
#if self.checker.poll() is None:
367
728
# os.kill(self.checker.pid, signal.SIGKILL)
368
except OSError, error:
729
except OSError as error:
369
730
if error.errno != errno.ESRCH: # No such process
371
732
self.checker = None
372
def still_valid(self):
373
"""Has the timeout not yet passed for this client?"""
374
now = datetime.datetime.now()
375
if self.last_checked_ok is None:
376
return now < (self.created + self.timeout)
378
return now < (self.last_checked_ok + self.timeout)
381
def peer_certificate(session):
382
"Return the peer's OpenPGP certificate as a bytestring"
383
# If not an OpenPGP certificate...
384
if gnutls.library.functions.gnutls_certificate_type_get\
385
(session._c_object) \
386
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
387
# ...do the normal thing
388
return session.peer_certificate
389
list_size = ctypes.c_uint()
390
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
391
(session._c_object, ctypes.byref(list_size))
392
if list_size.value == 0:
395
return ctypes.string_at(cert.data, cert.size)
398
def fingerprint(openpgp):
399
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
400
# New GnuTLS "datum" with the OpenPGP public key
401
datum = gnutls.library.types.gnutls_datum_t\
402
(ctypes.cast(ctypes.c_char_p(openpgp),
403
ctypes.POINTER(ctypes.c_ubyte)),
404
ctypes.c_uint(len(openpgp)))
405
# New empty GnuTLS certificate
406
crt = gnutls.library.types.gnutls_openpgp_crt_t()
407
gnutls.library.functions.gnutls_openpgp_crt_init\
409
# Import the OpenPGP public key into the certificate
410
gnutls.library.functions.gnutls_openpgp_crt_import\
411
(crt, ctypes.byref(datum),
412
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
413
# Verify the self signature in the key
414
crtverify = ctypes.c_uint();
415
gnutls.library.functions.gnutls_openpgp_crt_verify_self\
416
(crt, ctypes.c_uint(0), ctypes.byref(crtverify))
418
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
419
raise gnutls.errors.CertificateSecurityError("Verify failed")
420
# New buffer for the fingerprint
421
buffer = ctypes.create_string_buffer(20)
422
buffer_length = ctypes.c_size_t()
423
# Get the fingerprint from the certificate into the buffer
424
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
425
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
426
# Deinit the certificate
427
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
428
# Convert the buffer to a Python bytestring
429
fpr = ctypes.string_at(buffer, buffer_length.value)
430
# Convert the bytestring to hexadecimal notation
431
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
435
class tcp_handler(SocketServer.BaseRequestHandler, object):
436
"""A TCP request handler class.
437
Instantiated by IPv6_TCPServer for each request to handle it.
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.
438
1474
Note: This will run in its own forked process."""
440
1476
def handle(self):
441
logger.info(u"TCP connection from: %s",
442
unicode(self.client_address))
443
session = gnutls.connection.ClientSession\
444
(self.request, gnutls.connection.X509Credentials())
446
line = self.request.makefile().readline()
447
logger.debug(u"Protocol version: %r", line)
449
if int(line.strip().split()[0]) > 1:
451
except (ValueError, IndexError, RuntimeError), error:
452
logger.error(u"Unknown protocol version: %s", error)
455
# Note: gnutls.connection.X509Credentials is really a generic
456
# GnuTLS certificate credentials object so long as no X.509
457
# keys are added to it. Therefore, we can use it here despite
458
# using OpenPGP certificates.
460
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
461
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
463
priority = "NORMAL" # Fallback default, since this
465
if self.server.settings["priority"]:
466
priority = self.server.settings["priority"]
467
gnutls.library.functions.gnutls_priority_set_direct\
468
(session._c_object, priority, None);
472
except gnutls.errors.GNUTLSError, error:
473
logger.warning(u"Handshake failed: %s", error)
474
# Do not run session.bye() here: the session is not
475
# established. Just abandon the request.
478
fpr = fingerprint(peer_certificate(session))
479
except (TypeError, gnutls.errors.GNUTLSError), error:
480
logger.warning(u"Bad certificate: %s", error)
483
logger.debug(u"Fingerprint: %s", fpr)
485
for c in self.server.clients:
486
if c.fingerprint == fpr:
490
logger.warning(u"Client not found for fingerprint: %s",
494
# Have to check if client.still_valid(), since it is possible
495
# that the client timed out while establishing the GnuTLS
497
if not client.still_valid():
498
logger.warning(u"Client %(name)s is invalid",
503
while sent_size < len(client.secret):
504
sent = session.send(client.secret[sent_size:])
505
logger.debug(u"Sent: %d, remaining: %d",
506
sent, len(client.secret)
507
- (sent_size + sent))
512
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
513
"""IPv6 TCP server. Accepts 'None' as address and/or port.
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
515
settings: Server settings
516
clients: Set() of Client objects
1735
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
518
address_family = socket.AF_INET6
519
def __init__(self, *args, **kwargs):
520
if "settings" in kwargs:
521
self.settings = kwargs["settings"]
522
del kwargs["settings"]
523
if "clients" in kwargs:
524
self.clients = kwargs["clients"]
525
del kwargs["clients"]
526
return super(type(self), self).__init__(*args, **kwargs)
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)
527
1746
def server_bind(self):
528
1747
"""This overrides the normal server_bind() function
529
1748
to bind to an interface if one was specified, and also NOT to
530
1749
bind to an address or port if they were not specified."""
531
if self.settings["interface"]:
532
# 25 is from /usr/include/asm-i486/socket.h
533
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
535
self.socket.setsockopt(socket.SOL_SOCKET,
537
self.settings["interface"])
538
except socket.error, error:
539
if error[0] == errno.EPERM:
540
logger.error(u"No permission to"
541
u" bind to interface %s",
542
self.settings["interface"])
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",
545
1772
# Only bind(2) the socket if we really need to.
546
1773
if self.server_address[0] or self.server_address[1]:
547
1774
if not self.server_address[0]:
549
self.server_address = (in6addr_any,
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,
550
1780
self.server_address[1])
551
1781
elif not self.server_address[1]:
552
1782
self.server_address = (self.server_address[0],
554
# if self.settings["interface"]:
1784
# if self.interface:
555
1785
# self.server_address = (self.server_address[0],
558
1788
# if_nametoindex
561
return super(type(self), self).server_bind()
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)
1816
def server_activate(self):
1818
return socketserver.TCPServer.server_activate(self)
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)
564
1913
def string_to_delta(interval):
565
1914
"""Parse a string and return a datetime.timedelta
567
1916
>>> string_to_delta('7d')
568
1917
datetime.timedelta(7)
569
1918
>>> string_to_delta('60s')
711
2031
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
712
2032
"servicename": "Mandos",
2037
"statedir": "/var/lib/mandos"
715
2040
# Parse config file for server-global settings
716
server_config = ConfigParser.SafeConfigParser(server_defaults)
2041
server_config = configparser.SafeConfigParser(server_defaults)
717
2042
del server_defaults
718
server_config.read(os.path.join(options.configdir, "mandos.conf"))
2043
server_config.read(os.path.join(options.configdir,
719
2045
# Convert the SafeConfigParser object to a dict
720
2046
server_settings = server_config.defaults()
721
# Use getboolean on the boolean config option
722
server_settings["debug"] = server_config.getboolean\
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",
724
2054
del server_config
726
2056
# Override the settings from the config file with command line
727
2057
# options, if set.
728
2058
for option in ("interface", "address", "port", "debug",
729
"priority", "servicename", "configdir"):
2059
"priority", "servicename", "configdir",
2060
"use_dbus", "use_ipv6", "debuglevel", "restore",
730
2062
value = getattr(options, option)
731
2063
if value is not None:
732
2064
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])
734
2070
# Now we have our good server settings in "server_settings"
2072
##################################################################
736
2075
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"],
739
syslogger.setLevel(logging.WARNING)
740
console.setLevel(logging.WARNING)
2083
initlogger(debug, logging.DEBUG)
2088
level = getattr(logging, debuglevel.upper())
2089
initlogger(debug, level)
742
2091
if server_settings["servicename"] != "Mandos":
743
syslogger.setFormatter(logging.Formatter\
744
('Mandos (%s): %%(levelname)s:'
2092
syslogger.setFormatter(logging.Formatter
2093
('Mandos (%s) [%%(process)d]:'
2094
' %%(levelname)s: %%(message)s'
746
2095
% server_settings["servicename"]))
748
2097
# Parse config file with clients
749
client_defaults = { "timeout": "1h",
751
"checker": "fping -q -- %(host)s",
754
client_config = ConfigParser.SafeConfigParser(client_defaults)
2098
client_config = configparser.SafeConfigParser(Client.client_defaults)
755
2099
client_config.read(os.path.join(server_settings["configdir"],
756
2100
"clients.conf"))
759
service = AvahiService(name = server_settings["servicename"],
760
type = "_mandos._tcp", );
761
if server_settings["interface"]:
762
service.interface = if_nametoindex(server_settings["interface"])
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
2138
except OSError as error:
2139
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()
764
2169
global main_loop
767
2170
# From the Avahi example code
768
2171
DBusGMainLoop(set_as_default=True )
769
2172
main_loop = gobject.MainLoop()
770
2173
bus = dbus.SystemBus()
771
server = dbus.Interface(
772
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
773
avahi.DBUS_INTERFACE_SERVER )
774
2174
# End of Avahi example code
777
def remove_from_clients(client):
778
clients.remove(client)
780
logger.critical(u"No clients left, exiting")
783
clients.update(Set(Client(name = section,
784
stop_hook = remove_from_clients,
786
= dict(client_config.items(section)))
787
for section in client_config.sections()))
789
logger.critical(u"No clients defined")
793
# Redirect stdin so all checkers get /dev/null
794
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
795
os.dup2(null, sys.stdin.fileno())
800
logger.removeHandler(console)
801
# Close all input and output, do double fork, etc.
804
pidfilename = "/var/run/mandos/mandos.pid"
807
pidfile = open(pidfilename, "w")
808
pidfile.write(str(pid) + "\n")
812
logger.error(u"Could not write %s file with PID %d",
813
pidfilename, os.getpid())
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")
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
signal.signal(signal.SIGINT, signal.SIG_IGN)
2309
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2310
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()
816
2371
"Cleanup function; run on exit"
818
# From the Avahi example code
819
if not group is None:
822
# End of Avahi example code
825
client = clients.pop()
826
client.stop_hook = None
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()
829
2440
atexit.register(cleanup)
832
signal.signal(signal.SIGINT, signal.SIG_IGN)
833
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
834
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
836
for client in clients:
839
tcp_server = IPv6_TCPServer((server_settings["address"],
840
server_settings["port"]),
842
settings=server_settings,
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()
2451
tcp_server.server_activate()
844
2453
# Find out what port we got
845
2454
service.port = tcp_server.socket.getsockname()[1]
846
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
847
u" scope_id %d" % tcp_server.socket.getsockname())
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())
849
2463
#service.interface = tcp_server.socket.getsockname()[3]
852
2466
# From the Avahi example code
853
server.connect_to_signal("StateChanged", server_state_changed)
855
server_state_changed(server.GetState())
856
except dbus.exceptions.DBusException, error:
857
logger.critical(u"DBusException: %s", error)
2469
except dbus.exceptions.DBusException as error:
2470
logger.critical("DBusException: %s", error)
859
2473
# End of Avahi example code
861
2475
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
862
2476
lambda *args, **kwargs:
863
tcp_server.handle_request\
864
(*args[2:], **kwargs) or True)
2477
(tcp_server.handle_request
2478
(*args[2:], **kwargs) or True))
866
logger.debug(u"Starting main loop")
867
main_loop_started = True
2480
logger.debug("Starting main loop")
869
except AvahiError, error:
870
logger.critical(u"AvahiError: %s" + unicode(error))
2482
except AvahiError as error:
2483
logger.critical("AvahiError: %s", error)
872
2486
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
876
2494
if __name__ == '__main__':