157
104
max_renames: integer; maximum number of renames
158
105
rename_count: integer; counter so we only rename after collisions
159
106
a sensible number of times
160
group: D-Bus Entry Group
162
bus: dbus.SystemBus()
164
108
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
165
servicetype = None, port = None, TXT = None,
166
domain = "", host = "", max_renames = 32768,
167
protocol = avahi.PROTO_UNSPEC, bus = None):
109
type = None, port = None, TXT = None, domain = "",
110
host = "", max_renames = 32768):
168
111
self.interface = interface
170
self.type = servicetype
172
self.TXT = TXT if TXT is not None else []
173
119
self.domain = domain
175
121
self.rename_count = 0
176
122
self.max_renames = max_renames
177
self.protocol = protocol
178
self.group = None # our entry group
181
self.entry_group_state_changed_match = None
182
123
def rename(self):
183
124
"""Derived from the Avahi example code"""
184
125
if self.rename_count >= self.max_renames:
185
logger.critical("No suitable Zeroconf service name found"
186
" after %i retries, exiting.",
126
logger.critical(u"No suitable service name found after %i"
127
u" retries, exiting.", rename_count)
188
128
raise AvahiServiceError("Too many renames")
189
self.name = unicode(self.server
190
.GetAlternativeServiceName(self.name))
191
logger.info("Changing Zeroconf service name to %r ...",
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))
196
except dbus.exceptions.DBusException as error:
197
logger.critical("DBusException: %s", error)
200
136
self.rename_count += 1
201
137
def remove(self):
202
138
"""Derived from the Avahi example code"""
203
if self.entry_group_state_changed_match is not None:
204
self.entry_group_state_changed_match.remove()
205
self.entry_group_state_changed_match = None
206
if self.group is not None:
139
if group is not None:
209
142
"""Derived from the Avahi example code"""
211
if self.group is None:
212
self.group = dbus.Interface(
213
self.bus.get_object(avahi.DBUS_NAME,
214
self.server.EntryGroupNew()),
215
avahi.DBUS_INTERFACE_ENTRY_GROUP)
216
self.entry_group_state_changed_match = (
217
self.group.connect_to_signal(
218
'StateChanged', self.entry_group_state_changed))
219
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
220
self.name, self.type)
221
self.group.AddService(
224
dbus.UInt32(0), # flags
225
self.name, self.type,
226
self.domain, self.host,
227
dbus.UInt16(self.port),
228
avahi.string_array_to_txt_array(self.TXT))
230
def entry_group_state_changed(self, state, error):
231
"""Derived from the Avahi example code"""
232
logger.debug("Avahi entry group state change: %i", state)
234
if state == avahi.ENTRY_GROUP_ESTABLISHED:
235
logger.debug("Zeroconf service established.")
236
elif state == avahi.ENTRY_GROUP_COLLISION:
237
logger.info("Zeroconf service name collision.")
239
elif state == avahi.ENTRY_GROUP_FAILURE:
240
logger.critical("Avahi: Error in group state changed %s",
242
raise AvahiGroupError("State changed: %s"
245
"""Derived from the Avahi example code"""
246
if self.group is not None:
249
except (dbus.exceptions.UnknownMethodException,
250
dbus.exceptions.DBusException):
254
def server_state_changed(self, state, error=None):
255
"""Derived from the Avahi example code"""
256
logger.debug("Avahi server state change: %i", state)
257
bad_states = { avahi.SERVER_INVALID:
258
"Zeroconf server invalid",
259
avahi.SERVER_REGISTERING: None,
260
avahi.SERVER_COLLISION:
261
"Zeroconf server name collision",
262
avahi.SERVER_FAILURE:
263
"Zeroconf server failure" }
264
if state in bad_states:
265
if bad_states[state] is not None:
267
logger.error(bad_states[state])
269
logger.error(bad_states[state] + ": %r", error)
271
elif state == avahi.SERVER_RUNNING:
275
logger.debug("Unknown state: %r", state)
277
logger.debug("Unknown state: %r: %r", state, error)
279
"""Derived from the Avahi example code"""
280
if self.server is None:
281
self.server = dbus.Interface(
282
self.bus.get_object(avahi.DBUS_NAME,
283
avahi.DBUS_PATH_SERVER,
284
follow_name_owner_changes=True),
285
avahi.DBUS_INTERFACE_SERVER)
286
self.server.connect_to_signal("StateChanged",
287
self.server_state_changed)
288
self.server_state_changed(self.server.GetState())
290
class AvahiServiceToSyslog(AvahiService):
292
"""Add the new name to the syslog messages"""
293
ret = AvahiService.rename(self)
294
syslogger.setFormatter(logging.Formatter
295
('Mandos (%s) [%%(process)d]:'
296
' %%(levelname)s: %%(message)s'
300
def _timedelta_to_milliseconds(td):
301
"Convert a datetime.timedelta() to milliseconds"
302
return ((td.days * 24 * 60 * 60 * 1000)
303
+ (td.seconds * 1000)
304
+ (td.microseconds // 1000))
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
306
168
class Client(object):
307
169
"""A representation of a client host served by this server.
310
_approved: bool(); 'None' if not yet approved/disapproved
311
approval_delay: datetime.timedelta(); Time to wait for approval
312
approval_duration: datetime.timedelta(); Duration of one approval
313
checker: subprocess.Popen(); a running checker process used
314
to see if the client lives.
315
'None' if no process is running.
316
checker_callback_tag: a gobject event source tag, or None
317
checker_command: string; External command which is run to check
318
if client lives. %() expansions are done at
171
name: string; from the config file, used in log messages
172
fingerprint: string (40 or 32 hexadecimal digits); used to
173
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
319
190
runtime with vars(self) as dict, so that for
320
191
instance %(name)s can be used in the command.
321
checker_initiator_tag: a gobject event source tag, or None
322
created: datetime.datetime(); (UTC) object creation
323
client_structure: Object describing what attributes a client has
324
and is used for storing the client at exit
325
current_checker_command: string; current running checker_command
326
disable_initiator_tag: a gobject event source tag, or None
328
fingerprint: string (40 or 32 hexadecimal digits); used to
329
uniquely identify the client
330
host: string; available for use by the checker command
331
interval: datetime.timedelta(); How often to start a new checker
332
last_approval_request: datetime.datetime(); (UTC) or None
333
last_checked_ok: datetime.datetime(); (UTC) or None
334
last_checker_status: integer between 0 and 255 reflecting exit status
335
of last checker. -1 reflect crashed checker,
337
last_enabled: datetime.datetime(); (UTC)
338
name: string; from the config file, used in log messages and
340
secret: bytestring; sent verbatim (over TLS) to client
341
timeout: datetime.timedelta(); How long from last_checked_ok
342
until this client is disabled
343
extended_timeout: extra long timeout when password has been sent
344
runtime_expansions: Allowed attributes for runtime expansion.
345
expires: datetime.datetime(); time (UTC) when a client will be
193
_timeout: Real variable for 'timeout'
194
_interval: Real variable for 'interval'
195
_timeout_milliseconds: Used when calling gobject.timeout_add()
196
_interval_milliseconds: - '' -
349
runtime_expansions = ("approval_delay", "approval_duration",
350
"created", "enabled", "fingerprint",
351
"host", "interval", "last_checked_ok",
352
"last_enabled", "name", "timeout")
354
def timeout_milliseconds(self):
355
"Return the 'timeout' attribute in milliseconds"
356
return _timedelta_to_milliseconds(self.timeout)
358
def extended_timeout_milliseconds(self):
359
"Return the 'extended_timeout' attribute in milliseconds"
360
return _timedelta_to_milliseconds(self.extended_timeout)
362
def interval_milliseconds(self):
363
"Return the 'interval' attribute in milliseconds"
364
return _timedelta_to_milliseconds(self.interval)
366
def approval_delay_milliseconds(self):
367
return _timedelta_to_milliseconds(self.approval_delay)
369
def __init__(self, name = None, config=None):
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={}):
370
222
"""Note: the 'checker' key in 'config' sets the
371
223
'checker_command' attribute and *not* the 'checker'
376
logger.debug("Creating client %r", self.name)
226
logger.debug(u"Creating client %r", self.name)
377
227
# Uppercase and remove spaces from fingerprint for later
378
228
# comparison purposes with return value from the fingerprint()
380
self.fingerprint = (config["fingerprint"].upper()
382
logger.debug(" Fingerprint: %s", self.fingerprint)
230
self.fingerprint = config["fingerprint"].upper()\
232
logger.debug(u" Fingerprint: %s", self.fingerprint)
383
233
if "secret" in config:
384
self.secret = config["secret"].decode("base64")
234
self.secret = config["secret"].decode(u"base64")
385
235
elif "secfile" in config:
386
with open(os.path.expanduser(os.path.expandvars
387
(config["secfile"])),
389
self.secret = secfile.read()
236
sf = open(config["secfile"])
237
self.secret = sf.read()
391
raise TypeError("No secret or secfile for client %s"
240
raise TypeError(u"No secret or secfile for client %s"
393
242
self.host = config.get("host", "")
394
self.created = datetime.datetime.utcnow()
396
self.last_approval_request = None
397
self.last_enabled = datetime.datetime.utcnow()
243
self.created = datetime.datetime.now()
398
244
self.last_checked_ok = None
399
self.last_checker_status = None
400
245
self.timeout = string_to_delta(config["timeout"])
401
self.extended_timeout = string_to_delta(config
402
["extended_timeout"])
403
246
self.interval = string_to_delta(config["interval"])
247
self.stop_hook = stop_hook
404
248
self.checker = None
405
249
self.checker_initiator_tag = None
406
self.disable_initiator_tag = None
407
self.expires = datetime.datetime.utcnow() + self.timeout
250
self.stop_initiator_tag = None
408
251
self.checker_callback_tag = None
409
self.checker_command = config["checker"]
410
self.current_checker_command = None
411
self._approved = None
412
self.approved_by_default = config.get("approved_by_default",
414
self.approvals_pending = 0
415
self.approval_delay = string_to_delta(
416
config["approval_delay"])
417
self.approval_duration = string_to_delta(
418
config["approval_duration"])
419
self.changedstate = (multiprocessing_manager
420
.Condition(multiprocessing_manager
422
self.client_structure = [attr for attr in self.__dict__.iterkeys() if not attr.startswith("_")]
423
self.client_structure.append("client_structure")
425
for name, t in inspect.getmembers(type(self),
426
lambda obj: isinstance(obj, property)):
427
if not name.startswith("_"):
428
self.client_structure.append(name)
430
# Send notice to process children that client state has changed
431
def send_changedstate(self):
432
with self.changedstate:
433
self.changedstate.notify_all()
252
self.check_command = config["checker"]
436
254
"""Start this client's checker and timeout hooks"""
437
if getattr(self, "enabled", False):
440
self.send_changedstate()
441
self.expires = datetime.datetime.utcnow() + self.timeout
443
self.last_enabled = datetime.datetime.utcnow()
446
def disable(self, quiet=True):
447
"""Disable this client."""
448
if not getattr(self, "enabled", False):
255
# Schedule a new checker to be started an 'interval' from now,
256
# and every interval from then on.
257
self.checker_initiator_tag = gobject.timeout_add\
258
(self._interval_milliseconds,
260
# Also start a new checker *right now*.
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)
451
self.send_changedstate()
453
logger.info("Disabling client %s", self.name)
454
if getattr(self, "disable_initiator_tag", False):
455
gobject.source_remove(self.disable_initiator_tag)
456
self.disable_initiator_tag = None
276
if getattr(self, "stop_initiator_tag", False):
277
gobject.source_remove(self.stop_initiator_tag)
278
self.stop_initiator_tag = None
458
279
if getattr(self, "checker_initiator_tag", False):
459
280
gobject.source_remove(self.checker_initiator_tag)
460
281
self.checker_initiator_tag = None
461
282
self.stop_checker()
463
285
# Do not run this again if called by a gobject.timeout_add
466
287
def __del__(self):
469
def init_checker(self):
470
# Schedule a new checker to be started an 'interval' from now,
471
# and every interval from then on.
472
self.checker_initiator_tag = (gobject.timeout_add
473
(self.interval_milliseconds(),
475
# Schedule a disable() when 'timeout' has passed
476
self.disable_initiator_tag = (gobject.timeout_add
477
(self.timeout_milliseconds(),
479
# Also start a new checker *right now*.
482
def checker_callback(self, pid, condition, command):
288
self.stop_hook = None
290
def checker_callback(self, pid, condition):
483
291
"""The checker has completed, so take appropriate actions."""
292
now = datetime.datetime.now()
484
293
self.checker_callback_tag = None
485
294
self.checker = None
486
if os.WIFEXITED(condition):
487
self.last_checker_status = os.WEXITSTATUS(condition)
488
if self.last_checker_status == 0:
489
logger.info("Checker for %(name)s succeeded",
493
logger.info("Checker for %(name)s failed",
496
self.last_checker_status = -1
497
logger.warning("Checker for %(name)s crashed?",
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?",
500
def checked_ok(self, timeout=None):
501
"""Bump up the timeout for this client.
503
This should only be called when the client has been seen,
507
timeout = self.timeout
508
self.last_checked_ok = datetime.datetime.utcnow()
509
if self.disable_initiator_tag is not None:
510
gobject.source_remove(self.disable_initiator_tag)
511
if getattr(self, "enabled", False):
512
self.disable_initiator_tag = (gobject.timeout_add
513
(_timedelta_to_milliseconds
514
(timeout), self.disable))
515
self.expires = datetime.datetime.utcnow() + timeout
517
def need_approval(self):
518
self.last_approval_request = datetime.datetime.utcnow()
308
logger.info(u"Checker for %(name)s failed",
520
310
def start_checker(self):
521
311
"""Start a new checker subprocess if one is not running.
523
312
If a checker already exists, leave it running and do
525
314
# The reason for not killing a running checker is that if we
599
355
self.checker_callback_tag = None
600
356
if getattr(self, "checker", None) is None:
602
logger.debug("Stopping checker for %(name)s", vars(self))
358
logger.debug(u"Stopping checker for %(name)s", vars(self))
604
360
os.kill(self.checker.pid, signal.SIGTERM)
606
362
#if self.checker.poll() is None:
607
363
# os.kill(self.checker.pid, signal.SIGKILL)
608
except OSError as error:
364
except OSError, error:
609
365
if error.errno != errno.ESRCH: # No such process
611
367
self.checker = None
613
# Encrypts a client secret and stores it in a varible encrypted_secret
614
def encrypt_secret(self, key):
615
# Encryption-key need to be of a specific size, so we hash inputed key
616
hasheng = hashlib.sha256()
618
encryptionkey = hasheng.digest()
620
# Create validation hash so we know at decryption if it was sucessful
621
hasheng = hashlib.sha256()
622
hasheng.update(self.secret)
623
validationhash = hasheng.digest()
626
iv = os.urandom(Crypto.Cipher.AES.block_size)
627
ciphereng = Crypto.Cipher.AES.new(encryptionkey,
628
Crypto.Cipher.AES.MODE_CFB, iv)
629
ciphertext = ciphereng.encrypt(validationhash+self.secret)
630
self.encrypted_secret = (ciphertext, iv)
632
# Decrypt a encrypted client secret
633
def decrypt_secret(self, key):
634
# Decryption-key need to be of a specific size, so we hash inputed key
635
hasheng = hashlib.sha256()
637
encryptionkey = hasheng.digest()
639
# Decrypt encrypted secret
640
ciphertext, iv = self.encrypted_secret
641
ciphereng = Crypto.Cipher.AES.new(encryptionkey,
642
Crypto.Cipher.AES.MODE_CFB, iv)
643
plain = ciphereng.decrypt(ciphertext)
645
# Validate decrypted secret to know if it was succesful
646
hasheng = hashlib.sha256()
647
validationhash = plain[:hasheng.digest_size]
648
secret = plain[hasheng.digest_size:]
649
hasheng.update(secret)
651
# if validation fails, we use key as new secret. Otherwhise, we use
652
# the decrypted secret
653
if hasheng.digest() == validationhash:
657
del self.encrypted_secret
660
def dbus_service_property(dbus_interface, signature="v",
661
access="readwrite", byte_arrays=False):
662
"""Decorators for marking methods of a DBusObjectWithProperties to
663
become properties on the D-Bus.
665
The decorated method will be called with no arguments by "Get"
666
and with one argument by "Set".
668
The parameters, where they are supported, are the same as
669
dbus.service.method, except there is only "signature", since the
670
type from Get() and the type sent to Set() is the same.
672
# Encoding deeply encoded byte arrays is not supported yet by the
673
# "Set" method, so we fail early here:
674
if byte_arrays and signature != "ay":
675
raise ValueError("Byte arrays not supported for non-'ay'"
676
" signature %r" % signature)
678
func._dbus_is_property = True
679
func._dbus_interface = dbus_interface
680
func._dbus_signature = signature
681
func._dbus_access = access
682
func._dbus_name = func.__name__
683
if func._dbus_name.endswith("_dbus_property"):
684
func._dbus_name = func._dbus_name[:-14]
685
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
690
class DBusPropertyException(dbus.exceptions.DBusException):
691
"""A base class for D-Bus property-related exceptions
693
def __unicode__(self):
694
return unicode(str(self))
697
class DBusPropertyAccessException(DBusPropertyException):
698
"""A property's access permissions disallows an operation.
703
class DBusPropertyNotFound(DBusPropertyException):
704
"""An attempt was made to access a non-existing property.
709
class DBusObjectWithProperties(dbus.service.Object):
710
"""A D-Bus object with properties.
712
Classes inheriting from this can use the dbus_service_property
713
decorator to expose methods as D-Bus properties. It exposes the
714
standard Get(), Set(), and GetAll() methods on the D-Bus.
718
def _is_dbus_property(obj):
719
return getattr(obj, "_dbus_is_property", False)
721
def _get_all_dbus_properties(self):
722
"""Returns a generator of (name, attribute) pairs
724
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
725
for cls in self.__class__.__mro__
727
inspect.getmembers(cls, self._is_dbus_property))
729
def _get_dbus_property(self, interface_name, property_name):
730
"""Returns a bound method if one exists which is a D-Bus
731
property with the specified name and interface.
733
for cls in self.__class__.__mro__:
734
for name, value in (inspect.getmembers
735
(cls, self._is_dbus_property)):
736
if (value._dbus_name == property_name
737
and value._dbus_interface == interface_name):
738
return value.__get__(self)
741
raise DBusPropertyNotFound(self.dbus_object_path + ":"
742
+ interface_name + "."
745
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
747
def Get(self, interface_name, property_name):
748
"""Standard D-Bus property Get() method, see D-Bus standard.
750
prop = self._get_dbus_property(interface_name, property_name)
751
if prop._dbus_access == "write":
752
raise DBusPropertyAccessException(property_name)
754
if not hasattr(value, "variant_level"):
756
return type(value)(value, variant_level=value.variant_level+1)
758
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
759
def Set(self, interface_name, property_name, value):
760
"""Standard D-Bus property Set() method, see D-Bus standard.
762
prop = self._get_dbus_property(interface_name, property_name)
763
if prop._dbus_access == "read":
764
raise DBusPropertyAccessException(property_name)
765
if prop._dbus_get_args_options["byte_arrays"]:
766
# The byte_arrays option is not supported yet on
767
# signatures other than "ay".
768
if prop._dbus_signature != "ay":
770
value = dbus.ByteArray(''.join(unichr(byte)
774
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
775
out_signature="a{sv}")
776
def GetAll(self, interface_name):
777
"""Standard D-Bus property GetAll() method, see D-Bus
780
Note: Will not include properties with access="write".
783
for name, prop in self._get_all_dbus_properties():
785
and interface_name != prop._dbus_interface):
786
# Interface non-empty but did not match
788
# Ignore write-only properties
789
if prop._dbus_access == "write":
792
if not hasattr(value, "variant_level"):
793
properties[name] = value
795
properties[name] = type(value)(value, variant_level=
796
value.variant_level+1)
797
return dbus.Dictionary(properties, signature="sv")
799
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
801
path_keyword='object_path',
802
connection_keyword='connection')
803
def Introspect(self, object_path, connection):
804
"""Standard D-Bus method, overloaded to insert property tags.
806
xmlstring = dbus.service.Object.Introspect(self, object_path,
809
document = xml.dom.minidom.parseString(xmlstring)
810
def make_tag(document, name, prop):
811
e = document.createElement("property")
812
e.setAttribute("name", name)
813
e.setAttribute("type", prop._dbus_signature)
814
e.setAttribute("access", prop._dbus_access)
816
for if_tag in document.getElementsByTagName("interface"):
817
for tag in (make_tag(document, name, prop)
819
in self._get_all_dbus_properties()
820
if prop._dbus_interface
821
== if_tag.getAttribute("name")):
822
if_tag.appendChild(tag)
823
# Add the names to the return values for the
824
# "org.freedesktop.DBus.Properties" methods
825
if (if_tag.getAttribute("name")
826
== "org.freedesktop.DBus.Properties"):
827
for cn in if_tag.getElementsByTagName("method"):
828
if cn.getAttribute("name") == "Get":
829
for arg in cn.getElementsByTagName("arg"):
830
if (arg.getAttribute("direction")
832
arg.setAttribute("name", "value")
833
elif cn.getAttribute("name") == "GetAll":
834
for arg in cn.getElementsByTagName("arg"):
835
if (arg.getAttribute("direction")
837
arg.setAttribute("name", "props")
838
xmlstring = document.toxml("utf-8")
840
except (AttributeError, xml.dom.DOMException,
841
xml.parsers.expat.ExpatError) as error:
842
logger.error("Failed to override Introspection method",
847
def datetime_to_dbus (dt, variant_level=0):
848
"""Convert a UTC datetime.datetime() to a D-Bus type."""
850
return dbus.String("", variant_level = variant_level)
851
return dbus.String(dt.isoformat(),
852
variant_level=variant_level)
855
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
857
"""Applied to an empty subclass of a D-Bus object, this metaclass
858
will add additional D-Bus attributes matching a certain pattern.
860
def __new__(mcs, name, bases, attr):
861
# Go through all the base classes which could have D-Bus
862
# methods, signals, or properties in them
863
for base in (b for b in bases
864
if issubclass(b, dbus.service.Object)):
865
# Go though all attributes of the base class
866
for attrname, attribute in inspect.getmembers(base):
867
# Ignore non-D-Bus attributes, and D-Bus attributes
868
# with the wrong interface name
869
if (not hasattr(attribute, "_dbus_interface")
870
or not attribute._dbus_interface
871
.startswith("se.recompile.Mandos")):
873
# Create an alternate D-Bus interface name based on
875
alt_interface = (attribute._dbus_interface
876
.replace("se.recompile.Mandos",
877
"se.bsnet.fukt.Mandos"))
878
# Is this a D-Bus signal?
879
if getattr(attribute, "_dbus_is_signal", False):
880
# Extract the original non-method function by
882
nonmethod_func = (dict(
883
zip(attribute.func_code.co_freevars,
884
attribute.__closure__))["func"]
886
# Create a new, but exactly alike, function
887
# object, and decorate it to be a new D-Bus signal
888
# with the alternate D-Bus interface name
889
new_function = (dbus.service.signal
891
attribute._dbus_signature)
893
nonmethod_func.func_code,
894
nonmethod_func.func_globals,
895
nonmethod_func.func_name,
896
nonmethod_func.func_defaults,
897
nonmethod_func.func_closure)))
898
# Define a creator of a function to call both the
899
# old and new functions, so both the old and new
900
# signals gets sent when the function is called
901
def fixscope(func1, func2):
902
"""This function is a scope container to pass
903
func1 and func2 to the "call_both" function
904
outside of its arguments"""
905
def call_both(*args, **kwargs):
906
"""This function will emit two D-Bus
907
signals by calling func1 and func2"""
908
func1(*args, **kwargs)
909
func2(*args, **kwargs)
911
# Create the "call_both" function and add it to
913
attr[attrname] = fixscope(attribute,
915
# Is this a D-Bus method?
916
elif getattr(attribute, "_dbus_is_method", False):
917
# Create a new, but exactly alike, function
918
# object. Decorate it to be a new D-Bus method
919
# with the alternate D-Bus interface name. Add it
921
attr[attrname] = (dbus.service.method
923
attribute._dbus_in_signature,
924
attribute._dbus_out_signature)
926
(attribute.func_code,
927
attribute.func_globals,
929
attribute.func_defaults,
930
attribute.func_closure)))
931
# Is this a D-Bus property?
932
elif getattr(attribute, "_dbus_is_property", False):
933
# Create a new, but exactly alike, function
934
# object, and decorate it to be a new D-Bus
935
# property with the alternate D-Bus interface
936
# name. Add it to the class.
937
attr[attrname] = (dbus_service_property
939
attribute._dbus_signature,
940
attribute._dbus_access,
942
._dbus_get_args_options
945
(attribute.func_code,
946
attribute.func_globals,
948
attribute.func_defaults,
949
attribute.func_closure)))
950
return type.__new__(mcs, name, bases, attr)
953
class ClientDBus(Client, DBusObjectWithProperties):
954
"""A Client class using D-Bus
957
dbus_object_path: dbus.ObjectPath
958
bus: dbus.SystemBus()
961
runtime_expansions = (Client.runtime_expansions
962
+ ("dbus_object_path",))
964
# dbus.service.Object doesn't use super(), so we can't either.
966
def __init__(self, bus = None, *args, **kwargs):
968
Client.__init__(self, *args, **kwargs)
970
self._approvals_pending = 0
971
# Only now, when this client is initialized, can it show up on
973
client_object_name = unicode(self.name).translate(
976
self.dbus_object_path = (dbus.ObjectPath
977
("/clients/" + client_object_name))
978
DBusObjectWithProperties.__init__(self, self.bus,
979
self.dbus_object_path)
981
def notifychangeproperty(transform_func,
982
dbus_name, type_func=lambda x: x,
984
""" Modify a variable so that it's a property which announces
987
transform_fun: Function that takes a value and a variant_level
988
and transforms it to a D-Bus type.
989
dbus_name: D-Bus name of the variable
990
type_func: Function that transform the value before sending it
991
to the D-Bus. Default: no transform
992
variant_level: D-Bus variant level. Default: 1
994
attrname = "_{0}".format(dbus_name)
995
def setter(self, value):
996
if hasattr(self, "dbus_object_path"):
997
if (not hasattr(self, attrname) or
998
type_func(getattr(self, attrname, None))
999
!= type_func(value)):
1000
dbus_value = transform_func(type_func(value),
1003
self.PropertyChanged(dbus.String(dbus_name),
1005
setattr(self, attrname, value)
1007
return property(lambda self: getattr(self, attrname), setter)
1010
expires = notifychangeproperty(datetime_to_dbus, "Expires")
1011
approvals_pending = notifychangeproperty(dbus.Boolean,
1014
enabled = notifychangeproperty(dbus.Boolean, "Enabled")
1015
last_enabled = notifychangeproperty(datetime_to_dbus,
1017
checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
1018
type_func = lambda checker:
1019
checker is not None)
1020
last_checked_ok = notifychangeproperty(datetime_to_dbus,
1022
last_approval_request = notifychangeproperty(
1023
datetime_to_dbus, "LastApprovalRequest")
1024
approved_by_default = notifychangeproperty(dbus.Boolean,
1025
"ApprovedByDefault")
1026
approval_delay = notifychangeproperty(dbus.UInt16,
1029
_timedelta_to_milliseconds)
1030
approval_duration = notifychangeproperty(
1031
dbus.UInt16, "ApprovalDuration",
1032
type_func = _timedelta_to_milliseconds)
1033
host = notifychangeproperty(dbus.String, "Host")
1034
timeout = notifychangeproperty(dbus.UInt16, "Timeout",
1036
_timedelta_to_milliseconds)
1037
extended_timeout = notifychangeproperty(
1038
dbus.UInt16, "ExtendedTimeout",
1039
type_func = _timedelta_to_milliseconds)
1040
interval = notifychangeproperty(dbus.UInt16,
1043
_timedelta_to_milliseconds)
1044
checker_command = notifychangeproperty(dbus.String, "Checker")
1046
del notifychangeproperty
1048
def __del__(self, *args, **kwargs):
1050
self.remove_from_connection()
1053
if hasattr(DBusObjectWithProperties, "__del__"):
1054
DBusObjectWithProperties.__del__(self, *args, **kwargs)
1055
Client.__del__(self, *args, **kwargs)
1057
def checker_callback(self, pid, condition, command,
1059
self.checker_callback_tag = None
1061
if os.WIFEXITED(condition):
1062
exitstatus = os.WEXITSTATUS(condition)
1064
self.CheckerCompleted(dbus.Int16(exitstatus),
1065
dbus.Int64(condition),
1066
dbus.String(command))
1069
self.CheckerCompleted(dbus.Int16(-1),
1070
dbus.Int64(condition),
1071
dbus.String(command))
1073
return Client.checker_callback(self, pid, condition, command,
1076
def start_checker(self, *args, **kwargs):
1077
old_checker = self.checker
1078
if self.checker is not None:
1079
old_checker_pid = self.checker.pid
1081
old_checker_pid = None
1082
r = Client.start_checker(self, *args, **kwargs)
1083
# Only if new checker process was started
1084
if (self.checker is not None
1085
and old_checker_pid != self.checker.pid):
1087
self.CheckerStarted(self.current_checker_command)
1090
def _reset_approved(self):
1091
self._approved = None
1094
def approve(self, value=True):
1095
self.send_changedstate()
1096
self._approved = value
1097
gobject.timeout_add(_timedelta_to_milliseconds
1098
(self.approval_duration),
1099
self._reset_approved)
1102
## D-Bus methods, signals & properties
1103
_interface = "se.recompile.Mandos.Client"
1107
# CheckerCompleted - signal
1108
@dbus.service.signal(_interface, signature="nxs")
1109
def CheckerCompleted(self, exitcode, waitstatus, command):
1113
# CheckerStarted - signal
1114
@dbus.service.signal(_interface, signature="s")
1115
def CheckerStarted(self, command):
1119
# PropertyChanged - signal
1120
@dbus.service.signal(_interface, signature="sv")
1121
def PropertyChanged(self, property, value):
1125
# GotSecret - signal
1126
@dbus.service.signal(_interface)
1127
def GotSecret(self):
1129
Is sent after a successful transfer of secret from the Mandos
1130
server to mandos-client
1135
@dbus.service.signal(_interface, signature="s")
1136
def Rejected(self, reason):
1140
# NeedApproval - signal
1141
@dbus.service.signal(_interface, signature="tb")
1142
def NeedApproval(self, timeout, default):
1144
return self.need_approval()
1146
# NeRwequest - signal
1147
@dbus.service.signal(_interface, signature="s")
1148
def NewRequest(self, ip):
1150
Is sent after a client request a password.
1157
@dbus.service.method(_interface, in_signature="b")
1158
def Approve(self, value):
1161
# CheckedOK - method
1162
@dbus.service.method(_interface)
1163
def CheckedOK(self):
1167
@dbus.service.method(_interface)
1172
# StartChecker - method
1173
@dbus.service.method(_interface)
1174
def StartChecker(self):
1176
self.start_checker()
1179
@dbus.service.method(_interface)
1184
# StopChecker - method
1185
@dbus.service.method(_interface)
1186
def StopChecker(self):
1191
# ApprovalPending - property
1192
@dbus_service_property(_interface, signature="b", access="read")
1193
def ApprovalPending_dbus_property(self):
1194
return dbus.Boolean(bool(self.approvals_pending))
1196
# ApprovedByDefault - property
1197
@dbus_service_property(_interface, signature="b",
1199
def ApprovedByDefault_dbus_property(self, value=None):
1200
if value is None: # get
1201
return dbus.Boolean(self.approved_by_default)
1202
self.approved_by_default = bool(value)
1204
# ApprovalDelay - property
1205
@dbus_service_property(_interface, signature="t",
1207
def ApprovalDelay_dbus_property(self, value=None):
1208
if value is None: # get
1209
return dbus.UInt64(self.approval_delay_milliseconds())
1210
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1212
# ApprovalDuration - property
1213
@dbus_service_property(_interface, signature="t",
1215
def ApprovalDuration_dbus_property(self, value=None):
1216
if value is None: # get
1217
return dbus.UInt64(_timedelta_to_milliseconds(
1218
self.approval_duration))
1219
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1222
@dbus_service_property(_interface, signature="s", access="read")
1223
def Name_dbus_property(self):
1224
return dbus.String(self.name)
1226
# Fingerprint - property
1227
@dbus_service_property(_interface, signature="s", access="read")
1228
def Fingerprint_dbus_property(self):
1229
return dbus.String(self.fingerprint)
1232
@dbus_service_property(_interface, signature="s",
1234
def Host_dbus_property(self, value=None):
1235
if value is None: # get
1236
return dbus.String(self.host)
1239
# Created - property
1240
@dbus_service_property(_interface, signature="s", access="read")
1241
def Created_dbus_property(self):
1242
return dbus.String(datetime_to_dbus(self.created))
1244
# LastEnabled - property
1245
@dbus_service_property(_interface, signature="s", access="read")
1246
def LastEnabled_dbus_property(self):
1247
return datetime_to_dbus(self.last_enabled)
1249
# Enabled - property
1250
@dbus_service_property(_interface, signature="b",
1252
def Enabled_dbus_property(self, value=None):
1253
if value is None: # get
1254
return dbus.Boolean(self.enabled)
1260
# LastCheckedOK - property
1261
@dbus_service_property(_interface, signature="s",
1263
def LastCheckedOK_dbus_property(self, value=None):
1264
if value is not None:
1267
return datetime_to_dbus(self.last_checked_ok)
1269
# Expires - property
1270
@dbus_service_property(_interface, signature="s", access="read")
1271
def Expires_dbus_property(self):
1272
return datetime_to_dbus(self.expires)
1274
# LastApprovalRequest - property
1275
@dbus_service_property(_interface, signature="s", access="read")
1276
def LastApprovalRequest_dbus_property(self):
1277
return datetime_to_dbus(self.last_approval_request)
1279
# Timeout - property
1280
@dbus_service_property(_interface, signature="t",
1282
def Timeout_dbus_property(self, value=None):
1283
if value is None: # get
1284
return dbus.UInt64(self.timeout_milliseconds())
1285
self.timeout = datetime.timedelta(0, 0, 0, value)
1286
if getattr(self, "disable_initiator_tag", None) is None:
1288
# Reschedule timeout
1289
gobject.source_remove(self.disable_initiator_tag)
1290
self.disable_initiator_tag = None
1292
time_to_die = _timedelta_to_milliseconds((self
1297
if time_to_die <= 0:
1298
# The timeout has passed
1301
self.expires = (datetime.datetime.utcnow()
1302
+ datetime.timedelta(milliseconds =
1304
self.disable_initiator_tag = (gobject.timeout_add
1305
(time_to_die, self.disable))
1307
# ExtendedTimeout - property
1308
@dbus_service_property(_interface, signature="t",
1310
def ExtendedTimeout_dbus_property(self, value=None):
1311
if value is None: # get
1312
return dbus.UInt64(self.extended_timeout_milliseconds())
1313
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1315
# Interval - property
1316
@dbus_service_property(_interface, signature="t",
1318
def Interval_dbus_property(self, value=None):
1319
if value is None: # get
1320
return dbus.UInt64(self.interval_milliseconds())
1321
self.interval = datetime.timedelta(0, 0, 0, value)
1322
if getattr(self, "checker_initiator_tag", None) is None:
1324
# Reschedule checker run
1325
gobject.source_remove(self.checker_initiator_tag)
1326
self.checker_initiator_tag = (gobject.timeout_add
1327
(value, self.start_checker))
1328
self.start_checker() # Start one now, too
1330
# Checker - property
1331
@dbus_service_property(_interface, signature="s",
1333
def Checker_dbus_property(self, value=None):
1334
if value is None: # get
1335
return dbus.String(self.checker_command)
1336
self.checker_command = value
1338
# CheckerRunning - property
1339
@dbus_service_property(_interface, signature="b",
1341
def CheckerRunning_dbus_property(self, value=None):
1342
if value is None: # get
1343
return dbus.Boolean(self.checker is not None)
1345
self.start_checker()
1349
# ObjectPath - property
1350
@dbus_service_property(_interface, signature="o", access="read")
1351
def ObjectPath_dbus_property(self):
1352
return self.dbus_object_path # is already a dbus.ObjectPath
1355
@dbus_service_property(_interface, signature="ay",
1356
access="write", byte_arrays=True)
1357
def Secret_dbus_property(self, value):
1358
self.secret = str(value)
1363
class ProxyClient(object):
1364
def __init__(self, child_pipe, fpr, address):
1365
self._pipe = child_pipe
1366
self._pipe.send(('init', fpr, address))
1367
if not self._pipe.recv():
1370
def __getattribute__(self, name):
1371
if(name == '_pipe'):
1372
return super(ProxyClient, self).__getattribute__(name)
1373
self._pipe.send(('getattr', name))
1374
data = self._pipe.recv()
1375
if data[0] == 'data':
1377
if data[0] == 'function':
1378
def func(*args, **kwargs):
1379
self._pipe.send(('funcall', name, args, kwargs))
1380
return self._pipe.recv()[1]
1383
def __setattr__(self, name, value):
1384
if(name == '_pipe'):
1385
return super(ProxyClient, self).__setattr__(name, value)
1386
self._pipe.send(('setattr', name, value))
1389
class ClientDBusTransitional(ClientDBus):
1390
__metaclass__ = AlternateDBusNamesMetaclass
1393
class ClientHandler(socketserver.BaseRequestHandler, object):
1394
"""A class to handle client connections.
1396
Instantiated once for each connection to handle it.
368
def still_valid(self):
369
"""Has the timeout not yet passed for this client?"""
370
now = datetime.datetime.now()
371
if self.last_checked_ok is None:
372
return now < (self.created + self.timeout)
374
return now < (self.last_checked_ok + self.timeout)
377
def peer_certificate(session):
378
"Return the peer's OpenPGP certificate as a bytestring"
379
# If not an OpenPGP certificate...
380
if gnutls.library.functions.gnutls_certificate_type_get\
381
(session._c_object) \
382
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
383
# ...do the normal thing
384
return session.peer_certificate
385
list_size = ctypes.c_uint()
386
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
387
(session._c_object, ctypes.byref(list_size))
388
if list_size.value == 0:
391
return ctypes.string_at(cert.data, cert.size)
394
def fingerprint(openpgp):
395
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
396
# New GnuTLS "datum" with the OpenPGP public key
397
datum = gnutls.library.types.gnutls_datum_t\
398
(ctypes.cast(ctypes.c_char_p(openpgp),
399
ctypes.POINTER(ctypes.c_ubyte)),
400
ctypes.c_uint(len(openpgp)))
401
# New empty GnuTLS certificate
402
crt = gnutls.library.types.gnutls_openpgp_crt_t()
403
gnutls.library.functions.gnutls_openpgp_crt_init\
405
# Import the OpenPGP public key into the certificate
406
gnutls.library.functions.gnutls_openpgp_crt_import\
407
(crt, ctypes.byref(datum),
408
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
409
# New buffer for the fingerprint
410
buffer = ctypes.create_string_buffer(20)
411
buffer_length = ctypes.c_size_t()
412
# Get the fingerprint from the certificate into the buffer
413
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
414
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
415
# Deinit the certificate
416
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
417
# Convert the buffer to a Python bytestring
418
fpr = ctypes.string_at(buffer, buffer_length.value)
419
# Convert the bytestring to hexadecimal notation
420
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
424
class tcp_handler(SocketServer.BaseRequestHandler, object):
425
"""A TCP request handler class.
426
Instantiated by IPv6_TCPServer for each request to handle it.
1397
427
Note: This will run in its own forked process."""
1399
429
def handle(self):
1400
with contextlib.closing(self.server.child_pipe) as child_pipe:
1401
logger.info("TCP connection from: %s",
1402
unicode(self.client_address))
1403
logger.debug("Pipe FD: %d",
1404
self.server.child_pipe.fileno())
1406
session = (gnutls.connection
1407
.ClientSession(self.request,
1409
.X509Credentials()))
1411
# Note: gnutls.connection.X509Credentials is really a
1412
# generic GnuTLS certificate credentials object so long as
1413
# no X.509 keys are added to it. Therefore, we can use it
1414
# here despite using OpenPGP certificates.
1416
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1417
# "+AES-256-CBC", "+SHA1",
1418
# "+COMP-NULL", "+CTYPE-OPENPGP",
1420
# Use a fallback default, since this MUST be set.
1421
priority = self.server.gnutls_priority
1422
if priority is None:
1424
(gnutls.library.functions
1425
.gnutls_priority_set_direct(session._c_object,
1428
# Start communication using the Mandos protocol
1429
# Get protocol number
1430
line = self.request.makefile().readline()
1431
logger.debug("Protocol version: %r", line)
1433
if int(line.strip().split()[0]) > 1:
1435
except (ValueError, IndexError, RuntimeError) as error:
1436
logger.error("Unknown protocol version: %s", error)
1439
# Start GnuTLS connection
1442
except gnutls.errors.GNUTLSError as error:
1443
logger.warning("Handshake failed: %s", error)
1444
# Do not run session.bye() here: the session is not
1445
# established. Just abandon the request.
1447
logger.debug("Handshake succeeded")
1449
approval_required = False
1452
fpr = self.fingerprint(self.peer_certificate
1455
gnutls.errors.GNUTLSError) as error:
1456
logger.warning("Bad certificate: %s", error)
1458
logger.debug("Fingerprint: %s", fpr)
1459
if self.server.use_dbus:
1461
client.NewRequest(str(self.client_address))
1464
client = ProxyClient(child_pipe, fpr,
1465
self.client_address)
1469
if client.approval_delay:
1470
delay = client.approval_delay
1471
client.approvals_pending += 1
1472
approval_required = True
1475
if not client.enabled:
1476
logger.info("Client %s is disabled",
1478
if self.server.use_dbus:
1480
client.Rejected("Disabled")
1483
if client._approved or not client.approval_delay:
1484
#We are approved or approval is disabled
1486
elif client._approved is None:
1487
logger.info("Client %s needs approval",
1489
if self.server.use_dbus:
1491
client.NeedApproval(
1492
client.approval_delay_milliseconds(),
1493
client.approved_by_default)
1495
logger.warning("Client %s was not approved",
1497
if self.server.use_dbus:
1499
client.Rejected("Denied")
1502
#wait until timeout or approved
1503
time = datetime.datetime.now()
1504
client.changedstate.acquire()
1505
(client.changedstate.wait
1506
(float(client._timedelta_to_milliseconds(delay)
1508
client.changedstate.release()
1509
time2 = datetime.datetime.now()
1510
if (time2 - time) >= delay:
1511
if not client.approved_by_default:
1512
logger.warning("Client %s timed out while"
1513
" waiting for approval",
1515
if self.server.use_dbus:
1517
client.Rejected("Approval timed out")
1522
delay -= time2 - time
1525
while sent_size < len(client.secret):
1527
sent = session.send(client.secret[sent_size:])
1528
except gnutls.errors.GNUTLSError as error:
1529
logger.warning("gnutls send failed")
1531
logger.debug("Sent: %d, remaining: %d",
1532
sent, len(client.secret)
1533
- (sent_size + sent))
1536
logger.info("Sending secret to %s", client.name)
1537
# bump the timeout using extended_timeout
1538
client.checked_ok(client.extended_timeout)
1539
if self.server.use_dbus:
1544
if approval_required:
1545
client.approvals_pending -= 1
1548
except gnutls.errors.GNUTLSError as error:
1549
logger.warning("GnuTLS bye failed")
1552
def peer_certificate(session):
1553
"Return the peer's OpenPGP certificate as a bytestring"
1554
# If not an OpenPGP certificate...
1555
if (gnutls.library.functions
1556
.gnutls_certificate_type_get(session._c_object)
1557
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1558
# ...do the normal thing
1559
return session.peer_certificate
1560
list_size = ctypes.c_uint(1)
1561
cert_list = (gnutls.library.functions
1562
.gnutls_certificate_get_peers
1563
(session._c_object, ctypes.byref(list_size)))
1564
if not bool(cert_list) and list_size.value != 0:
1565
raise gnutls.errors.GNUTLSError("error getting peer"
1567
if list_size.value == 0:
1570
return ctypes.string_at(cert.data, cert.size)
1573
def fingerprint(openpgp):
1574
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1575
# New GnuTLS "datum" with the OpenPGP public key
1576
datum = (gnutls.library.types
1577
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1580
ctypes.c_uint(len(openpgp))))
1581
# New empty GnuTLS certificate
1582
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1583
(gnutls.library.functions
1584
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1585
# Import the OpenPGP public key into the certificate
1586
(gnutls.library.functions
1587
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1588
gnutls.library.constants
1589
.GNUTLS_OPENPGP_FMT_RAW))
1590
# Verify the self signature in the key
1591
crtverify = ctypes.c_uint()
1592
(gnutls.library.functions
1593
.gnutls_openpgp_crt_verify_self(crt, 0,
1594
ctypes.byref(crtverify)))
1595
if crtverify.value != 0:
1596
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1597
raise (gnutls.errors.CertificateSecurityError
1599
# New buffer for the fingerprint
1600
buf = ctypes.create_string_buffer(20)
1601
buf_len = ctypes.c_size_t()
1602
# Get the fingerprint from the certificate into the buffer
1603
(gnutls.library.functions
1604
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1605
ctypes.byref(buf_len)))
1606
# Deinit the certificate
1607
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1608
# Convert the buffer to a Python bytestring
1609
fpr = ctypes.string_at(buf, buf_len.value)
1610
# Convert the bytestring to hexadecimal notation
1611
hex_fpr = ''.join("%02X" % ord(char) for char in fpr)
1615
class MultiprocessingMixIn(object):
1616
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1617
def sub_process_main(self, request, address):
1619
self.finish_request(request, address)
1621
self.handle_error(request, address)
1622
self.close_request(request)
1624
def process_request(self, request, address):
1625
"""Start a new process to process the request."""
1626
proc = multiprocessing.Process(target = self.sub_process_main,
1633
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1634
""" adds a pipe to the MixIn """
1635
def process_request(self, request, client_address):
1636
"""Overrides and wraps the original process_request().
1638
This function creates a new pipe in self.pipe
1640
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1642
proc = MultiprocessingMixIn.process_request(self, request,
1644
self.child_pipe.close()
1645
self.add_pipe(parent_pipe, proc)
1647
def add_pipe(self, parent_pipe, proc):
1648
"""Dummy function; override as necessary"""
1649
raise NotImplementedError
1652
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1653
socketserver.TCPServer, object):
1654
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
430
logger.info(u"TCP connection from: %s",
431
unicode(self.client_address))
432
session = gnutls.connection.ClientSession\
433
(self.request, gnutls.connection.X509Credentials())
435
line = self.request.makefile().readline()
436
logger.debug(u"Protocol version: %r", line)
438
if int(line.strip().split()[0]) > 1:
440
except (ValueError, IndexError, RuntimeError), error:
441
logger.error(u"Unknown protocol version: %s", error)
444
# Note: gnutls.connection.X509Credentials is really a generic
445
# GnuTLS certificate credentials object so long as no X.509
446
# keys are added to it. Therefore, we can use it here despite
447
# using OpenPGP certificates.
449
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
450
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
452
priority = "NORMAL" # Fallback default, since this
454
if self.server.settings["priority"]:
455
priority = self.server.settings["priority"]
456
gnutls.library.functions.gnutls_priority_set_direct\
457
(session._c_object, priority, None);
461
except gnutls.errors.GNUTLSError, error:
462
logger.warning(u"Handshake failed: %s", error)
463
# Do not run session.bye() here: the session is not
464
# established. Just abandon the request.
467
fpr = fingerprint(peer_certificate(session))
468
except (TypeError, gnutls.errors.GNUTLSError), error:
469
logger.warning(u"Bad certificate: %s", error)
472
logger.debug(u"Fingerprint: %s", fpr)
474
for c in self.server.clients:
475
if c.fingerprint == fpr:
479
logger.warning(u"Client not found for fingerprint: %s",
483
# Have to check if client.still_valid(), since it is possible
484
# that the client timed out while establishing the GnuTLS
486
if not client.still_valid():
487
logger.warning(u"Client %(name)s is invalid",
492
while sent_size < len(client.secret):
493
sent = session.send(client.secret[sent_size:])
494
logger.debug(u"Sent: %d, remaining: %d",
495
sent, len(client.secret)
496
- (sent_size + sent))
501
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
502
"""IPv6 TCP server. Accepts 'None' as address and/or port.
1657
enabled: Boolean; whether this server is activated yet
1658
interface: None or a network interface name (string)
1659
use_ipv6: Boolean; to use IPv6 or not
504
settings: Server settings
505
clients: Set() of Client objects
1661
def __init__(self, server_address, RequestHandlerClass,
1662
interface=None, use_ipv6=True):
1663
self.interface = interface
1665
self.address_family = socket.AF_INET6
1666
socketserver.TCPServer.__init__(self, server_address,
1667
RequestHandlerClass)
507
address_family = socket.AF_INET6
508
def __init__(self, *args, **kwargs):
509
if "settings" in kwargs:
510
self.settings = kwargs["settings"]
511
del kwargs["settings"]
512
if "clients" in kwargs:
513
self.clients = kwargs["clients"]
514
del kwargs["clients"]
515
return super(type(self), self).__init__(*args, **kwargs)
1668
516
def server_bind(self):
1669
517
"""This overrides the normal server_bind() function
1670
518
to bind to an interface if one was specified, and also NOT to
1671
519
bind to an address or port if they were not specified."""
1672
if self.interface is not None:
1673
if SO_BINDTODEVICE is None:
1674
logger.error("SO_BINDTODEVICE does not exist;"
1675
" cannot bind to interface %s",
1679
self.socket.setsockopt(socket.SOL_SOCKET,
1683
except socket.error as error:
1684
if error[0] == errno.EPERM:
1685
logger.error("No permission to"
1686
" bind to interface %s",
1688
elif error[0] == errno.ENOPROTOOPT:
1689
logger.error("SO_BINDTODEVICE not available;"
1690
" cannot bind to interface %s",
520
if self.settings["interface"]:
521
# 25 is from /usr/include/asm-i486/socket.h
522
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
524
self.socket.setsockopt(socket.SOL_SOCKET,
526
self.settings["interface"])
527
except socket.error, error:
528
if error[0] == errno.EPERM:
529
logger.error(u"No permission to"
530
u" bind to interface %s",
531
self.settings["interface"])
1694
534
# Only bind(2) the socket if we really need to.
1695
535
if self.server_address[0] or self.server_address[1]:
1696
536
if not self.server_address[0]:
1697
if self.address_family == socket.AF_INET6:
1698
any_address = "::" # in6addr_any
1700
any_address = socket.INADDR_ANY
1701
self.server_address = (any_address,
538
self.server_address = (in6addr_any,
1702
539
self.server_address[1])
1703
540
elif not self.server_address[1]:
1704
541
self.server_address = (self.server_address[0],
1706
# if self.interface:
543
# if self.settings["interface"]:
1707
544
# self.server_address = (self.server_address[0],
1710
547
# if_nametoindex
1712
return socketserver.TCPServer.server_bind(self)
1715
class MandosServer(IPv6_TCPServer):
1719
clients: set of Client objects
1720
gnutls_priority GnuTLS priority string
1721
use_dbus: Boolean; to emit D-Bus signals or not
1723
Assumes a gobject.MainLoop event loop.
1725
def __init__(self, server_address, RequestHandlerClass,
1726
interface=None, use_ipv6=True, clients=None,
1727
gnutls_priority=None, use_dbus=True):
1728
self.enabled = False
1729
self.clients = clients
1730
if self.clients is None:
1732
self.use_dbus = use_dbus
1733
self.gnutls_priority = gnutls_priority
1734
IPv6_TCPServer.__init__(self, server_address,
1735
RequestHandlerClass,
1736
interface = interface,
1737
use_ipv6 = use_ipv6)
1738
def server_activate(self):
1740
return socketserver.TCPServer.server_activate(self)
1745
def add_pipe(self, parent_pipe, proc):
1746
# Call "handle_ipc" for both data and EOF events
1747
gobject.io_add_watch(parent_pipe.fileno(),
1748
gobject.IO_IN | gobject.IO_HUP,
1749
functools.partial(self.handle_ipc,
1754
def handle_ipc(self, source, condition, parent_pipe=None,
1755
proc = None, client_object=None):
1757
gobject.IO_IN: "IN", # There is data to read.
1758
gobject.IO_OUT: "OUT", # Data can be written (without
1760
gobject.IO_PRI: "PRI", # There is urgent data to read.
1761
gobject.IO_ERR: "ERR", # Error condition.
1762
gobject.IO_HUP: "HUP" # Hung up (the connection has been
1763
# broken, usually for pipes and
1766
conditions_string = ' | '.join(name
1768
condition_names.iteritems()
1769
if cond & condition)
1770
# error, or the other end of multiprocessing.Pipe has closed
1771
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1772
# Wait for other process to exit
1776
# Read a request from the child
1777
request = parent_pipe.recv()
1778
command = request[0]
1780
if command == 'init':
1782
address = request[2]
1784
for c in self.clients.itervalues():
1785
if c.fingerprint == fpr:
1789
logger.info("Client not found for fingerprint: %s, ad"
1790
"dress: %s", fpr, address)
1793
mandos_dbus_service.ClientNotFound(fpr,
1795
parent_pipe.send(False)
1798
gobject.io_add_watch(parent_pipe.fileno(),
1799
gobject.IO_IN | gobject.IO_HUP,
1800
functools.partial(self.handle_ipc,
1806
parent_pipe.send(True)
1807
# remove the old hook in favor of the new above hook on
1810
if command == 'funcall':
1811
funcname = request[1]
1815
parent_pipe.send(('data', getattr(client_object,
1819
if command == 'getattr':
1820
attrname = request[1]
1821
if callable(client_object.__getattribute__(attrname)):
1822
parent_pipe.send(('function',))
1824
parent_pipe.send(('data', client_object
1825
.__getattribute__(attrname)))
1827
if command == 'setattr':
1828
attrname = request[1]
1830
setattr(client_object, attrname, value)
550
return super(type(self), self).server_bind()
1835
553
def string_to_delta(interval):
1836
554
"""Parse a string and return a datetime.timedelta
1838
556
>>> string_to_delta('7d')
1839
557
datetime.timedelta(7)
1840
558
>>> string_to_delta('60s')
1951
695
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1952
696
"servicename": "Mandos",
1958
699
# Parse config file for server-global settings
1959
server_config = configparser.SafeConfigParser(server_defaults)
700
server_config = ConfigParser.SafeConfigParser(server_defaults)
1960
701
del server_defaults
1961
server_config.read(os.path.join(options.configdir,
702
server_config.read(os.path.join(options.configdir, "mandos.conf"))
703
server_section = "server"
1963
704
# Convert the SafeConfigParser object to a dict
1964
server_settings = server_config.defaults()
1965
# Use the appropriate methods on the non-string config options
1966
for option in ("debug", "use_dbus", "use_ipv6"):
1967
server_settings[option] = server_config.getboolean("DEFAULT",
1969
if server_settings["port"]:
1970
server_settings["port"] = server_config.getint("DEFAULT",
705
server_settings = dict(server_config.items(server_section))
706
# Use getboolean on the boolean config option
707
server_settings["debug"] = server_config.getboolean\
708
(server_section, "debug")
1972
709
del server_config
1974
711
# Override the settings from the config file with command line
1975
712
# options, if set.
1976
713
for option in ("interface", "address", "port", "debug",
1977
"priority", "servicename", "configdir",
1978
"use_dbus", "use_ipv6", "debuglevel", "restore"):
714
"priority", "servicename", "configdir"):
1979
715
value = getattr(options, option)
1980
716
if value is not None:
1981
717
server_settings[option] = value
1983
# Force all strings to be unicode
1984
for option in server_settings.keys():
1985
if type(server_settings[option]) is str:
1986
server_settings[option] = unicode(server_settings[option])
1987
719
# Now we have our good server settings in "server_settings"
1989
##################################################################
1992
721
debug = server_settings["debug"]
1993
debuglevel = server_settings["debuglevel"]
1994
use_dbus = server_settings["use_dbus"]
1995
use_ipv6 = server_settings["use_ipv6"]
1998
initlogger(logging.DEBUG)
2003
level = getattr(logging, debuglevel.upper())
724
syslogger.setLevel(logging.WARNING)
725
console.setLevel(logging.WARNING)
2006
727
if server_settings["servicename"] != "Mandos":
2007
syslogger.setFormatter(logging.Formatter
2008
('Mandos (%s) [%%(process)d]:'
2009
' %%(levelname)s: %%(message)s'
728
syslogger.setFormatter(logging.Formatter\
729
('Mandos (%s): %%(levelname)s:'
2010
731
% server_settings["servicename"]))
2012
733
# Parse config file with clients
2013
client_defaults = { "timeout": "5m",
2014
"extended_timeout": "15m",
734
client_defaults = { "timeout": "1h",
2016
736
"checker": "fping -q -- %%(host)s",
2018
"approval_delay": "0s",
2019
"approval_duration": "1s",
2021
client_config = configparser.SafeConfigParser(client_defaults)
738
client_config = ConfigParser.SafeConfigParser(client_defaults)
2022
739
client_config.read(os.path.join(server_settings["configdir"],
2023
740
"clients.conf"))
2025
global mandos_dbus_service
2026
mandos_dbus_service = None
2028
tcp_server = MandosServer((server_settings["address"],
2029
server_settings["port"]),
2031
interface=(server_settings["interface"]
2035
server_settings["priority"],
2038
pidfilename = "/var/run/mandos.pid"
2040
pidfile = open(pidfilename, "w")
2042
logger.error("Could not open file %r", pidfilename)
2045
uid = pwd.getpwnam("_mandos").pw_uid
2046
gid = pwd.getpwnam("_mandos").pw_gid
2049
uid = pwd.getpwnam("mandos").pw_uid
2050
gid = pwd.getpwnam("mandos").pw_gid
2053
uid = pwd.getpwnam("nobody").pw_uid
2054
gid = pwd.getpwnam("nobody").pw_gid
2061
except OSError as error:
2062
if error[0] != errno.EPERM:
2066
# Enable all possible GnuTLS debugging
2068
# "Use a log level over 10 to enable all debugging options."
2070
gnutls.library.functions.gnutls_global_set_log_level(11)
2072
@gnutls.library.types.gnutls_log_func
2073
def debug_gnutls(level, string):
2074
logger.debug("GnuTLS: %s", string[:-1])
2076
(gnutls.library.functions
2077
.gnutls_global_set_log_function(debug_gnutls))
2079
# Redirect stdin so all checkers get /dev/null
2080
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2081
os.dup2(null, sys.stdin.fileno())
2085
# No console logging
2086
logger.removeHandler(console)
2088
# Need to fork before connecting to D-Bus
2090
# Close all input and output, do double fork, etc.
743
service = AvahiService(name = server_settings["servicename"],
744
type = "_mandos._tcp", );
745
if server_settings["interface"]:
746
service.interface = if_nametoindex(server_settings["interface"])
2093
748
global main_loop
2094
751
# From the Avahi example code
2095
752
DBusGMainLoop(set_as_default=True )
2096
753
main_loop = gobject.MainLoop()
2097
754
bus = dbus.SystemBus()
755
server = dbus.Interface(
756
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
757
avahi.DBUS_INTERFACE_SERVER )
2098
758
# End of Avahi example code
2101
bus_name = dbus.service.BusName("se.recompile.Mandos",
2102
bus, do_not_queue=True)
2103
old_bus_name = (dbus.service.BusName
2104
("se.bsnet.fukt.Mandos", bus,
2106
except dbus.exceptions.NameExistsException as e:
2107
logger.error(unicode(e) + ", disabling D-Bus")
2109
server_settings["use_dbus"] = False
2110
tcp_server.use_dbus = False
2111
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2112
service = AvahiServiceToSyslog(name =
2113
server_settings["servicename"],
2114
servicetype = "_mandos._tcp",
2115
protocol = protocol, bus = bus)
2116
if server_settings["interface"]:
2117
service.interface = (if_nametoindex
2118
(str(server_settings["interface"])))
2120
global multiprocessing_manager
2121
multiprocessing_manager = multiprocessing.Manager()
2123
client_class = Client
2125
client_class = functools.partial(ClientDBusTransitional,
2128
special_settings = {
2129
# Some settings need to be accessd by special methods;
2130
# booleans need .getboolean(), etc. Here is a list of them:
2131
"approved_by_default":
2133
client_config.getboolean(section, "approved_by_default"),
2135
# Construct a new dict of client settings of this form:
2136
# { client_name: {setting_name: value, ...}, ...}
2137
# with exceptions for any special settings as defined above
2138
client_settings = dict((clientname,
2140
(value if setting not in special_settings
2141
else special_settings[setting](clientname)))
2142
for setting, value in client_config.items(clientname)))
2143
for clientname in client_config.sections())
2145
old_client_settings = {}
2148
# Get client data and settings from last running state.
2149
if server_settings["restore"]:
2151
with open(stored_state_path, "rb") as stored_state:
2152
clients_data, old_client_settings = pickle.load(stored_state)
2153
os.remove(stored_state_path)
2154
except IOError as e:
2155
logger.warning("Could not load persistant state: {0}".format(e))
2156
if e.errno != errno.ENOENT:
2159
for client in clients_data:
2160
client_name = client["name"]
2162
# Decide which value to use after restoring saved state.
2163
# We have three different values: Old config file,
2164
# new config file, and saved state.
2165
# New config value takes precedence if it differs from old
2166
# config value, otherwise use saved state.
2167
for name, value in client_settings[client_name].items():
2169
# For each value in new config, check if it differs
2170
# from the old config value (Except for the "secret"
2172
if name != "secret" and value != old_client_settings[client_name][name]:
2173
setattr(client, name, value)
2177
# Clients who has passed its expire date, can still be enabled if its
2178
# last checker was sucessful. Clients who checkers failed before we
2179
# stored it state is asumed to had failed checker during downtime.
2180
if client["enabled"] and client["last_checked_ok"]:
2181
if ((datetime.datetime.utcnow() - client["last_checked_ok"])
2182
> client["interval"]):
2183
if client["last_checker_status"] != 0:
2184
client["enabled"] = False
2186
client["expires"] = datetime.datetime.utcnow() + client["timeout"]
2188
client["changedstate"] = (multiprocessing_manager
2189
.Condition(multiprocessing_manager
2192
new_client = ClientDBusTransitional.__new__(ClientDBusTransitional)
2193
tcp_server.clients[client_name] = new_client
2194
new_client.bus = bus
2195
for name, value in client.iteritems():
2196
setattr(new_client, name, value)
2197
client_object_name = unicode(client_name).translate(
2198
{ord("."): ord("_"),
2199
ord("-"): ord("_")})
2200
new_client.dbus_object_path = (dbus.ObjectPath
2201
("/clients/" + client_object_name))
2202
DBusObjectWithProperties.__init__(new_client,
2204
new_client.dbus_object_path)
2206
tcp_server.clients[client_name] = Client.__new__(Client)
2207
for name, value in client.iteritems():
2208
setattr(tcp_server.clients[client_name], name, value)
2210
tcp_server.clients[client_name].decrypt_secret(
2211
client_settings[client_name]["secret"])
2213
# Create/remove clients based on new changes made to config
2214
for clientname in set(old_client_settings) - set(client_settings):
2215
del tcp_server.clients[clientname]
2216
for clientname in set(client_settings) - set(old_client_settings):
2217
tcp_server.clients[clientname] = (client_class(name = clientname,
2223
if not tcp_server.clients:
2224
logger.warning("No clients defined")
2230
pidfile.write(str(pid) + "\n".encode("utf-8"))
2233
logger.error("Could not write to file %r with PID %d",
2236
# "pidfile" was never created
761
def remove_from_clients(client):
762
clients.remove(client)
764
logger.critical(u"No clients left, exiting")
767
clients.update(Set(Client(name = section,
768
stop_hook = remove_from_clients,
770
= dict(client_config.items(section)))
771
for section in client_config.sections()))
773
logger.critical(u"No clients defined")
777
logger.removeHandler(console)
780
pidfilename = "/var/run/mandos/mandos.pid"
783
pidfile = open(pidfilename, "w")
784
pidfile.write(str(pid) + "\n")
788
logger.error(u"Could not write %s file with PID %d",
789
pidfilename, os.getpid())
792
"Cleanup function; run on exit"
794
# From the Avahi example code
795
if not group is None:
798
# End of Avahi example code
801
client = clients.pop()
802
client.stop_hook = None
805
atexit.register(cleanup)
2240
808
signal.signal(signal.SIGINT, signal.SIG_IGN)
2242
809
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2243
810
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2246
class MandosDBusService(dbus.service.Object):
2247
"""A D-Bus proxy object"""
2249
dbus.service.Object.__init__(self, bus, "/")
2250
_interface = "se.recompile.Mandos"
2252
@dbus.service.signal(_interface, signature="o")
2253
def ClientAdded(self, objpath):
2257
@dbus.service.signal(_interface, signature="ss")
2258
def ClientNotFound(self, fingerprint, address):
2262
@dbus.service.signal(_interface, signature="os")
2263
def ClientRemoved(self, objpath, name):
2267
@dbus.service.method(_interface, out_signature="ao")
2268
def GetAllClients(self):
2270
return dbus.Array(c.dbus_object_path
2272
tcp_server.clients.itervalues())
2274
@dbus.service.method(_interface,
2275
out_signature="a{oa{sv}}")
2276
def GetAllClientsWithProperties(self):
2278
return dbus.Dictionary(
2279
((c.dbus_object_path, c.GetAll(""))
2280
for c in tcp_server.clients.itervalues()),
2283
@dbus.service.method(_interface, in_signature="o")
2284
def RemoveClient(self, object_path):
2286
for c in tcp_server.clients.itervalues():
2287
if c.dbus_object_path == object_path:
2288
del tcp_server.clients[c.name]
2289
c.remove_from_connection()
2290
# Don't signal anything except ClientRemoved
2291
c.disable(quiet=True)
2293
self.ClientRemoved(object_path, c.name)
2295
raise KeyError(object_path)
2299
class MandosDBusServiceTransitional(MandosDBusService):
2300
__metaclass__ = AlternateDBusNamesMetaclass
2301
mandos_dbus_service = MandosDBusServiceTransitional()
2304
"Cleanup function; run on exit"
2307
multiprocessing.active_children()
2308
if not (tcp_server.clients or client_settings):
2311
# Store client before exiting. Secrets are encrypted with key based
2312
# on what config file has. If config file is removed/edited, old
2313
# secret will thus be unrecovable.
2315
for client in tcp_server.clients.itervalues():
2316
client.encrypt_secret(client_settings[client.name]["secret"])
2320
# A list of attributes that will not be stored when shuting down.
2321
exclude = set(("bus", "changedstate", "secret"))
2322
for name, typ in inspect.getmembers(dbus.service.Object):
2325
client_dict["encrypted_secret"] = client.encrypted_secret
2326
for attr in client.client_structure:
2327
if attr not in exclude:
2328
client_dict[attr] = getattr(client, attr)
2330
clients.append(client_dict)
2331
del client_settings[client.name]["secret"]
2334
with os.fdopen(os.open(stored_state_path, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0600), "wb") as stored_state:
2335
pickle.dump((clients, client_settings), stored_state)
2336
except IOError as e:
2337
logger.warning("Could not save persistant state: {0}".format(e))
2338
if e.errno != errno.ENOENT:
2341
# Delete all clients, and settings from config
2342
while tcp_server.clients:
2343
name, client = tcp_server.clients.popitem()
2345
client.remove_from_connection()
2346
# Don't signal anything except ClientRemoved
2347
client.disable(quiet=True)
2350
mandos_dbus_service.ClientRemoved(client
2353
client_settings.clear()
2355
atexit.register(cleanup)
2357
for client in tcp_server.clients.itervalues():
2360
mandos_dbus_service.ClientAdded(client.dbus_object_path)
2361
# Need to initiate checking of clients
2363
client.init_checker()
2367
tcp_server.server_activate()
812
for client in clients:
815
tcp_server = IPv6_TCPServer((server_settings["address"],
816
server_settings["port"]),
818
settings=server_settings,
2369
820
# Find out what port we got
2370
821
service.port = tcp_server.socket.getsockname()[1]
2372
logger.info("Now listening on address %r, port %d,"
2373
" flowinfo %d, scope_id %d"
2374
% tcp_server.socket.getsockname())
2376
logger.info("Now listening on address %r, port %d"
2377
% tcp_server.socket.getsockname())
822
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
823
u" scope_id %d" % tcp_server.socket.getsockname())
2379
825
#service.interface = tcp_server.socket.getsockname()[3]
2382
828
# From the Avahi example code
829
server.connect_to_signal("StateChanged", server_state_changed)
2385
except dbus.exceptions.DBusException as error:
2386
logger.critical("DBusException: %s", error)
831
server_state_changed(server.GetState())
832
except dbus.exceptions.DBusException, error:
833
logger.critical(u"DBusException: %s", error)
2389
835
# End of Avahi example code
2391
837
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
2392
838
lambda *args, **kwargs:
2393
(tcp_server.handle_request
2394
(*args[2:], **kwargs) or True))
839
tcp_server.handle_request\
840
(*args[2:], **kwargs) or True)
2396
logger.debug("Starting main loop")
842
logger.debug(u"Starting main loop")
843
main_loop_started = True
2398
except AvahiError as error:
2399
logger.critical("AvahiError: %s", error)
845
except AvahiError, error:
846
logger.critical(u"AvahiError: %s" + unicode(error))
2402
848
except KeyboardInterrupt:
2404
print("", file=sys.stderr)
2405
logger.debug("Server received KeyboardInterrupt")
2406
logger.debug("Server exiting")
2407
# Must run before the D-Bus bus name gets deregistered
2411
852
if __name__ == '__main__':