100
136
max_renames: integer; maximum number of renames
101
137
rename_count: integer; counter so we only rename after collisions
102
138
a sensible number of times
139
group: D-Bus Entry Group
141
bus: dbus.SystemBus()
104
143
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
105
type = None, port = None, TXT = None, domain = "",
106
host = "", max_renames = 32768):
144
servicetype = None, port = None, TXT = None,
145
domain = "", host = "", max_renames = 32768,
146
protocol = avahi.PROTO_UNSPEC, bus = None):
107
147
self.interface = interface
149
self.type = servicetype
151
self.TXT = TXT if TXT is not None else []
115
152
self.domain = domain
117
154
self.rename_count = 0
155
self.max_renames = max_renames
156
self.protocol = protocol
157
self.group = None # our entry group
160
self.entry_group_state_changed_match = None
118
161
def rename(self):
119
162
"""Derived from the Avahi example code"""
120
163
if self.rename_count >= self.max_renames:
121
logger.critical(u"No suitable service name found after %i"
122
u" retries, exiting.", rename_count)
164
logger.critical("No suitable Zeroconf service name found"
165
" after %i retries, exiting.",
123
167
raise AvahiServiceError("Too many renames")
124
name = server.GetAlternativeServiceName(name)
125
logger.error(u"Changing name to %r ...", name)
126
syslogger.setFormatter(logging.Formatter\
127
('Mandos (%s): %%(levelname)s:'
128
' %%(message)s' % name))
168
self.name = unicode(self.server
169
.GetAlternativeServiceName(self.name))
170
logger.info("Changing Zeroconf service name to %r ...",
175
except dbus.exceptions.DBusException as error:
176
logger.critical("DBusException: %s", error)
131
179
self.rename_count += 1
132
180
def remove(self):
133
181
"""Derived from the Avahi example code"""
134
if group is not None:
182
if self.entry_group_state_changed_match is not None:
183
self.entry_group_state_changed_match.remove()
184
self.entry_group_state_changed_match = None
185
if self.group is not None:
137
188
"""Derived from the Avahi example code"""
140
group = dbus.Interface\
141
(bus.get_object(avahi.DBUS_NAME,
142
server.EntryGroupNew()),
143
avahi.DBUS_INTERFACE_ENTRY_GROUP)
144
group.connect_to_signal('StateChanged',
145
entry_group_state_changed)
146
logger.debug(u"Adding service '%s' of type '%s' ...",
147
service.name, service.type)
149
self.interface, # interface
150
avahi.PROTO_INET6, # protocol
151
dbus.UInt32(0), # flags
152
self.name, self.type,
153
self.domain, self.host,
154
dbus.UInt16(self.port),
155
avahi.string_array_to_txt_array(self.TXT))
158
# From the Avahi example code:
159
group = None # our entry group
160
# End of Avahi example code
190
if self.group is None:
191
self.group = dbus.Interface(
192
self.bus.get_object(avahi.DBUS_NAME,
193
self.server.EntryGroupNew()),
194
avahi.DBUS_INTERFACE_ENTRY_GROUP)
195
self.entry_group_state_changed_match = (
196
self.group.connect_to_signal(
197
'StateChanged', self.entry_group_state_changed))
198
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
199
self.name, self.type)
200
self.group.AddService(
203
dbus.UInt32(0), # flags
204
self.name, self.type,
205
self.domain, self.host,
206
dbus.UInt16(self.port),
207
avahi.string_array_to_txt_array(self.TXT))
209
def entry_group_state_changed(self, state, error):
210
"""Derived from the Avahi example code"""
211
logger.debug("Avahi entry group state change: %i", state)
213
if state == avahi.ENTRY_GROUP_ESTABLISHED:
214
logger.debug("Zeroconf service established.")
215
elif state == avahi.ENTRY_GROUP_COLLISION:
216
logger.info("Zeroconf service name collision.")
218
elif state == avahi.ENTRY_GROUP_FAILURE:
219
logger.critical("Avahi: Error in group state changed %s",
221
raise AvahiGroupError("State changed: %s"
224
"""Derived from the Avahi example code"""
225
if self.group is not None:
228
except (dbus.exceptions.UnknownMethodException,
229
dbus.exceptions.DBusException) as e:
233
def server_state_changed(self, state, error=None):
234
"""Derived from the Avahi example code"""
235
logger.debug("Avahi server state change: %i", state)
236
bad_states = { avahi.SERVER_INVALID:
237
"Zeroconf server invalid",
238
avahi.SERVER_REGISTERING: None,
239
avahi.SERVER_COLLISION:
240
"Zeroconf server name collision",
241
avahi.SERVER_FAILURE:
242
"Zeroconf server failure" }
243
if state in bad_states:
244
if bad_states[state] is not None:
246
logger.error(bad_states[state])
248
logger.error(bad_states[state] + ": %r", error)
250
elif state == avahi.SERVER_RUNNING:
254
logger.debug("Unknown state: %r", state)
256
logger.debug("Unknown state: %r: %r", state, error)
258
"""Derived from the Avahi example code"""
259
if self.server is None:
260
self.server = dbus.Interface(
261
self.bus.get_object(avahi.DBUS_NAME,
262
avahi.DBUS_PATH_SERVER,
263
follow_name_owner_changes=True),
264
avahi.DBUS_INTERFACE_SERVER)
265
self.server.connect_to_signal("StateChanged",
266
self.server_state_changed)
267
self.server_state_changed(self.server.GetState())
269
class AvahiServiceToSyslog(AvahiService):
271
"""Add the new name to the syslog messages"""
272
ret = AvahiService.rename(self)
273
syslogger.setFormatter(logging.Formatter
274
('Mandos (%s) [%%(process)d]:'
275
' %%(levelname)s: %%(message)s'
279
def _timedelta_to_milliseconds(td):
280
"Convert a datetime.timedelta() to milliseconds"
281
return ((td.days * 24 * 60 * 60 * 1000)
282
+ (td.seconds * 1000)
283
+ (td.microseconds // 1000))
163
285
class Client(object):
164
286
"""A representation of a client host served by this server.
166
name: string; from the config file, used in log messages
289
_approved: bool(); 'None' if not yet approved/disapproved
290
approval_delay: datetime.timedelta(); Time to wait for approval
291
approval_duration: datetime.timedelta(); Duration of one approval
292
checker: subprocess.Popen(); a running checker process used
293
to see if the client lives.
294
'None' if no process is running.
295
checker_callback_tag: a gobject event source tag, or None
296
checker_command: string; External command which is run to check
297
if client lives. %() expansions are done at
298
runtime with vars(self) as dict, so that for
299
instance %(name)s can be used in the command.
300
checker_initiator_tag: a gobject event source tag, or None
301
created: datetime.datetime(); (UTC) object creation
302
client_structure: Object describing what attributes a client has
303
and is used for storing the client at exit
304
current_checker_command: string; current running checker_command
305
disable_initiator_tag: a gobject event source tag, or None
167
307
fingerprint: string (40 or 32 hexadecimal digits); used to
168
308
uniquely identify the client
169
secret: bytestring; sent verbatim (over TLS) to client
170
host: string; available for use by the checker command
171
created: datetime.datetime(); object creation, not client host
172
last_checked_ok: datetime.datetime() or None if not yet checked OK
173
timeout: datetime.timedelta(); How long from last_checked_ok
174
until this client is invalid
175
interval: datetime.timedelta(); How often to start a new checker
176
stop_hook: If set, called by stop() as stop_hook(self)
177
checker: subprocess.Popen(); a running checker process used
178
to see if the client lives.
179
'None' if no process is running.
180
checker_initiator_tag: a gobject event source tag, or None
181
stop_initiator_tag: - '' -
182
checker_callback_tag: - '' -
183
checker_command: string; External command which is run to check if
184
client lives. %() expansions are done at
185
runtime with vars(self) as dict, so that for
186
instance %(name)s can be used in the command.
188
_timeout: Real variable for 'timeout'
189
_interval: Real variable for 'interval'
190
_timeout_milliseconds: Used when calling gobject.timeout_add()
191
_interval_milliseconds: - '' -
309
host: string; available for use by the checker command
310
interval: datetime.timedelta(); How often to start a new checker
311
last_approval_request: datetime.datetime(); (UTC) or None
312
last_checked_ok: datetime.datetime(); (UTC) or None
313
last_checker_status: integer between 0 and 255 reflecting exit status
314
of last checker. -1 reflect crashed checker,
316
last_enabled: datetime.datetime(); (UTC)
317
name: string; from the config file, used in log messages and
319
secret: bytestring; sent verbatim (over TLS) to client
320
timeout: datetime.timedelta(); How long from last_checked_ok
321
until this client is disabled
322
extended_timeout: extra long timeout when password has been sent
323
runtime_expansions: Allowed attributes for runtime expansion.
324
expires: datetime.datetime(); time (UTC) when a client will be
193
def _set_timeout(self, timeout):
194
"Setter function for 'timeout' attribute"
195
self._timeout = timeout
196
self._timeout_milliseconds = ((self.timeout.days
197
* 24 * 60 * 60 * 1000)
198
+ (self.timeout.seconds * 1000)
199
+ (self.timeout.microseconds
201
timeout = property(lambda self: self._timeout,
204
def _set_interval(self, interval):
205
"Setter function for 'interval' attribute"
206
self._interval = interval
207
self._interval_milliseconds = ((self.interval.days
208
* 24 * 60 * 60 * 1000)
209
+ (self.interval.seconds
211
+ (self.interval.microseconds
213
interval = property(lambda self: self._interval,
216
def __init__(self, name = None, stop_hook=None, config={}):
328
runtime_expansions = ("approval_delay", "approval_duration",
329
"created", "enabled", "fingerprint",
330
"host", "interval", "last_checked_ok",
331
"last_enabled", "name", "timeout")
333
def timeout_milliseconds(self):
334
"Return the 'timeout' attribute in milliseconds"
335
return _timedelta_to_milliseconds(self.timeout)
337
def extended_timeout_milliseconds(self):
338
"Return the 'extended_timeout' attribute in milliseconds"
339
return _timedelta_to_milliseconds(self.extended_timeout)
341
def interval_milliseconds(self):
342
"Return the 'interval' attribute in milliseconds"
343
return _timedelta_to_milliseconds(self.interval)
345
def approval_delay_milliseconds(self):
346
return _timedelta_to_milliseconds(self.approval_delay)
348
def __init__(self, name = None, config=None):
217
349
"""Note: the 'checker' key in 'config' sets the
218
350
'checker_command' attribute and *not* the 'checker'
221
logger.debug(u"Creating client %r", self.name)
355
logger.debug("Creating client %r", self.name)
222
356
# Uppercase and remove spaces from fingerprint for later
223
357
# comparison purposes with return value from the fingerprint()
225
self.fingerprint = config["fingerprint"].upper()\
227
logger.debug(u" Fingerprint: %s", self.fingerprint)
359
self.fingerprint = (config["fingerprint"].upper()
361
logger.debug(" Fingerprint: %s", self.fingerprint)
228
362
if "secret" in config:
229
self.secret = config["secret"].decode(u"base64")
363
self.secret = config["secret"].decode("base64")
230
364
elif "secfile" in config:
231
sf = open(config["secfile"])
232
self.secret = sf.read()
365
with open(os.path.expanduser(os.path.expandvars
366
(config["secfile"])),
368
self.secret = secfile.read()
235
raise TypeError(u"No secret or secfile for client %s"
370
raise TypeError("No secret or secfile for client %s"
237
372
self.host = config.get("host", "")
238
self.created = datetime.datetime.now()
373
self.created = datetime.datetime.utcnow()
375
self.last_approval_request = None
376
self.last_enabled = datetime.datetime.utcnow()
239
377
self.last_checked_ok = None
378
self.last_checker_status = None
240
379
self.timeout = string_to_delta(config["timeout"])
380
self.extended_timeout = string_to_delta(config
381
["extended_timeout"])
241
382
self.interval = string_to_delta(config["interval"])
242
self.stop_hook = stop_hook
243
383
self.checker = None
244
384
self.checker_initiator_tag = None
245
self.stop_initiator_tag = None
385
self.disable_initiator_tag = None
386
self.expires = datetime.datetime.utcnow() + self.timeout
246
387
self.checker_callback_tag = None
247
self.check_command = config["checker"]
388
self.checker_command = config["checker"]
389
self.current_checker_command = None
390
self._approved = None
391
self.approved_by_default = config.get("approved_by_default",
393
self.approvals_pending = 0
394
self.approval_delay = string_to_delta(
395
config["approval_delay"])
396
self.approval_duration = string_to_delta(
397
config["approval_duration"])
398
self.changedstate = (multiprocessing_manager
399
.Condition(multiprocessing_manager
401
self.client_structure = [attr for attr in self.__dict__.iterkeys() if not attr.startswith("_")]
402
self.client_structure.append("client_structure")
405
for name, t in inspect.getmembers(type(self),
406
lambda obj: isinstance(obj, property)):
407
if not name.startswith("_"):
408
self.client_structure.append(name)
410
# Send notice to process children that client state has changed
411
def send_changedstate(self):
412
with self.changedstate:
413
self.changedstate.notify_all()
249
416
"""Start this client's checker and timeout hooks"""
250
# Schedule a new checker to be started an 'interval' from now,
251
# and every interval from then on.
252
self.checker_initiator_tag = gobject.timeout_add\
253
(self._interval_milliseconds,
255
# Also start a new checker *right now*.
257
# Schedule a stop() when 'timeout' has passed
258
self.stop_initiator_tag = gobject.timeout_add\
259
(self._timeout_milliseconds,
263
The possibility that a client might be restarted is left open,
264
but not currently used."""
265
# If this client doesn't have a secret, it is already stopped.
266
if hasattr(self, "secret") and self.secret:
267
logger.info(u"Stopping client %s", self.name)
417
if getattr(self, "enabled", False):
420
self.send_changedstate()
421
self.expires = datetime.datetime.utcnow() + self.timeout
423
self.last_enabled = datetime.datetime.utcnow()
426
def disable(self, quiet=True):
427
"""Disable this client."""
428
if not getattr(self, "enabled", False):
271
if getattr(self, "stop_initiator_tag", False):
272
gobject.source_remove(self.stop_initiator_tag)
273
self.stop_initiator_tag = None
431
self.send_changedstate()
433
logger.info("Disabling client %s", self.name)
434
if getattr(self, "disable_initiator_tag", False):
435
gobject.source_remove(self.disable_initiator_tag)
436
self.disable_initiator_tag = None
274
438
if getattr(self, "checker_initiator_tag", False):
275
439
gobject.source_remove(self.checker_initiator_tag)
276
440
self.checker_initiator_tag = None
277
441
self.stop_checker()
280
443
# Do not run this again if called by a gobject.timeout_add
282
446
def __del__(self):
283
self.stop_hook = None
285
def checker_callback(self, pid, condition):
449
def init_checker(self):
450
# Schedule a new checker to be started an 'interval' from now,
451
# and every interval from then on.
452
self.checker_initiator_tag = (gobject.timeout_add
453
(self.interval_milliseconds(),
455
# Schedule a disable() when 'timeout' has passed
456
self.disable_initiator_tag = (gobject.timeout_add
457
(self.timeout_milliseconds(),
459
# Also start a new checker *right now*.
463
def checker_callback(self, pid, condition, command):
286
464
"""The checker has completed, so take appropriate actions."""
287
now = datetime.datetime.now()
288
465
self.checker_callback_tag = None
289
466
self.checker = None
290
if os.WIFEXITED(condition) \
291
and (os.WEXITSTATUS(condition) == 0):
292
logger.info(u"Checker for %(name)s succeeded",
294
self.last_checked_ok = now
295
gobject.source_remove(self.stop_initiator_tag)
296
self.stop_initiator_tag = gobject.timeout_add\
297
(self._timeout_milliseconds,
299
elif not os.WIFEXITED(condition):
300
logger.warning(u"Checker for %(name)s crashed?",
467
if os.WIFEXITED(condition):
468
self.last_checker_status = os.WEXITSTATUS(condition)
469
if self.last_checker_status == 0:
470
logger.info("Checker for %(name)s succeeded",
474
logger.info("Checker for %(name)s failed",
477
self.last_checker_status = -1
478
logger.warning("Checker for %(name)s crashed?",
303
logger.info(u"Checker for %(name)s failed",
481
def checked_ok(self, timeout=None):
482
"""Bump up the timeout for this client.
484
This should only be called when the client has been seen,
488
timeout = self.timeout
489
self.last_checked_ok = datetime.datetime.utcnow()
490
if self.disable_initiator_tag is not None:
491
gobject.source_remove(self.disable_initiator_tag)
492
if getattr(self, "enabled", False):
493
self.disable_initiator_tag = (gobject.timeout_add
494
(_timedelta_to_milliseconds
495
(timeout), self.disable))
496
self.expires = datetime.datetime.utcnow() + timeout
498
def need_approval(self):
499
self.last_approval_request = datetime.datetime.utcnow()
305
501
def start_checker(self):
306
502
"""Start a new checker subprocess if one is not running.
307
504
If a checker already exists, leave it running and do
309
506
# The reason for not killing a running checker is that if we
350
580
self.checker_callback_tag = None
351
581
if getattr(self, "checker", None) is None:
353
logger.debug(u"Stopping checker for %(name)s", vars(self))
583
logger.debug("Stopping checker for %(name)s", vars(self))
355
585
os.kill(self.checker.pid, signal.SIGTERM)
357
587
#if self.checker.poll() is None:
358
588
# os.kill(self.checker.pid, signal.SIGKILL)
359
except OSError, error:
589
except OSError as error:
360
590
if error.errno != errno.ESRCH: # No such process
362
592
self.checker = None
363
def still_valid(self):
364
"""Has the timeout not yet passed for this client?"""
365
now = datetime.datetime.now()
366
if self.last_checked_ok is None:
367
return now < (self.created + self.timeout)
369
return now < (self.last_checked_ok + self.timeout)
372
def peer_certificate(session):
373
"Return the peer's OpenPGP certificate as a bytestring"
374
# If not an OpenPGP certificate...
375
if gnutls.library.functions.gnutls_certificate_type_get\
376
(session._c_object) \
377
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
378
# ...do the normal thing
379
return session.peer_certificate
380
list_size = ctypes.c_uint()
381
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
382
(session._c_object, ctypes.byref(list_size))
383
if list_size.value == 0:
386
return ctypes.string_at(cert.data, cert.size)
389
def fingerprint(openpgp):
390
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
391
# New GnuTLS "datum" with the OpenPGP public key
392
datum = gnutls.library.types.gnutls_datum_t\
393
(ctypes.cast(ctypes.c_char_p(openpgp),
394
ctypes.POINTER(ctypes.c_ubyte)),
395
ctypes.c_uint(len(openpgp)))
396
# New empty GnuTLS certificate
397
crt = gnutls.library.types.gnutls_openpgp_crt_t()
398
gnutls.library.functions.gnutls_openpgp_crt_init\
400
# Import the OpenPGP public key into the certificate
401
gnutls.library.functions.gnutls_openpgp_crt_import\
402
(crt, ctypes.byref(datum),
403
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
404
# New buffer for the fingerprint
405
buffer = ctypes.create_string_buffer(20)
406
buffer_length = ctypes.c_size_t()
407
# Get the fingerprint from the certificate into the buffer
408
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
409
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
410
# Deinit the certificate
411
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
412
# Convert the buffer to a Python bytestring
413
fpr = ctypes.string_at(buffer, buffer_length.value)
414
# Convert the bytestring to hexadecimal notation
415
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
419
class tcp_handler(SocketServer.BaseRequestHandler, object):
420
"""A TCP request handler class.
421
Instantiated by IPv6_TCPServer for each request to handle it.
594
# Encrypts a client secret and stores it in a varible encrypted_secret
595
def encrypt_secret(self, key):
596
# Encryption-key need to be of a specific size, so we hash inputed key
597
hasheng = hashlib.sha256()
599
encryptionkey = hasheng.digest()
601
# Create validation hash so we know at decryption if it was sucessful
602
hasheng = hashlib.sha256()
603
hasheng.update(self.secret)
604
validationhash = hasheng.digest()
607
iv = os.urandom(Crypto.Cipher.AES.block_size)
608
ciphereng = Crypto.Cipher.AES.new(encryptionkey,
609
Crypto.Cipher.AES.MODE_CFB, iv)
610
ciphertext = ciphereng.encrypt(validationhash+self.secret)
611
self.encrypted_secret = (ciphertext, iv)
613
# Decrypt a encrypted client secret
614
def decrypt_secret(self, key):
615
# Decryption-key need to be of a specific size, so we hash inputed key
616
hasheng = hashlib.sha256()
618
encryptionkey = hasheng.digest()
620
# Decrypt encrypted secret
621
ciphertext, iv = self.encrypted_secret
622
ciphereng = Crypto.Cipher.AES.new(encryptionkey,
623
Crypto.Cipher.AES.MODE_CFB, iv)
624
plain = ciphereng.decrypt(ciphertext)
626
# Validate decrypted secret to know if it was succesful
627
hasheng = hashlib.sha256()
628
validationhash = plain[:hasheng.digest_size]
629
secret = plain[hasheng.digest_size:]
630
hasheng.update(secret)
632
# if validation fails, we use key as new secret. Otherwhise, we use
633
# the decrypted secret
634
if hasheng.digest() == validationhash:
638
del self.encrypted_secret
641
def dbus_service_property(dbus_interface, signature="v",
642
access="readwrite", byte_arrays=False):
643
"""Decorators for marking methods of a DBusObjectWithProperties to
644
become properties on the D-Bus.
646
The decorated method will be called with no arguments by "Get"
647
and with one argument by "Set".
649
The parameters, where they are supported, are the same as
650
dbus.service.method, except there is only "signature", since the
651
type from Get() and the type sent to Set() is the same.
653
# Encoding deeply encoded byte arrays is not supported yet by the
654
# "Set" method, so we fail early here:
655
if byte_arrays and signature != "ay":
656
raise ValueError("Byte arrays not supported for non-'ay'"
657
" signature %r" % signature)
659
func._dbus_is_property = True
660
func._dbus_interface = dbus_interface
661
func._dbus_signature = signature
662
func._dbus_access = access
663
func._dbus_name = func.__name__
664
if func._dbus_name.endswith("_dbus_property"):
665
func._dbus_name = func._dbus_name[:-14]
666
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
671
class DBusPropertyException(dbus.exceptions.DBusException):
672
"""A base class for D-Bus property-related exceptions
674
def __unicode__(self):
675
return unicode(str(self))
678
class DBusPropertyAccessException(DBusPropertyException):
679
"""A property's access permissions disallows an operation.
684
class DBusPropertyNotFound(DBusPropertyException):
685
"""An attempt was made to access a non-existing property.
690
class DBusObjectWithProperties(dbus.service.Object):
691
"""A D-Bus object with properties.
693
Classes inheriting from this can use the dbus_service_property
694
decorator to expose methods as D-Bus properties. It exposes the
695
standard Get(), Set(), and GetAll() methods on the D-Bus.
699
def _is_dbus_property(obj):
700
return getattr(obj, "_dbus_is_property", False)
702
def _get_all_dbus_properties(self):
703
"""Returns a generator of (name, attribute) pairs
705
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
706
for cls in self.__class__.__mro__
708
inspect.getmembers(cls, self._is_dbus_property))
710
def _get_dbus_property(self, interface_name, property_name):
711
"""Returns a bound method if one exists which is a D-Bus
712
property with the specified name and interface.
714
for cls in self.__class__.__mro__:
715
for name, value in (inspect.getmembers
716
(cls, self._is_dbus_property)):
717
if (value._dbus_name == property_name
718
and value._dbus_interface == interface_name):
719
return value.__get__(self)
722
raise DBusPropertyNotFound(self.dbus_object_path + ":"
723
+ interface_name + "."
726
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
728
def Get(self, interface_name, property_name):
729
"""Standard D-Bus property Get() method, see D-Bus standard.
731
prop = self._get_dbus_property(interface_name, property_name)
732
if prop._dbus_access == "write":
733
raise DBusPropertyAccessException(property_name)
735
if not hasattr(value, "variant_level"):
737
return type(value)(value, variant_level=value.variant_level+1)
739
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
740
def Set(self, interface_name, property_name, value):
741
"""Standard D-Bus property Set() method, see D-Bus standard.
743
prop = self._get_dbus_property(interface_name, property_name)
744
if prop._dbus_access == "read":
745
raise DBusPropertyAccessException(property_name)
746
if prop._dbus_get_args_options["byte_arrays"]:
747
# The byte_arrays option is not supported yet on
748
# signatures other than "ay".
749
if prop._dbus_signature != "ay":
751
value = dbus.ByteArray(''.join(unichr(byte)
755
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
756
out_signature="a{sv}")
757
def GetAll(self, interface_name):
758
"""Standard D-Bus property GetAll() method, see D-Bus
761
Note: Will not include properties with access="write".
764
for name, prop in self._get_all_dbus_properties():
766
and interface_name != prop._dbus_interface):
767
# Interface non-empty but did not match
769
# Ignore write-only properties
770
if prop._dbus_access == "write":
773
if not hasattr(value, "variant_level"):
776
all[name] = type(value)(value, variant_level=
777
value.variant_level+1)
778
return dbus.Dictionary(all, signature="sv")
780
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
782
path_keyword='object_path',
783
connection_keyword='connection')
784
def Introspect(self, object_path, connection):
785
"""Standard D-Bus method, overloaded to insert property tags.
787
xmlstring = dbus.service.Object.Introspect(self, object_path,
790
document = xml.dom.minidom.parseString(xmlstring)
791
def make_tag(document, name, prop):
792
e = document.createElement("property")
793
e.setAttribute("name", name)
794
e.setAttribute("type", prop._dbus_signature)
795
e.setAttribute("access", prop._dbus_access)
797
for if_tag in document.getElementsByTagName("interface"):
798
for tag in (make_tag(document, name, prop)
800
in self._get_all_dbus_properties()
801
if prop._dbus_interface
802
== if_tag.getAttribute("name")):
803
if_tag.appendChild(tag)
804
# Add the names to the return values for the
805
# "org.freedesktop.DBus.Properties" methods
806
if (if_tag.getAttribute("name")
807
== "org.freedesktop.DBus.Properties"):
808
for cn in if_tag.getElementsByTagName("method"):
809
if cn.getAttribute("name") == "Get":
810
for arg in cn.getElementsByTagName("arg"):
811
if (arg.getAttribute("direction")
813
arg.setAttribute("name", "value")
814
elif cn.getAttribute("name") == "GetAll":
815
for arg in cn.getElementsByTagName("arg"):
816
if (arg.getAttribute("direction")
818
arg.setAttribute("name", "props")
819
xmlstring = document.toxml("utf-8")
821
except (AttributeError, xml.dom.DOMException,
822
xml.parsers.expat.ExpatError) as error:
823
logger.error("Failed to override Introspection method",
828
def datetime_to_dbus (dt, variant_level=0):
829
"""Convert a UTC datetime.datetime() to a D-Bus type."""
831
return dbus.String("", variant_level = variant_level)
832
return dbus.String(dt.isoformat(),
833
variant_level=variant_level)
835
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
837
"""Applied to an empty subclass of a D-Bus object, this metaclass
838
will add additional D-Bus attributes matching a certain pattern.
840
def __new__(mcs, name, bases, attr):
841
# Go through all the base classes which could have D-Bus
842
# methods, signals, or properties in them
843
for base in (b for b in bases
844
if issubclass(b, dbus.service.Object)):
845
# Go though all attributes of the base class
846
for attrname, attribute in inspect.getmembers(base):
847
# Ignore non-D-Bus attributes, and D-Bus attributes
848
# with the wrong interface name
849
if (not hasattr(attribute, "_dbus_interface")
850
or not attribute._dbus_interface
851
.startswith("se.recompile.Mandos")):
853
# Create an alternate D-Bus interface name based on
855
alt_interface = (attribute._dbus_interface
856
.replace("se.recompile.Mandos",
857
"se.bsnet.fukt.Mandos"))
858
# Is this a D-Bus signal?
859
if getattr(attribute, "_dbus_is_signal", False):
860
# Extract the original non-method function by
862
nonmethod_func = (dict(
863
zip(attribute.func_code.co_freevars,
864
attribute.__closure__))["func"]
866
# Create a new, but exactly alike, function
867
# object, and decorate it to be a new D-Bus signal
868
# with the alternate D-Bus interface name
869
new_function = (dbus.service.signal
871
attribute._dbus_signature)
873
nonmethod_func.func_code,
874
nonmethod_func.func_globals,
875
nonmethod_func.func_name,
876
nonmethod_func.func_defaults,
877
nonmethod_func.func_closure)))
878
# Define a creator of a function to call both the
879
# old and new functions, so both the old and new
880
# signals gets sent when the function is called
881
def fixscope(func1, func2):
882
"""This function is a scope container to pass
883
func1 and func2 to the "call_both" function
884
outside of its arguments"""
885
def call_both(*args, **kwargs):
886
"""This function will emit two D-Bus
887
signals by calling func1 and func2"""
888
func1(*args, **kwargs)
889
func2(*args, **kwargs)
891
# Create the "call_both" function and add it to
893
attr[attrname] = fixscope(attribute,
895
# Is this a D-Bus method?
896
elif getattr(attribute, "_dbus_is_method", False):
897
# Create a new, but exactly alike, function
898
# object. Decorate it to be a new D-Bus method
899
# with the alternate D-Bus interface name. Add it
901
attr[attrname] = (dbus.service.method
903
attribute._dbus_in_signature,
904
attribute._dbus_out_signature)
906
(attribute.func_code,
907
attribute.func_globals,
909
attribute.func_defaults,
910
attribute.func_closure)))
911
# Is this a D-Bus property?
912
elif getattr(attribute, "_dbus_is_property", False):
913
# Create a new, but exactly alike, function
914
# object, and decorate it to be a new D-Bus
915
# property with the alternate D-Bus interface
916
# name. Add it to the class.
917
attr[attrname] = (dbus_service_property
919
attribute._dbus_signature,
920
attribute._dbus_access,
922
._dbus_get_args_options
925
(attribute.func_code,
926
attribute.func_globals,
928
attribute.func_defaults,
929
attribute.func_closure)))
930
return type.__new__(mcs, name, bases, attr)
932
class ClientDBus(Client, DBusObjectWithProperties):
933
"""A Client class using D-Bus
936
dbus_object_path: dbus.ObjectPath
937
bus: dbus.SystemBus()
940
runtime_expansions = (Client.runtime_expansions
941
+ ("dbus_object_path",))
943
# dbus.service.Object doesn't use super(), so we can't either.
945
def __init__(self, bus = None, *args, **kwargs):
947
Client.__init__(self, *args, **kwargs)
949
self._approvals_pending = 0
950
# Only now, when this client is initialized, can it show up on
952
client_object_name = unicode(self.name).translate(
955
self.dbus_object_path = (dbus.ObjectPath
956
("/clients/" + client_object_name))
957
DBusObjectWithProperties.__init__(self, self.bus,
958
self.dbus_object_path)
960
def notifychangeproperty(transform_func,
961
dbus_name, type_func=lambda x: x,
963
""" Modify a variable so that it's a property which announces
966
transform_fun: Function that takes a value and a variant_level
967
and transforms it to a D-Bus type.
968
dbus_name: D-Bus name of the variable
969
type_func: Function that transform the value before sending it
970
to the D-Bus. Default: no transform
971
variant_level: D-Bus variant level. Default: 1
973
attrname = "_{0}".format(dbus_name)
974
def setter(self, value):
975
if hasattr(self, "dbus_object_path"):
976
if (not hasattr(self, attrname) or
977
type_func(getattr(self, attrname, None))
978
!= type_func(value)):
979
dbus_value = transform_func(type_func(value),
982
self.PropertyChanged(dbus.String(dbus_name),
984
setattr(self, attrname, value)
986
return property(lambda self: getattr(self, attrname), setter)
989
expires = notifychangeproperty(datetime_to_dbus, "Expires")
990
approvals_pending = notifychangeproperty(dbus.Boolean,
993
enabled = notifychangeproperty(dbus.Boolean, "Enabled")
994
last_enabled = notifychangeproperty(datetime_to_dbus,
996
checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
997
type_func = lambda checker:
999
last_checked_ok = notifychangeproperty(datetime_to_dbus,
1001
last_approval_request = notifychangeproperty(
1002
datetime_to_dbus, "LastApprovalRequest")
1003
approved_by_default = notifychangeproperty(dbus.Boolean,
1004
"ApprovedByDefault")
1005
approval_delay = notifychangeproperty(dbus.UInt16,
1008
_timedelta_to_milliseconds)
1009
approval_duration = notifychangeproperty(
1010
dbus.UInt16, "ApprovalDuration",
1011
type_func = _timedelta_to_milliseconds)
1012
host = notifychangeproperty(dbus.String, "Host")
1013
timeout = notifychangeproperty(dbus.UInt16, "Timeout",
1015
_timedelta_to_milliseconds)
1016
extended_timeout = notifychangeproperty(
1017
dbus.UInt16, "ExtendedTimeout",
1018
type_func = _timedelta_to_milliseconds)
1019
interval = notifychangeproperty(dbus.UInt16,
1022
_timedelta_to_milliseconds)
1023
checker_command = notifychangeproperty(dbus.String, "Checker")
1025
del notifychangeproperty
1027
def __del__(self, *args, **kwargs):
1029
self.remove_from_connection()
1032
if hasattr(DBusObjectWithProperties, "__del__"):
1033
DBusObjectWithProperties.__del__(self, *args, **kwargs)
1034
Client.__del__(self, *args, **kwargs)
1036
def checker_callback(self, pid, condition, command,
1038
self.checker_callback_tag = None
1040
if os.WIFEXITED(condition):
1041
exitstatus = os.WEXITSTATUS(condition)
1043
self.CheckerCompleted(dbus.Int16(exitstatus),
1044
dbus.Int64(condition),
1045
dbus.String(command))
1048
self.CheckerCompleted(dbus.Int16(-1),
1049
dbus.Int64(condition),
1050
dbus.String(command))
1052
return Client.checker_callback(self, pid, condition, command,
1055
def start_checker(self, *args, **kwargs):
1056
old_checker = self.checker
1057
if self.checker is not None:
1058
old_checker_pid = self.checker.pid
1060
old_checker_pid = None
1061
r = Client.start_checker(self, *args, **kwargs)
1062
# Only if new checker process was started
1063
if (self.checker is not None
1064
and old_checker_pid != self.checker.pid):
1066
self.CheckerStarted(self.current_checker_command)
1069
def _reset_approved(self):
1070
self._approved = None
1073
def approve(self, value=True):
1074
self.send_changedstate()
1075
self._approved = value
1076
gobject.timeout_add(_timedelta_to_milliseconds
1077
(self.approval_duration),
1078
self._reset_approved)
1081
## D-Bus methods, signals & properties
1082
_interface = "se.recompile.Mandos.Client"
1086
# CheckerCompleted - signal
1087
@dbus.service.signal(_interface, signature="nxs")
1088
def CheckerCompleted(self, exitcode, waitstatus, command):
1092
# CheckerStarted - signal
1093
@dbus.service.signal(_interface, signature="s")
1094
def CheckerStarted(self, command):
1098
# PropertyChanged - signal
1099
@dbus.service.signal(_interface, signature="sv")
1100
def PropertyChanged(self, property, value):
1104
# GotSecret - signal
1105
@dbus.service.signal(_interface)
1106
def GotSecret(self):
1108
Is sent after a successful transfer of secret from the Mandos
1109
server to mandos-client
1114
@dbus.service.signal(_interface, signature="s")
1115
def Rejected(self, reason):
1119
# NeedApproval - signal
1120
@dbus.service.signal(_interface, signature="tb")
1121
def NeedApproval(self, timeout, default):
1123
return self.need_approval()
1128
@dbus.service.method(_interface, in_signature="b")
1129
def Approve(self, value):
1132
# CheckedOK - method
1133
@dbus.service.method(_interface)
1134
def CheckedOK(self):
1138
@dbus.service.method(_interface)
1143
# StartChecker - method
1144
@dbus.service.method(_interface)
1145
def StartChecker(self):
1147
self.start_checker()
1150
@dbus.service.method(_interface)
1155
# StopChecker - method
1156
@dbus.service.method(_interface)
1157
def StopChecker(self):
1162
# ApprovalPending - property
1163
@dbus_service_property(_interface, signature="b", access="read")
1164
def ApprovalPending_dbus_property(self):
1165
return dbus.Boolean(bool(self.approvals_pending))
1167
# ApprovedByDefault - property
1168
@dbus_service_property(_interface, signature="b",
1170
def ApprovedByDefault_dbus_property(self, value=None):
1171
if value is None: # get
1172
return dbus.Boolean(self.approved_by_default)
1173
self.approved_by_default = bool(value)
1175
# ApprovalDelay - property
1176
@dbus_service_property(_interface, signature="t",
1178
def ApprovalDelay_dbus_property(self, value=None):
1179
if value is None: # get
1180
return dbus.UInt64(self.approval_delay_milliseconds())
1181
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1183
# ApprovalDuration - property
1184
@dbus_service_property(_interface, signature="t",
1186
def ApprovalDuration_dbus_property(self, value=None):
1187
if value is None: # get
1188
return dbus.UInt64(_timedelta_to_milliseconds(
1189
self.approval_duration))
1190
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1193
@dbus_service_property(_interface, signature="s", access="read")
1194
def Name_dbus_property(self):
1195
return dbus.String(self.name)
1197
# Fingerprint - property
1198
@dbus_service_property(_interface, signature="s", access="read")
1199
def Fingerprint_dbus_property(self):
1200
return dbus.String(self.fingerprint)
1203
@dbus_service_property(_interface, signature="s",
1205
def Host_dbus_property(self, value=None):
1206
if value is None: # get
1207
return dbus.String(self.host)
1210
# Created - property
1211
@dbus_service_property(_interface, signature="s", access="read")
1212
def Created_dbus_property(self):
1213
return dbus.String(datetime_to_dbus(self.created))
1215
# LastEnabled - property
1216
@dbus_service_property(_interface, signature="s", access="read")
1217
def LastEnabled_dbus_property(self):
1218
return datetime_to_dbus(self.last_enabled)
1220
# Enabled - property
1221
@dbus_service_property(_interface, signature="b",
1223
def Enabled_dbus_property(self, value=None):
1224
if value is None: # get
1225
return dbus.Boolean(self.enabled)
1231
# LastCheckedOK - property
1232
@dbus_service_property(_interface, signature="s",
1234
def LastCheckedOK_dbus_property(self, value=None):
1235
if value is not None:
1238
return datetime_to_dbus(self.last_checked_ok)
1240
# Expires - property
1241
@dbus_service_property(_interface, signature="s", access="read")
1242
def Expires_dbus_property(self):
1243
return datetime_to_dbus(self.expires)
1245
# LastApprovalRequest - property
1246
@dbus_service_property(_interface, signature="s", access="read")
1247
def LastApprovalRequest_dbus_property(self):
1248
return datetime_to_dbus(self.last_approval_request)
1250
# Timeout - property
1251
@dbus_service_property(_interface, signature="t",
1253
def Timeout_dbus_property(self, value=None):
1254
if value is None: # get
1255
return dbus.UInt64(self.timeout_milliseconds())
1256
self.timeout = datetime.timedelta(0, 0, 0, value)
1257
if getattr(self, "disable_initiator_tag", None) is None:
1259
# Reschedule timeout
1260
gobject.source_remove(self.disable_initiator_tag)
1261
self.disable_initiator_tag = None
1263
time_to_die = _timedelta_to_milliseconds((self
1268
if time_to_die <= 0:
1269
# The timeout has passed
1272
self.expires = (datetime.datetime.utcnow()
1273
+ datetime.timedelta(milliseconds =
1275
self.disable_initiator_tag = (gobject.timeout_add
1276
(time_to_die, self.disable))
1278
# ExtendedTimeout - property
1279
@dbus_service_property(_interface, signature="t",
1281
def ExtendedTimeout_dbus_property(self, value=None):
1282
if value is None: # get
1283
return dbus.UInt64(self.extended_timeout_milliseconds())
1284
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1286
# Interval - property
1287
@dbus_service_property(_interface, signature="t",
1289
def Interval_dbus_property(self, value=None):
1290
if value is None: # get
1291
return dbus.UInt64(self.interval_milliseconds())
1292
self.interval = datetime.timedelta(0, 0, 0, value)
1293
if getattr(self, "checker_initiator_tag", None) is None:
1295
# Reschedule checker run
1296
gobject.source_remove(self.checker_initiator_tag)
1297
self.checker_initiator_tag = (gobject.timeout_add
1298
(value, self.start_checker))
1299
self.start_checker() # Start one now, too
1301
# Checker - property
1302
@dbus_service_property(_interface, signature="s",
1304
def Checker_dbus_property(self, value=None):
1305
if value is None: # get
1306
return dbus.String(self.checker_command)
1307
self.checker_command = value
1309
# CheckerRunning - property
1310
@dbus_service_property(_interface, signature="b",
1312
def CheckerRunning_dbus_property(self, value=None):
1313
if value is None: # get
1314
return dbus.Boolean(self.checker is not None)
1316
self.start_checker()
1320
# ObjectPath - property
1321
@dbus_service_property(_interface, signature="o", access="read")
1322
def ObjectPath_dbus_property(self):
1323
return self.dbus_object_path # is already a dbus.ObjectPath
1326
@dbus_service_property(_interface, signature="ay",
1327
access="write", byte_arrays=True)
1328
def Secret_dbus_property(self, value):
1329
self.secret = str(value)
1334
class ProxyClient(object):
1335
def __init__(self, child_pipe, fpr, address):
1336
self._pipe = child_pipe
1337
self._pipe.send(('init', fpr, address))
1338
if not self._pipe.recv():
1341
def __getattribute__(self, name):
1342
if(name == '_pipe'):
1343
return super(ProxyClient, self).__getattribute__(name)
1344
self._pipe.send(('getattr', name))
1345
data = self._pipe.recv()
1346
if data[0] == 'data':
1348
if data[0] == 'function':
1349
def func(*args, **kwargs):
1350
self._pipe.send(('funcall', name, args, kwargs))
1351
return self._pipe.recv()[1]
1354
def __setattr__(self, name, value):
1355
if(name == '_pipe'):
1356
return super(ProxyClient, self).__setattr__(name, value)
1357
self._pipe.send(('setattr', name, value))
1359
class ClientDBusTransitional(ClientDBus):
1360
__metaclass__ = AlternateDBusNamesMetaclass
1362
class ClientHandler(socketserver.BaseRequestHandler, object):
1363
"""A class to handle client connections.
1365
Instantiated once for each connection to handle it.
422
1366
Note: This will run in its own forked process."""
424
1368
def handle(self):
425
logger.info(u"TCP connection from: %s",
426
unicode(self.client_address))
427
session = gnutls.connection.ClientSession\
428
(self.request, gnutls.connection.X509Credentials())
430
line = self.request.makefile().readline()
431
logger.debug(u"Protocol version: %r", line)
433
if int(line.strip().split()[0]) > 1:
435
except (ValueError, IndexError, RuntimeError), error:
436
logger.error(u"Unknown protocol version: %s", error)
439
# Note: gnutls.connection.X509Credentials is really a generic
440
# GnuTLS certificate credentials object so long as no X.509
441
# keys are added to it. Therefore, we can use it here despite
442
# using OpenPGP certificates.
444
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
445
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
447
priority = "NORMAL" # Fallback default, since this
449
if self.server.settings["priority"]:
450
priority = self.server.settings["priority"]
451
gnutls.library.functions.gnutls_priority_set_direct\
452
(session._c_object, priority, None);
456
except gnutls.errors.GNUTLSError, error:
457
logger.warning(u"Handshake failed: %s", error)
458
# Do not run session.bye() here: the session is not
459
# established. Just abandon the request.
462
fpr = fingerprint(peer_certificate(session))
463
except (TypeError, gnutls.errors.GNUTLSError), error:
464
logger.warning(u"Bad certificate: %s", error)
467
logger.debug(u"Fingerprint: %s", fpr)
469
for c in self.server.clients:
470
if c.fingerprint == fpr:
474
logger.warning(u"Client not found for fingerprint: %s",
478
# Have to check if client.still_valid(), since it is possible
479
# that the client timed out while establishing the GnuTLS
481
if not client.still_valid():
482
logger.warning(u"Client %(name)s is invalid",
487
while sent_size < len(client.secret):
488
sent = session.send(client.secret[sent_size:])
489
logger.debug(u"Sent: %d, remaining: %d",
490
sent, len(client.secret)
491
- (sent_size + sent))
496
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
497
"""IPv6 TCP server. Accepts 'None' as address and/or port.
1369
with contextlib.closing(self.server.child_pipe) as child_pipe:
1370
logger.info("TCP connection from: %s",
1371
unicode(self.client_address))
1372
logger.debug("Pipe FD: %d",
1373
self.server.child_pipe.fileno())
1375
session = (gnutls.connection
1376
.ClientSession(self.request,
1378
.X509Credentials()))
1380
# Note: gnutls.connection.X509Credentials is really a
1381
# generic GnuTLS certificate credentials object so long as
1382
# no X.509 keys are added to it. Therefore, we can use it
1383
# here despite using OpenPGP certificates.
1385
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1386
# "+AES-256-CBC", "+SHA1",
1387
# "+COMP-NULL", "+CTYPE-OPENPGP",
1389
# Use a fallback default, since this MUST be set.
1390
priority = self.server.gnutls_priority
1391
if priority is None:
1393
(gnutls.library.functions
1394
.gnutls_priority_set_direct(session._c_object,
1397
# Start communication using the Mandos protocol
1398
# Get protocol number
1399
line = self.request.makefile().readline()
1400
logger.debug("Protocol version: %r", line)
1402
if int(line.strip().split()[0]) > 1:
1404
except (ValueError, IndexError, RuntimeError) as error:
1405
logger.error("Unknown protocol version: %s", error)
1408
# Start GnuTLS connection
1411
except gnutls.errors.GNUTLSError as error:
1412
logger.warning("Handshake failed: %s", error)
1413
# Do not run session.bye() here: the session is not
1414
# established. Just abandon the request.
1416
logger.debug("Handshake succeeded")
1418
approval_required = False
1421
fpr = self.fingerprint(self.peer_certificate
1424
gnutls.errors.GNUTLSError) as error:
1425
logger.warning("Bad certificate: %s", error)
1427
logger.debug("Fingerprint: %s", fpr)
1430
client = ProxyClient(child_pipe, fpr,
1431
self.client_address)
1435
if client.approval_delay:
1436
delay = client.approval_delay
1437
client.approvals_pending += 1
1438
approval_required = True
1441
if not client.enabled:
1442
logger.info("Client %s is disabled",
1444
if self.server.use_dbus:
1446
client.Rejected("Disabled")
1449
if client._approved or not client.approval_delay:
1450
#We are approved or approval is disabled
1452
elif client._approved is None:
1453
logger.info("Client %s needs approval",
1455
if self.server.use_dbus:
1457
client.NeedApproval(
1458
client.approval_delay_milliseconds(),
1459
client.approved_by_default)
1461
logger.warning("Client %s was not approved",
1463
if self.server.use_dbus:
1465
client.Rejected("Denied")
1468
#wait until timeout or approved
1469
time = datetime.datetime.now()
1470
client.changedstate.acquire()
1471
(client.changedstate.wait
1472
(float(client._timedelta_to_milliseconds(delay)
1474
client.changedstate.release()
1475
time2 = datetime.datetime.now()
1476
if (time2 - time) >= delay:
1477
if not client.approved_by_default:
1478
logger.warning("Client %s timed out while"
1479
" waiting for approval",
1481
if self.server.use_dbus:
1483
client.Rejected("Approval timed out")
1488
delay -= time2 - time
1491
while sent_size < len(client.secret):
1493
sent = session.send(client.secret[sent_size:])
1494
except gnutls.errors.GNUTLSError as error:
1495
logger.warning("gnutls send failed")
1497
logger.debug("Sent: %d, remaining: %d",
1498
sent, len(client.secret)
1499
- (sent_size + sent))
1502
logger.info("Sending secret to %s", client.name)
1503
# bump the timeout using extended_timeout
1504
client.checked_ok(client.extended_timeout)
1505
if self.server.use_dbus:
1510
if approval_required:
1511
client.approvals_pending -= 1
1514
except gnutls.errors.GNUTLSError as error:
1515
logger.warning("GnuTLS bye failed")
1518
def peer_certificate(session):
1519
"Return the peer's OpenPGP certificate as a bytestring"
1520
# If not an OpenPGP certificate...
1521
if (gnutls.library.functions
1522
.gnutls_certificate_type_get(session._c_object)
1523
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1524
# ...do the normal thing
1525
return session.peer_certificate
1526
list_size = ctypes.c_uint(1)
1527
cert_list = (gnutls.library.functions
1528
.gnutls_certificate_get_peers
1529
(session._c_object, ctypes.byref(list_size)))
1530
if not bool(cert_list) and list_size.value != 0:
1531
raise gnutls.errors.GNUTLSError("error getting peer"
1533
if list_size.value == 0:
1536
return ctypes.string_at(cert.data, cert.size)
1539
def fingerprint(openpgp):
1540
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1541
# New GnuTLS "datum" with the OpenPGP public key
1542
datum = (gnutls.library.types
1543
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1546
ctypes.c_uint(len(openpgp))))
1547
# New empty GnuTLS certificate
1548
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1549
(gnutls.library.functions
1550
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1551
# Import the OpenPGP public key into the certificate
1552
(gnutls.library.functions
1553
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1554
gnutls.library.constants
1555
.GNUTLS_OPENPGP_FMT_RAW))
1556
# Verify the self signature in the key
1557
crtverify = ctypes.c_uint()
1558
(gnutls.library.functions
1559
.gnutls_openpgp_crt_verify_self(crt, 0,
1560
ctypes.byref(crtverify)))
1561
if crtverify.value != 0:
1562
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1563
raise (gnutls.errors.CertificateSecurityError
1565
# New buffer for the fingerprint
1566
buf = ctypes.create_string_buffer(20)
1567
buf_len = ctypes.c_size_t()
1568
# Get the fingerprint from the certificate into the buffer
1569
(gnutls.library.functions
1570
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1571
ctypes.byref(buf_len)))
1572
# Deinit the certificate
1573
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1574
# Convert the buffer to a Python bytestring
1575
fpr = ctypes.string_at(buf, buf_len.value)
1576
# Convert the bytestring to hexadecimal notation
1577
hex_fpr = ''.join("%02X" % ord(char) for char in fpr)
1581
class MultiprocessingMixIn(object):
1582
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1583
def sub_process_main(self, request, address):
1585
self.finish_request(request, address)
1587
self.handle_error(request, address)
1588
self.close_request(request)
1590
def process_request(self, request, address):
1591
"""Start a new process to process the request."""
1592
proc = multiprocessing.Process(target = self.sub_process_main,
1599
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1600
""" adds a pipe to the MixIn """
1601
def process_request(self, request, client_address):
1602
"""Overrides and wraps the original process_request().
1604
This function creates a new pipe in self.pipe
1606
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1608
proc = MultiprocessingMixIn.process_request(self, request,
1610
self.child_pipe.close()
1611
self.add_pipe(parent_pipe, proc)
1613
def add_pipe(self, parent_pipe, proc):
1614
"""Dummy function; override as necessary"""
1615
raise NotImplementedError
1618
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1619
socketserver.TCPServer, object):
1620
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
499
settings: Server settings
500
clients: Set() of Client objects
1623
enabled: Boolean; whether this server is activated yet
1624
interface: None or a network interface name (string)
1625
use_ipv6: Boolean; to use IPv6 or not
502
address_family = socket.AF_INET6
503
def __init__(self, *args, **kwargs):
504
if "settings" in kwargs:
505
self.settings = kwargs["settings"]
506
del kwargs["settings"]
507
if "clients" in kwargs:
508
self.clients = kwargs["clients"]
509
del kwargs["clients"]
510
return super(type(self), self).__init__(*args, **kwargs)
1627
def __init__(self, server_address, RequestHandlerClass,
1628
interface=None, use_ipv6=True):
1629
self.interface = interface
1631
self.address_family = socket.AF_INET6
1632
socketserver.TCPServer.__init__(self, server_address,
1633
RequestHandlerClass)
511
1634
def server_bind(self):
512
1635
"""This overrides the normal server_bind() function
513
1636
to bind to an interface if one was specified, and also NOT to
514
1637
bind to an address or port if they were not specified."""
515
if self.settings["interface"]:
516
# 25 is from /usr/include/asm-i486/socket.h
517
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
519
self.socket.setsockopt(socket.SOL_SOCKET,
521
self.settings["interface"])
522
except socket.error, error:
523
if error[0] == errno.EPERM:
524
logger.error(u"No permission to"
525
u" bind to interface %s",
526
self.settings["interface"])
1638
if self.interface is not None:
1639
if SO_BINDTODEVICE is None:
1640
logger.error("SO_BINDTODEVICE does not exist;"
1641
" cannot bind to interface %s",
1645
self.socket.setsockopt(socket.SOL_SOCKET,
1649
except socket.error as error:
1650
if error[0] == errno.EPERM:
1651
logger.error("No permission to"
1652
" bind to interface %s",
1654
elif error[0] == errno.ENOPROTOOPT:
1655
logger.error("SO_BINDTODEVICE not available;"
1656
" cannot bind to interface %s",
529
1660
# Only bind(2) the socket if we really need to.
530
1661
if self.server_address[0] or self.server_address[1]:
531
1662
if not self.server_address[0]:
533
self.server_address = (in6addr_any,
1663
if self.address_family == socket.AF_INET6:
1664
any_address = "::" # in6addr_any
1666
any_address = socket.INADDR_ANY
1667
self.server_address = (any_address,
534
1668
self.server_address[1])
535
1669
elif not self.server_address[1]:
536
1670
self.server_address = (self.server_address[0],
538
# if self.settings["interface"]:
1672
# if self.interface:
539
1673
# self.server_address = (self.server_address[0],
542
1676
# if_nametoindex
545
return super(type(self), self).server_bind()
1678
return socketserver.TCPServer.server_bind(self)
1681
class MandosServer(IPv6_TCPServer):
1685
clients: set of Client objects
1686
gnutls_priority GnuTLS priority string
1687
use_dbus: Boolean; to emit D-Bus signals or not
1689
Assumes a gobject.MainLoop event loop.
1691
def __init__(self, server_address, RequestHandlerClass,
1692
interface=None, use_ipv6=True, clients=None,
1693
gnutls_priority=None, use_dbus=True):
1694
self.enabled = False
1695
self.clients = clients
1696
if self.clients is None:
1698
self.use_dbus = use_dbus
1699
self.gnutls_priority = gnutls_priority
1700
IPv6_TCPServer.__init__(self, server_address,
1701
RequestHandlerClass,
1702
interface = interface,
1703
use_ipv6 = use_ipv6)
1704
def server_activate(self):
1706
return socketserver.TCPServer.server_activate(self)
1711
def add_pipe(self, parent_pipe, proc):
1712
# Call "handle_ipc" for both data and EOF events
1713
gobject.io_add_watch(parent_pipe.fileno(),
1714
gobject.IO_IN | gobject.IO_HUP,
1715
functools.partial(self.handle_ipc,
1720
def handle_ipc(self, source, condition, parent_pipe=None,
1721
proc = None, client_object=None):
1723
gobject.IO_IN: "IN", # There is data to read.
1724
gobject.IO_OUT: "OUT", # Data can be written (without
1726
gobject.IO_PRI: "PRI", # There is urgent data to read.
1727
gobject.IO_ERR: "ERR", # Error condition.
1728
gobject.IO_HUP: "HUP" # Hung up (the connection has been
1729
# broken, usually for pipes and
1732
conditions_string = ' | '.join(name
1734
condition_names.iteritems()
1735
if cond & condition)
1736
# error, or the other end of multiprocessing.Pipe has closed
1737
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1738
# Wait for other process to exit
1742
# Read a request from the child
1743
request = parent_pipe.recv()
1744
command = request[0]
1746
if command == 'init':
1748
address = request[2]
1750
for c in self.clients.itervalues():
1751
if c.fingerprint == fpr:
1755
logger.info("Client not found for fingerprint: %s, ad"
1756
"dress: %s", fpr, address)
1759
mandos_dbus_service.ClientNotFound(fpr,
1761
parent_pipe.send(False)
1764
gobject.io_add_watch(parent_pipe.fileno(),
1765
gobject.IO_IN | gobject.IO_HUP,
1766
functools.partial(self.handle_ipc,
1772
parent_pipe.send(True)
1773
# remove the old hook in favor of the new above hook on
1776
if command == 'funcall':
1777
funcname = request[1]
1781
parent_pipe.send(('data', getattr(client_object,
1785
if command == 'getattr':
1786
attrname = request[1]
1787
if callable(client_object.__getattribute__(attrname)):
1788
parent_pipe.send(('function',))
1790
parent_pipe.send(('data', client_object
1791
.__getattribute__(attrname)))
1793
if command == 'setattr':
1794
attrname = request[1]
1796
setattr(client_object, attrname, value)
548
1801
def string_to_delta(interval):
549
1802
"""Parse a string and return a datetime.timedelta
551
1804
>>> string_to_delta('7d')
552
1805
datetime.timedelta(7)
553
1806
>>> string_to_delta('60s')
690
1941
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
691
1942
"servicename": "Mandos",
694
1948
# Parse config file for server-global settings
695
server_config = ConfigParser.SafeConfigParser(server_defaults)
1949
server_config = configparser.SafeConfigParser(server_defaults)
696
1950
del server_defaults
697
server_config.read(os.path.join(options.configdir, "mandos.conf"))
698
server_section = "server"
1951
server_config.read(os.path.join(options.configdir,
699
1953
# Convert the SafeConfigParser object to a dict
700
server_settings = dict(server_config.items(server_section))
701
# Use getboolean on the boolean config option
702
server_settings["debug"] = server_config.getboolean\
703
(server_section, "debug")
1954
server_settings = server_config.defaults()
1955
# Use the appropriate methods on the non-string config options
1956
for option in ("debug", "use_dbus", "use_ipv6"):
1957
server_settings[option] = server_config.getboolean("DEFAULT",
1959
if server_settings["port"]:
1960
server_settings["port"] = server_config.getint("DEFAULT",
704
1962
del server_config
706
1964
# Override the settings from the config file with command line
707
1965
# options, if set.
708
1966
for option in ("interface", "address", "port", "debug",
709
"priority", "servicename", "configdir"):
1967
"priority", "servicename", "configdir",
1968
"use_dbus", "use_ipv6", "debuglevel", "restore"):
710
1969
value = getattr(options, option)
711
1970
if value is not None:
712
1971
server_settings[option] = value
1973
# Force all strings to be unicode
1974
for option in server_settings.keys():
1975
if type(server_settings[option]) is str:
1976
server_settings[option] = unicode(server_settings[option])
714
1977
# Now we have our good server settings in "server_settings"
1979
##################################################################
716
1982
debug = server_settings["debug"]
719
syslogger.setLevel(logging.WARNING)
1983
debuglevel = server_settings["debuglevel"]
1984
use_dbus = server_settings["use_dbus"]
1985
use_ipv6 = server_settings["use_ipv6"]
721
1987
if server_settings["servicename"] != "Mandos":
722
syslogger.setFormatter(logging.Formatter\
723
('Mandos (%s): %%(levelname)s:'
1988
syslogger.setFormatter(logging.Formatter
1989
('Mandos (%s) [%%(process)d]:'
1990
' %%(levelname)s: %%(message)s'
725
1991
% server_settings["servicename"]))
727
1993
# Parse config file with clients
728
client_defaults = { "timeout": "1h",
1994
client_defaults = { "timeout": "5m",
1995
"extended_timeout": "15m",
730
1997
"checker": "fping -q -- %%(host)s",
1999
"approval_delay": "0s",
2000
"approval_duration": "1s",
732
client_config = ConfigParser.SafeConfigParser(client_defaults)
2002
client_config = configparser.SafeConfigParser(client_defaults)
733
2003
client_config.read(os.path.join(server_settings["configdir"],
734
2004
"clients.conf"))
737
service = AvahiService(name = server_settings["servicename"],
738
type = "_mandos._tcp", );
739
if server_settings["interface"]:
740
service.interface = if_nametoindex(server_settings["interface"])
2006
global mandos_dbus_service
2007
mandos_dbus_service = None
2009
tcp_server = MandosServer((server_settings["address"],
2010
server_settings["port"]),
2012
interface=(server_settings["interface"]
2016
server_settings["priority"],
2019
pidfilename = "/var/run/mandos.pid"
2021
pidfile = open(pidfilename, "w")
2023
logger.error("Could not open file %r", pidfilename)
2026
uid = pwd.getpwnam("_mandos").pw_uid
2027
gid = pwd.getpwnam("_mandos").pw_gid
2030
uid = pwd.getpwnam("mandos").pw_uid
2031
gid = pwd.getpwnam("mandos").pw_gid
2034
uid = pwd.getpwnam("nobody").pw_uid
2035
gid = pwd.getpwnam("nobody").pw_gid
2042
except OSError as error:
2043
if error[0] != errno.EPERM:
2046
if not debug and not debuglevel:
2047
logger.setLevel(logging.WARNING)
2049
level = getattr(logging, debuglevel.upper())
2050
logger.setLevel(level)
2053
logger.setLevel(logging.DEBUG)
2054
# Enable all possible GnuTLS debugging
2056
# "Use a log level over 10 to enable all debugging options."
2058
gnutls.library.functions.gnutls_global_set_log_level(11)
2060
@gnutls.library.types.gnutls_log_func
2061
def debug_gnutls(level, string):
2062
logger.debug("GnuTLS: %s", string[:-1])
2064
(gnutls.library.functions
2065
.gnutls_global_set_log_function(debug_gnutls))
2067
# Redirect stdin so all checkers get /dev/null
2068
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
2069
os.dup2(null, sys.stdin.fileno())
2073
# No console logging
2074
logger.removeHandler(console)
2076
# Need to fork before connecting to D-Bus
2078
# Close all input and output, do double fork, etc.
742
2081
global main_loop
745
2082
# From the Avahi example code
746
2083
DBusGMainLoop(set_as_default=True )
747
2084
main_loop = gobject.MainLoop()
748
2085
bus = dbus.SystemBus()
749
server = dbus.Interface(
750
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
751
avahi.DBUS_INTERFACE_SERVER )
752
2086
# End of Avahi example code
755
console = logging.StreamHandler()
756
# console.setLevel(logging.DEBUG)
757
console.setFormatter(logging.Formatter\
758
('%(levelname)s: %(message)s'))
759
logger.addHandler(console)
763
def remove_from_clients(client):
764
clients.remove(client)
766
logger.critical(u"No clients left, exiting")
769
clients.update(Set(Client(name = section,
770
stop_hook = remove_from_clients,
772
= dict(client_config.items(section)))
773
for section in client_config.sections()))
775
logger.critical(u"No clients defined")
2089
bus_name = dbus.service.BusName("se.recompile.Mandos",
2090
bus, do_not_queue=True)
2091
old_bus_name = (dbus.service.BusName
2092
("se.bsnet.fukt.Mandos", bus,
2094
except dbus.exceptions.NameExistsException as e:
2095
logger.error(unicode(e) + ", disabling D-Bus")
2097
server_settings["use_dbus"] = False
2098
tcp_server.use_dbus = False
2099
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2100
service = AvahiServiceToSyslog(name =
2101
server_settings["servicename"],
2102
servicetype = "_mandos._tcp",
2103
protocol = protocol, bus = bus)
2104
if server_settings["interface"]:
2105
service.interface = (if_nametoindex
2106
(str(server_settings["interface"])))
2108
global multiprocessing_manager
2109
multiprocessing_manager = multiprocessing.Manager()
2111
client_class = Client
2113
client_class = functools.partial(ClientDBusTransitional,
2116
special_settings = {
2117
# Some settings need to be accessd by special methods;
2118
# booleans need .getboolean(), etc. Here is a list of them:
2119
"approved_by_default":
2121
client_config.getboolean(section, "approved_by_default"),
2123
# Construct a new dict of client settings of this form:
2124
# { client_name: {setting_name: value, ...}, ...}
2125
# with exceptions for any special settings as defined above
2126
client_settings = dict((clientname,
2128
(value if setting not in special_settings
2129
else special_settings[setting](clientname)))
2130
for setting, value in client_config.items(clientname)))
2131
for clientname in client_config.sections())
2133
old_client_settings = {}
2136
# Get client data and settings from last running state.
2137
if server_settings["restore"]:
2139
with open(stored_state_path, "rb") as stored_state:
2140
clients_data, old_client_settings = pickle.load(stored_state)
2141
os.remove(stored_state_path)
2142
except IOError as e:
2143
logger.warning("Could not load persistant state: {0}".format(e))
2144
if e.errno != errno.ENOENT:
2147
for client in clients_data:
2148
client_name = client["name"]
2150
# Decide which value to use after restoring saved state.
2151
# We have three different values: Old config file,
2152
# new config file, and saved state.
2153
# New config value takes precedence if it differs from old
2154
# config value, otherwise use saved state.
2155
for name, value in client_settings[client_name].items():
2157
# For each value in new config, check if it differs
2158
# from the old config value (Except for the "secret"
2160
if name != "secret" and value != old_client_settings[client_name][name]:
2161
setattr(client, name, value)
2165
# Clients who has passed its expire date, can still be enabled if its
2166
# last checker was sucessful. Clients who checkers failed before we
2167
# stored it state is asumed to had failed checker during downtime.
2168
if client["enabled"] and client["last_checked_ok"]:
2169
if ((datetime.datetime.utcnow() - client["last_checked_ok"])
2170
> client["interval"]):
2171
if client["last_checker_status"] != 0:
2172
client["enabled"] = False
2174
client["expires"] = datetime.datetime.utcnow() + client["timeout"]
2176
client["changedstate"] = (multiprocessing_manager
2177
.Condition(multiprocessing_manager
2180
new_client = ClientDBusTransitional.__new__(ClientDBusTransitional)
2181
tcp_server.clients[client_name] = new_client
2182
new_client.bus = bus
2183
for name, value in client.iteritems():
2184
setattr(new_client, name, value)
2185
client_object_name = unicode(client_name).translate(
2186
{ord("."): ord("_"),
2187
ord("-"): ord("_")})
2188
new_client.dbus_object_path = (dbus.ObjectPath
2189
("/clients/" + client_object_name))
2190
DBusObjectWithProperties.__init__(new_client,
2192
new_client.dbus_object_path)
2194
tcp_server.clients[client_name] = Client.__new__(Client)
2195
for name, value in client.iteritems():
2196
setattr(tcp_server.clients[client_name], name, value)
2198
tcp_server.clients[client_name].decrypt_secret(
2199
client_settings[client_name]["secret"])
2201
# Create/remove clients based on new changes made to config
2202
for clientname in set(old_client_settings) - set(client_settings):
2203
del tcp_server.clients[clientname]
2204
for clientname in set(client_settings) - set(old_client_settings):
2205
tcp_server.clients[clientname] = (client_class(name = clientname,
2211
if not tcp_server.clients:
2212
logger.warning("No clients defined")
781
pidfilename = "/var/run/mandos/mandos.pid"
784
pidfile = open(pidfilename, "w")
785
pidfile.write(str(pid) + "\n")
789
logger.error(u"Could not write %s file with PID %d",
790
pidfilename, os.getpid())
2218
pidfile.write(str(pid) + "\n".encode("utf-8"))
2221
logger.error("Could not write to file %r with PID %d",
2224
# "pidfile" was never created
2228
signal.signal(signal.SIGINT, signal.SIG_IGN)
2230
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2231
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2234
class MandosDBusService(dbus.service.Object):
2235
"""A D-Bus proxy object"""
2237
dbus.service.Object.__init__(self, bus, "/")
2238
_interface = "se.recompile.Mandos"
2240
@dbus.service.signal(_interface, signature="o")
2241
def ClientAdded(self, objpath):
2245
@dbus.service.signal(_interface, signature="ss")
2246
def ClientNotFound(self, fingerprint, address):
2250
@dbus.service.signal(_interface, signature="os")
2251
def ClientRemoved(self, objpath, name):
2255
@dbus.service.method(_interface, out_signature="ao")
2256
def GetAllClients(self):
2258
return dbus.Array(c.dbus_object_path
2260
tcp_server.clients.itervalues())
2262
@dbus.service.method(_interface,
2263
out_signature="a{oa{sv}}")
2264
def GetAllClientsWithProperties(self):
2266
return dbus.Dictionary(
2267
((c.dbus_object_path, c.GetAll(""))
2268
for c in tcp_server.clients.itervalues()),
2271
@dbus.service.method(_interface, in_signature="o")
2272
def RemoveClient(self, object_path):
2274
for c in tcp_server.clients.itervalues():
2275
if c.dbus_object_path == object_path:
2276
del tcp_server.clients[c.name]
2277
c.remove_from_connection()
2278
# Don't signal anything except ClientRemoved
2279
c.disable(quiet=True)
2281
self.ClientRemoved(object_path, c.name)
2283
raise KeyError(object_path)
2287
class MandosDBusServiceTransitional(MandosDBusService):
2288
__metaclass__ = AlternateDBusNamesMetaclass
2289
mandos_dbus_service = MandosDBusServiceTransitional()
793
2292
"Cleanup function; run on exit"
795
# From the Avahi example code
796
if not group is None:
799
# End of Avahi example code
802
client = clients.pop()
803
client.stop_hook = None
2295
multiprocessing.active_children()
2296
if not (tcp_server.clients or client_settings):
2299
# Store client before exiting. Secrets are encrypted with key based
2300
# on what config file has. If config file is removed/edited, old
2301
# secret will thus be unrecovable.
2303
for client in tcp_server.clients.itervalues():
2304
client.encrypt_secret(client_settings[client.name]["secret"])
2308
# A list of attributes that will not be stored when shuting down.
2309
exclude = set(("bus", "changedstate", "secret"))
2310
for name, typ in inspect.getmembers(dbus.service.Object):
2313
client_dict["encrypted_secret"] = client.encrypted_secret
2314
for attr in client.client_structure:
2315
if attr not in exclude:
2316
client_dict[attr] = getattr(client, attr)
2318
clients.append(client_dict)
2319
del client_settings[client.name]["secret"]
2322
with os.fdopen(os.open(stored_state_path, os.O_CREAT|os.O_WRONLY|os.O_TRUNC, 0600), "wb") as stored_state:
2323
pickle.dump((clients, client_settings), stored_state)
2324
except IOError as e:
2325
logger.warning("Could not save persistant state: {0}".format(e))
2326
if e.errno != errno.ENOENT:
2329
# Delete all clients, and settings from config
2330
while tcp_server.clients:
2331
name, client = tcp_server.clients.popitem()
2333
client.remove_from_connection()
2334
# Don't signal anything except ClientRemoved
2335
client.disable(quiet=True)
2338
mandos_dbus_service.ClientRemoved(client
2341
client_settings.clear()
806
2343
atexit.register(cleanup)
809
signal.signal(signal.SIGINT, signal.SIG_IGN)
810
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
811
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
813
for client in clients:
816
tcp_server = IPv6_TCPServer((server_settings["address"],
817
server_settings["port"]),
819
settings=server_settings,
2345
for client in tcp_server.clients.itervalues():
2348
mandos_dbus_service.ClientAdded(client.dbus_object_path)
2349
# Need to initiate checking of clients
2351
client.init_checker()
2355
tcp_server.server_activate()
821
2357
# Find out what port we got
822
2358
service.port = tcp_server.socket.getsockname()[1]
823
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
824
u" scope_id %d" % tcp_server.socket.getsockname())
2360
logger.info("Now listening on address %r, port %d,"
2361
" flowinfo %d, scope_id %d"
2362
% tcp_server.socket.getsockname())
2364
logger.info("Now listening on address %r, port %d"
2365
% tcp_server.socket.getsockname())
826
2367
#service.interface = tcp_server.socket.getsockname()[3]
829
2370
# From the Avahi example code
830
server.connect_to_signal("StateChanged", server_state_changed)
832
server_state_changed(server.GetState())
833
except dbus.exceptions.DBusException, error:
834
logger.critical(u"DBusException: %s", error)
2373
except dbus.exceptions.DBusException as error:
2374
logger.critical("DBusException: %s", error)
836
2377
# End of Avahi example code
838
2379
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
839
2380
lambda *args, **kwargs:
840
tcp_server.handle_request\
841
(*args[2:], **kwargs) or True)
2381
(tcp_server.handle_request
2382
(*args[2:], **kwargs) or True))
843
logger.debug(u"Starting main loop")
844
main_loop_started = True
2384
logger.debug("Starting main loop")
846
except AvahiError, error:
847
logger.critical(u"AvahiError: %s" + unicode(error))
2386
except AvahiError as error:
2387
logger.critical("AvahiError: %s", error)
849
2390
except KeyboardInterrupt:
2392
print("", file=sys.stderr)
2393
logger.debug("Server received KeyboardInterrupt")
2394
logger.debug("Server exiting")
2395
# Must run before the D-Bus bus name gets deregistered
853
2399
if __name__ == '__main__':