104
136
max_renames: integer; maximum number of renames
105
137
rename_count: integer; counter so we only rename after collisions
106
138
a sensible number of times
139
group: D-Bus Entry Group
141
bus: dbus.SystemBus()
108
143
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
109
type = None, port = None, TXT = None, domain = "",
110
host = "", max_renames = 32768):
144
servicetype = None, port = None, TXT = None,
145
domain = "", host = "", max_renames = 32768,
146
protocol = avahi.PROTO_UNSPEC, bus = None):
111
147
self.interface = interface
149
self.type = servicetype
151
self.TXT = TXT if TXT is not None else []
119
152
self.domain = domain
121
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
122
161
def rename(self):
123
162
"""Derived from the Avahi example code"""
124
163
if self.rename_count >= self.max_renames:
125
logger.critical(u"No suitable service name found after %i"
126
u" retries, exiting.", rename_count)
164
logger.critical("No suitable Zeroconf service name found"
165
" after %i retries, exiting.",
127
167
raise AvahiServiceError("Too many renames")
128
name = server.GetAlternativeServiceName(name)
129
logger.error(u"Changing name to %r ...", name)
130
syslogger.setFormatter(logging.Formatter\
131
('Mandos (%s): %%(levelname)s:'
132
' %%(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)
135
179
self.rename_count += 1
136
180
def remove(self):
137
181
"""Derived from the Avahi example code"""
138
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:
141
188
"""Derived from the Avahi example code"""
144
group = dbus.Interface\
145
(bus.get_object(avahi.DBUS_NAME,
146
server.EntryGroupNew()),
147
avahi.DBUS_INTERFACE_ENTRY_GROUP)
148
group.connect_to_signal('StateChanged',
149
entry_group_state_changed)
150
logger.debug(u"Adding service '%s' of type '%s' ...",
151
service.name, service.type)
153
self.interface, # interface
154
avahi.PROTO_INET6, # protocol
155
dbus.UInt32(0), # flags
156
self.name, self.type,
157
self.domain, self.host,
158
dbus.UInt16(self.port),
159
avahi.string_array_to_txt_array(self.TXT))
162
# From the Avahi example code:
163
group = None # our entry group
164
# 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))
167
285
class Client(object):
168
286
"""A representation of a client host served by this server.
170
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
171
307
fingerprint: string (40 or 32 hexadecimal digits); used to
172
308
uniquely identify the client
173
secret: bytestring; sent verbatim (over TLS) to client
174
host: string; available for use by the checker command
175
created: datetime.datetime(); object creation, not client host
176
last_checked_ok: datetime.datetime() or None if not yet checked OK
177
timeout: datetime.timedelta(); How long from last_checked_ok
178
until this client is invalid
179
interval: datetime.timedelta(); How often to start a new checker
180
stop_hook: If set, called by stop() as stop_hook(self)
181
checker: subprocess.Popen(); a running checker process used
182
to see if the client lives.
183
'None' if no process is running.
184
checker_initiator_tag: a gobject event source tag, or None
185
stop_initiator_tag: - '' -
186
checker_callback_tag: - '' -
187
checker_command: string; External command which is run to check if
188
client lives. %() expansions are done at
189
runtime with vars(self) as dict, so that for
190
instance %(name)s can be used in the command.
192
_timeout: Real variable for 'timeout'
193
_interval: Real variable for 'interval'
194
_timeout_milliseconds: Used when calling gobject.timeout_add()
195
_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
197
def _set_timeout(self, timeout):
198
"Setter function for 'timeout' attribute"
199
self._timeout = timeout
200
self._timeout_milliseconds = ((self.timeout.days
201
* 24 * 60 * 60 * 1000)
202
+ (self.timeout.seconds * 1000)
203
+ (self.timeout.microseconds
205
timeout = property(lambda self: self._timeout,
208
def _set_interval(self, interval):
209
"Setter function for 'interval' attribute"
210
self._interval = interval
211
self._interval_milliseconds = ((self.interval.days
212
* 24 * 60 * 60 * 1000)
213
+ (self.interval.seconds
215
+ (self.interval.microseconds
217
interval = property(lambda self: self._interval,
220
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):
221
349
"""Note: the 'checker' key in 'config' sets the
222
350
'checker_command' attribute and *not* the 'checker'
225
logger.debug(u"Creating client %r", self.name)
355
logger.debug("Creating client %r", self.name)
226
356
# Uppercase and remove spaces from fingerprint for later
227
357
# comparison purposes with return value from the fingerprint()
229
self.fingerprint = config["fingerprint"].upper()\
231
logger.debug(u" Fingerprint: %s", self.fingerprint)
359
self.fingerprint = (config["fingerprint"].upper()
361
logger.debug(" Fingerprint: %s", self.fingerprint)
232
362
if "secret" in config:
233
self.secret = config["secret"].decode(u"base64")
363
self.secret = config["secret"].decode("base64")
234
364
elif "secfile" in config:
235
sf = open(config["secfile"])
236
self.secret = sf.read()
365
with open(os.path.expanduser(os.path.expandvars
366
(config["secfile"])),
368
self.secret = secfile.read()
239
raise TypeError(u"No secret or secfile for client %s"
370
raise TypeError("No secret or secfile for client %s"
241
372
self.host = config.get("host", "")
242
self.created = datetime.datetime.now()
373
self.created = datetime.datetime.utcnow()
375
self.last_approval_request = None
376
self.last_enabled = datetime.datetime.utcnow()
243
377
self.last_checked_ok = None
378
self.last_checker_status = None
244
379
self.timeout = string_to_delta(config["timeout"])
380
self.extended_timeout = string_to_delta(config
381
["extended_timeout"])
245
382
self.interval = string_to_delta(config["interval"])
246
self.stop_hook = stop_hook
247
383
self.checker = None
248
384
self.checker_initiator_tag = None
249
self.stop_initiator_tag = None
385
self.disable_initiator_tag = None
386
self.expires = datetime.datetime.utcnow() + self.timeout
250
387
self.checker_callback_tag = None
251
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()
253
416
"""Start this client's checker and timeout hooks"""
254
# Schedule a new checker to be started an 'interval' from now,
255
# and every interval from then on.
256
self.checker_initiator_tag = gobject.timeout_add\
257
(self._interval_milliseconds,
259
# Also start a new checker *right now*.
261
# Schedule a stop() when 'timeout' has passed
262
self.stop_initiator_tag = gobject.timeout_add\
263
(self._timeout_milliseconds,
267
The possibility that a client might be restarted is left open,
268
but not currently used."""
269
# If this client doesn't have a secret, it is already stopped.
270
if hasattr(self, "secret") and self.secret:
271
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):
275
if getattr(self, "stop_initiator_tag", False):
276
gobject.source_remove(self.stop_initiator_tag)
277
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
278
438
if getattr(self, "checker_initiator_tag", False):
279
439
gobject.source_remove(self.checker_initiator_tag)
280
440
self.checker_initiator_tag = None
281
441
self.stop_checker()
284
443
# Do not run this again if called by a gobject.timeout_add
286
446
def __del__(self):
287
self.stop_hook = None
289
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):
290
464
"""The checker has completed, so take appropriate actions."""
291
now = datetime.datetime.now()
292
465
self.checker_callback_tag = None
293
466
self.checker = None
294
if os.WIFEXITED(condition) \
295
and (os.WEXITSTATUS(condition) == 0):
296
logger.info(u"Checker for %(name)s succeeded",
298
self.last_checked_ok = now
299
gobject.source_remove(self.stop_initiator_tag)
300
self.stop_initiator_tag = gobject.timeout_add\
301
(self._timeout_milliseconds,
303
elif not os.WIFEXITED(condition):
304
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?",
307
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()
309
501
def start_checker(self):
310
502
"""Start a new checker subprocess if one is not running.
311
504
If a checker already exists, leave it running and do
313
506
# The reason for not killing a running checker is that if we
354
580
self.checker_callback_tag = None
355
581
if getattr(self, "checker", None) is None:
357
logger.debug(u"Stopping checker for %(name)s", vars(self))
583
logger.debug("Stopping checker for %(name)s", vars(self))
359
585
os.kill(self.checker.pid, signal.SIGTERM)
361
587
#if self.checker.poll() is None:
362
588
# os.kill(self.checker.pid, signal.SIGKILL)
363
except OSError, error:
589
except OSError as error:
364
590
if error.errno != errno.ESRCH: # No such process
366
592
self.checker = None
367
def still_valid(self):
368
"""Has the timeout not yet passed for this client?"""
369
now = datetime.datetime.now()
370
if self.last_checked_ok is None:
371
return now < (self.created + self.timeout)
373
return now < (self.last_checked_ok + self.timeout)
376
def peer_certificate(session):
377
"Return the peer's OpenPGP certificate as a bytestring"
378
# If not an OpenPGP certificate...
379
if gnutls.library.functions.gnutls_certificate_type_get\
380
(session._c_object) \
381
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
382
# ...do the normal thing
383
return session.peer_certificate
384
list_size = ctypes.c_uint()
385
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
386
(session._c_object, ctypes.byref(list_size))
387
if list_size.value == 0:
390
return ctypes.string_at(cert.data, cert.size)
393
def fingerprint(openpgp):
394
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
395
# New GnuTLS "datum" with the OpenPGP public key
396
datum = gnutls.library.types.gnutls_datum_t\
397
(ctypes.cast(ctypes.c_char_p(openpgp),
398
ctypes.POINTER(ctypes.c_ubyte)),
399
ctypes.c_uint(len(openpgp)))
400
# New empty GnuTLS certificate
401
crt = gnutls.library.types.gnutls_openpgp_crt_t()
402
gnutls.library.functions.gnutls_openpgp_crt_init\
404
# Import the OpenPGP public key into the certificate
405
gnutls.library.functions.gnutls_openpgp_crt_import\
406
(crt, ctypes.byref(datum),
407
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
408
# New buffer for the fingerprint
409
buffer = ctypes.create_string_buffer(20)
410
buffer_length = ctypes.c_size_t()
411
# Get the fingerprint from the certificate into the buffer
412
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
413
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
414
# Deinit the certificate
415
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
416
# Convert the buffer to a Python bytestring
417
fpr = ctypes.string_at(buffer, buffer_length.value)
418
# Convert the bytestring to hexadecimal notation
419
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
423
class tcp_handler(SocketServer.BaseRequestHandler, object):
424
"""A TCP request handler class.
425
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.
426
1366
Note: This will run in its own forked process."""
428
1368
def handle(self):
429
logger.info(u"TCP connection from: %s",
430
unicode(self.client_address))
431
session = gnutls.connection.ClientSession\
432
(self.request, gnutls.connection.X509Credentials())
434
line = self.request.makefile().readline()
435
logger.debug(u"Protocol version: %r", line)
437
if int(line.strip().split()[0]) > 1:
439
except (ValueError, IndexError, RuntimeError), error:
440
logger.error(u"Unknown protocol version: %s", error)
443
# Note: gnutls.connection.X509Credentials is really a generic
444
# GnuTLS certificate credentials object so long as no X.509
445
# keys are added to it. Therefore, we can use it here despite
446
# using OpenPGP certificates.
448
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
449
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
451
priority = "NORMAL" # Fallback default, since this
453
if self.server.settings["priority"]:
454
priority = self.server.settings["priority"]
455
gnutls.library.functions.gnutls_priority_set_direct\
456
(session._c_object, priority, None);
460
except gnutls.errors.GNUTLSError, error:
461
logger.warning(u"Handshake failed: %s", error)
462
# Do not run session.bye() here: the session is not
463
# established. Just abandon the request.
466
fpr = fingerprint(peer_certificate(session))
467
except (TypeError, gnutls.errors.GNUTLSError), error:
468
logger.warning(u"Bad certificate: %s", error)
471
logger.debug(u"Fingerprint: %s", fpr)
473
for c in self.server.clients:
474
if c.fingerprint == fpr:
478
logger.warning(u"Client not found for fingerprint: %s",
482
# Have to check if client.still_valid(), since it is possible
483
# that the client timed out while establishing the GnuTLS
485
if not client.still_valid():
486
logger.warning(u"Client %(name)s is invalid",
491
while sent_size < len(client.secret):
492
sent = session.send(client.secret[sent_size:])
493
logger.debug(u"Sent: %d, remaining: %d",
494
sent, len(client.secret)
495
- (sent_size + sent))
500
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
501
"""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
503
settings: Server settings
504
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
506
address_family = socket.AF_INET6
507
def __init__(self, *args, **kwargs):
508
if "settings" in kwargs:
509
self.settings = kwargs["settings"]
510
del kwargs["settings"]
511
if "clients" in kwargs:
512
self.clients = kwargs["clients"]
513
del kwargs["clients"]
514
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)
515
1634
def server_bind(self):
516
1635
"""This overrides the normal server_bind() function
517
1636
to bind to an interface if one was specified, and also NOT to
518
1637
bind to an address or port if they were not specified."""
519
if self.settings["interface"]:
520
# 25 is from /usr/include/asm-i486/socket.h
521
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
523
self.socket.setsockopt(socket.SOL_SOCKET,
525
self.settings["interface"])
526
except socket.error, error:
527
if error[0] == errno.EPERM:
528
logger.error(u"No permission to"
529
u" bind to interface %s",
530
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",
533
1660
# Only bind(2) the socket if we really need to.
534
1661
if self.server_address[0] or self.server_address[1]:
535
1662
if not self.server_address[0]:
537
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,
538
1668
self.server_address[1])
539
1669
elif not self.server_address[1]:
540
1670
self.server_address = (self.server_address[0],
542
# if self.settings["interface"]:
1672
# if self.interface:
543
1673
# self.server_address = (self.server_address[0],
546
1676
# if_nametoindex
549
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)
552
1801
def string_to_delta(interval):
553
1802
"""Parse a string and return a datetime.timedelta
555
1804
>>> string_to_delta('7d')
556
1805
datetime.timedelta(7)
557
1806
>>> string_to_delta('60s')
694
1941
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
695
1942
"servicename": "Mandos",
698
1948
# Parse config file for server-global settings
699
server_config = ConfigParser.SafeConfigParser(server_defaults)
1949
server_config = configparser.SafeConfigParser(server_defaults)
700
1950
del server_defaults
701
server_config.read(os.path.join(options.configdir, "mandos.conf"))
702
server_section = "server"
1951
server_config.read(os.path.join(options.configdir,
703
1953
# Convert the SafeConfigParser object to a dict
704
server_settings = dict(server_config.items(server_section))
705
# Use getboolean on the boolean config option
706
server_settings["debug"] = server_config.getboolean\
707
(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",
708
1962
del server_config
710
1964
# Override the settings from the config file with command line
711
1965
# options, if set.
712
1966
for option in ("interface", "address", "port", "debug",
713
"priority", "servicename", "configdir"):
1967
"priority", "servicename", "configdir",
1968
"use_dbus", "use_ipv6", "debuglevel", "restore"):
714
1969
value = getattr(options, option)
715
1970
if value is not None:
716
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])
718
1977
# Now we have our good server settings in "server_settings"
1979
##################################################################
720
1982
debug = server_settings["debug"]
723
syslogger.setLevel(logging.WARNING)
724
console.setLevel(logging.WARNING)
1983
debuglevel = server_settings["debuglevel"]
1984
use_dbus = server_settings["use_dbus"]
1985
use_ipv6 = server_settings["use_ipv6"]
726
1987
if server_settings["servicename"] != "Mandos":
727
syslogger.setFormatter(logging.Formatter\
728
('Mandos (%s): %%(levelname)s:'
1988
syslogger.setFormatter(logging.Formatter
1989
('Mandos (%s) [%%(process)d]:'
1990
' %%(levelname)s: %%(message)s'
730
1991
% server_settings["servicename"]))
732
1993
# Parse config file with clients
733
client_defaults = { "timeout": "1h",
1994
client_defaults = { "timeout": "5m",
1995
"extended_timeout": "15m",
735
1997
"checker": "fping -q -- %%(host)s",
1999
"approval_delay": "0s",
2000
"approval_duration": "1s",
737
client_config = ConfigParser.SafeConfigParser(client_defaults)
2002
client_config = configparser.SafeConfigParser(client_defaults)
738
2003
client_config.read(os.path.join(server_settings["configdir"],
739
2004
"clients.conf"))
742
service = AvahiService(name = server_settings["servicename"],
743
type = "_mandos._tcp", );
744
if server_settings["interface"]:
745
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.
747
2081
global main_loop
750
2082
# From the Avahi example code
751
2083
DBusGMainLoop(set_as_default=True )
752
2084
main_loop = gobject.MainLoop()
753
2085
bus = dbus.SystemBus()
754
server = dbus.Interface(
755
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
756
avahi.DBUS_INTERFACE_SERVER )
757
2086
# End of Avahi example code
760
def remove_from_clients(client):
761
clients.remove(client)
763
logger.critical(u"No clients left, exiting")
766
clients.update(Set(Client(name = section,
767
stop_hook = remove_from_clients,
769
= dict(client_config.items(section)))
770
for section in client_config.sections()))
772
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")
776
logger.removeHandler(console)
779
pidfilename = "/var/run/mandos/mandos.pid"
782
pidfile = open(pidfilename, "w")
783
pidfile.write(str(pid) + "\n")
787
logger.error(u"Could not write %s file with PID %d",
788
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()
791
2292
"Cleanup function; run on exit"
793
# From the Avahi example code
794
if not group is None:
797
# End of Avahi example code
800
client = clients.pop()
801
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()
804
2343
atexit.register(cleanup)
807
signal.signal(signal.SIGINT, signal.SIG_IGN)
808
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
809
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
811
for client in clients:
814
tcp_server = IPv6_TCPServer((server_settings["address"],
815
server_settings["port"]),
817
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()
819
2357
# Find out what port we got
820
2358
service.port = tcp_server.socket.getsockname()[1]
821
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
822
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())
824
2367
#service.interface = tcp_server.socket.getsockname()[3]
827
2370
# From the Avahi example code
828
server.connect_to_signal("StateChanged", server_state_changed)
830
server_state_changed(server.GetState())
831
except dbus.exceptions.DBusException, error:
832
logger.critical(u"DBusException: %s", error)
2373
except dbus.exceptions.DBusException as error:
2374
logger.critical("DBusException: %s", error)
834
2377
# End of Avahi example code
836
2379
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
837
2380
lambda *args, **kwargs:
838
tcp_server.handle_request\
839
(*args[2:], **kwargs) or True)
2381
(tcp_server.handle_request
2382
(*args[2:], **kwargs) or True))
841
logger.debug(u"Starting main loop")
842
main_loop_started = True
2384
logger.debug("Starting main loop")
844
except AvahiError, error:
845
logger.critical(u"AvahiError: %s" + unicode(error))
2386
except AvahiError as error:
2387
logger.critical("AvahiError: %s", error)
847
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
851
2399
if __name__ == '__main__':