106
126
max_renames: integer; maximum number of renames
107
127
rename_count: integer; counter so we only rename after collisions
108
128
a sensible number of times
129
group: D-Bus Entry Group
131
bus: dbus.SystemBus()
110
133
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
111
type = None, port = None, TXT = None, domain = "",
112
host = "", max_renames = 32768):
134
servicetype = None, port = None, TXT = None,
135
domain = u"", host = u"", max_renames = 32768,
136
protocol = avahi.PROTO_UNSPEC, bus = None):
113
137
self.interface = interface
139
self.type = servicetype
141
self.TXT = TXT if TXT is not None else []
121
142
self.domain = domain
123
144
self.rename_count = 0
124
145
self.max_renames = max_renames
146
self.protocol = protocol
147
self.group = None # our entry group
125
150
def rename(self):
126
151
"""Derived from the Avahi example code"""
127
152
if self.rename_count >= self.max_renames:
128
153
logger.critical(u"No suitable Zeroconf service name found"
129
154
u" after %i retries, exiting.",
131
raise AvahiServiceError("Too many renames")
132
self.name = server.GetAlternativeServiceName(self.name)
156
raise AvahiServiceError(u"Too many renames")
157
self.name = self.server.GetAlternativeServiceName(self.name)
133
158
logger.info(u"Changing Zeroconf service name to %r ...",
135
syslogger.setFormatter(logging.Formatter\
136
('Mandos (%s): %%(levelname)s:'
137
' %%(message)s' % self.name))
160
syslogger.setFormatter(logging.Formatter
161
(u'Mandos (%s) [%%(process)d]:'
162
u' %%(levelname)s: %%(message)s'
140
166
self.rename_count += 1
141
167
def remove(self):
142
168
"""Derived from the Avahi example code"""
143
if group is not None:
169
if self.group is not None:
146
172
"""Derived from the Avahi example code"""
149
group = dbus.Interface\
150
(bus.get_object(avahi.DBUS_NAME,
151
server.EntryGroupNew()),
152
avahi.DBUS_INTERFACE_ENTRY_GROUP)
153
group.connect_to_signal('StateChanged',
154
entry_group_state_changed)
173
if self.group is None:
174
self.group = dbus.Interface(
175
self.bus.get_object(avahi.DBUS_NAME,
176
self.server.EntryGroupNew()),
177
avahi.DBUS_INTERFACE_ENTRY_GROUP)
178
self.group.connect_to_signal('StateChanged',
180
.entry_group_state_changed)
155
181
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
156
service.name, service.type)
158
self.interface, # interface
159
avahi.PROTO_INET6, # protocol
160
dbus.UInt32(0), # flags
161
self.name, self.type,
162
self.domain, self.host,
163
dbus.UInt16(self.port),
164
avahi.string_array_to_txt_array(self.TXT))
167
# From the Avahi example code:
168
group = None # our entry group
169
# End of Avahi example code
182
self.name, self.type)
183
self.group.AddService(
186
dbus.UInt32(0), # flags
187
self.name, self.type,
188
self.domain, self.host,
189
dbus.UInt16(self.port),
190
avahi.string_array_to_txt_array(self.TXT))
192
def entry_group_state_changed(self, state, error):
193
"""Derived from the Avahi example code"""
194
logger.debug(u"Avahi state change: %i", state)
196
if state == avahi.ENTRY_GROUP_ESTABLISHED:
197
logger.debug(u"Zeroconf service established.")
198
elif state == avahi.ENTRY_GROUP_COLLISION:
199
logger.warning(u"Zeroconf service name collision.")
201
elif state == avahi.ENTRY_GROUP_FAILURE:
202
logger.critical(u"Avahi: Error in group state changed %s",
204
raise AvahiGroupError(u"State changed: %s"
207
"""Derived from the Avahi example code"""
208
if self.group is not None:
211
def server_state_changed(self, state):
212
"""Derived from the Avahi example code"""
213
if state == avahi.SERVER_COLLISION:
214
logger.error(u"Zeroconf server name collision")
216
elif state == avahi.SERVER_RUNNING:
219
"""Derived from the Avahi example code"""
220
if self.server is None:
221
self.server = dbus.Interface(
222
self.bus.get_object(avahi.DBUS_NAME,
223
avahi.DBUS_PATH_SERVER),
224
avahi.DBUS_INTERFACE_SERVER)
225
self.server.connect_to_signal(u"StateChanged",
226
self.server_state_changed)
227
self.server_state_changed(self.server.GetState())
172
230
class Client(object):
173
231
"""A representation of a client host served by this server.
175
name: string; from the config file, used in log messages
234
name: string; from the config file, used in log messages and
176
236
fingerprint: string (40 or 32 hexadecimal digits); used to
177
237
uniquely identify the client
178
secret: bytestring; sent verbatim (over TLS) to client
179
host: string; available for use by the checker command
180
created: datetime.datetime(); object creation, not client host
181
last_checked_ok: datetime.datetime() or None if not yet checked OK
182
timeout: datetime.timedelta(); How long from last_checked_ok
183
until this client is invalid
184
interval: datetime.timedelta(); How often to start a new checker
185
stop_hook: If set, called by stop() as stop_hook(self)
186
checker: subprocess.Popen(); a running checker process used
187
to see if the client lives.
188
'None' if no process is running.
238
secret: bytestring; sent verbatim (over TLS) to client
239
host: string; available for use by the checker command
240
created: datetime.datetime(); (UTC) object creation
241
last_enabled: datetime.datetime(); (UTC)
243
last_checked_ok: datetime.datetime(); (UTC) or None
244
timeout: datetime.timedelta(); How long from last_checked_ok
245
until this client is invalid
246
interval: datetime.timedelta(); How often to start a new checker
247
disable_hook: If set, called by disable() as disable_hook(self)
248
checker: subprocess.Popen(); a running checker process used
249
to see if the client lives.
250
'None' if no process is running.
189
251
checker_initiator_tag: a gobject event source tag, or None
190
stop_initiator_tag: - '' -
252
disable_initiator_tag: - '' -
191
253
checker_callback_tag: - '' -
192
254
checker_command: string; External command which is run to check if
193
255
client lives. %() expansions are done at
194
256
runtime with vars(self) as dict, so that for
195
257
instance %(name)s can be used in the command.
197
_timeout: Real variable for 'timeout'
198
_interval: Real variable for 'interval'
199
_timeout_milliseconds: Used when calling gobject.timeout_add()
200
_interval_milliseconds: - '' -
258
current_checker_command: string; current running checker_command
202
def _set_timeout(self, timeout):
203
"Setter function for 'timeout' attribute"
204
self._timeout = timeout
205
self._timeout_milliseconds = ((self.timeout.days
206
* 24 * 60 * 60 * 1000)
207
+ (self.timeout.seconds * 1000)
208
+ (self.timeout.microseconds
210
timeout = property(lambda self: self._timeout,
213
def _set_interval(self, interval):
214
"Setter function for 'interval' attribute"
215
self._interval = interval
216
self._interval_milliseconds = ((self.interval.days
217
* 24 * 60 * 60 * 1000)
218
+ (self.interval.seconds
220
+ (self.interval.microseconds
222
interval = property(lambda self: self._interval,
225
def __init__(self, name = None, stop_hook=None, config={}):
262
def _timedelta_to_milliseconds(td):
263
"Convert a datetime.timedelta() to milliseconds"
264
return ((td.days * 24 * 60 * 60 * 1000)
265
+ (td.seconds * 1000)
266
+ (td.microseconds // 1000))
268
def timeout_milliseconds(self):
269
"Return the 'timeout' attribute in milliseconds"
270
return self._timedelta_to_milliseconds(self.timeout)
272
def interval_milliseconds(self):
273
"Return the 'interval' attribute in milliseconds"
274
return self._timedelta_to_milliseconds(self.interval)
276
def __init__(self, name = None, disable_hook=None, config=None):
226
277
"""Note: the 'checker' key in 'config' sets the
227
278
'checker_command' attribute and *not* the 'checker'
230
283
logger.debug(u"Creating client %r", self.name)
231
284
# Uppercase and remove spaces from fingerprint for later
232
285
# comparison purposes with return value from the fingerprint()
234
self.fingerprint = config["fingerprint"].upper()\
287
self.fingerprint = (config[u"fingerprint"].upper()
236
289
logger.debug(u" Fingerprint: %s", self.fingerprint)
237
if "secret" in config:
238
self.secret = config["secret"].decode(u"base64")
239
elif "secfile" in config:
240
sf = open(config["secfile"])
241
self.secret = sf.read()
290
if u"secret" in config:
291
self.secret = config[u"secret"].decode(u"base64")
292
elif u"secfile" in config:
293
with closing(open(os.path.expanduser
295
(config[u"secfile"])),
297
self.secret = secfile.read()
244
299
raise TypeError(u"No secret or secfile for client %s"
246
self.host = config.get("host", "")
247
self.created = datetime.datetime.now()
301
self.host = config.get(u"host", u"")
302
self.created = datetime.datetime.utcnow()
304
self.last_enabled = None
248
305
self.last_checked_ok = None
249
self.timeout = string_to_delta(config["timeout"])
250
self.interval = string_to_delta(config["interval"])
251
self.stop_hook = stop_hook
306
self.timeout = string_to_delta(config[u"timeout"])
307
self.interval = string_to_delta(config[u"interval"])
308
self.disable_hook = disable_hook
252
309
self.checker = None
253
310
self.checker_initiator_tag = None
254
self.stop_initiator_tag = None
311
self.disable_initiator_tag = None
255
312
self.checker_callback_tag = None
256
self.check_command = config["checker"]
313
self.checker_command = config[u"checker"]
314
self.current_checker_command = None
315
self.last_connect = None
258
318
"""Start this client's checker and timeout hooks"""
319
if getattr(self, u"enabled", False):
322
self.last_enabled = datetime.datetime.utcnow()
259
323
# Schedule a new checker to be started an 'interval' from now,
260
324
# and every interval from then on.
261
self.checker_initiator_tag = gobject.timeout_add\
262
(self._interval_milliseconds,
325
self.checker_initiator_tag = (gobject.timeout_add
326
(self.interval_milliseconds(),
264
328
# Also start a new checker *right now*.
265
329
self.start_checker()
266
# Schedule a stop() when 'timeout' has passed
267
self.stop_initiator_tag = gobject.timeout_add\
268
(self._timeout_milliseconds,
272
The possibility that a client might be restarted is left open,
273
but not currently used."""
274
# If this client doesn't have a secret, it is already stopped.
275
if hasattr(self, "secret") and self.secret:
276
logger.info(u"Stopping client %s", self.name)
330
# Schedule a disable() when 'timeout' has passed
331
self.disable_initiator_tag = (gobject.timeout_add
332
(self.timeout_milliseconds(),
337
"""Disable this client."""
338
if not getattr(self, "enabled", False):
280
if getattr(self, "stop_initiator_tag", False):
281
gobject.source_remove(self.stop_initiator_tag)
282
self.stop_initiator_tag = None
283
if getattr(self, "checker_initiator_tag", False):
340
logger.info(u"Disabling client %s", self.name)
341
if getattr(self, u"disable_initiator_tag", False):
342
gobject.source_remove(self.disable_initiator_tag)
343
self.disable_initiator_tag = None
344
if getattr(self, u"checker_initiator_tag", False):
284
345
gobject.source_remove(self.checker_initiator_tag)
285
346
self.checker_initiator_tag = None
286
347
self.stop_checker()
348
if self.disable_hook:
349
self.disable_hook(self)
289
351
# Do not run this again if called by a gobject.timeout_add
291
354
def __del__(self):
292
self.stop_hook = None
294
def checker_callback(self, pid, condition):
355
self.disable_hook = None
358
def checker_callback(self, pid, condition, command):
295
359
"""The checker has completed, so take appropriate actions."""
296
now = datetime.datetime.now()
297
360
self.checker_callback_tag = None
298
361
self.checker = None
299
if os.WIFEXITED(condition) \
300
and (os.WEXITSTATUS(condition) == 0):
301
logger.info(u"Checker for %(name)s succeeded",
303
self.last_checked_ok = now
304
gobject.source_remove(self.stop_initiator_tag)
305
self.stop_initiator_tag = gobject.timeout_add\
306
(self._timeout_milliseconds,
308
elif not os.WIFEXITED(condition):
362
if os.WIFEXITED(condition):
363
exitstatus = os.WEXITSTATUS(condition)
365
logger.info(u"Checker for %(name)s succeeded",
369
logger.info(u"Checker for %(name)s failed",
309
372
logger.warning(u"Checker for %(name)s crashed?",
312
logger.info(u"Checker for %(name)s failed",
375
def checked_ok(self):
376
"""Bump up the timeout for this client.
378
This should only be called when the client has been seen,
381
self.last_checked_ok = datetime.datetime.utcnow()
382
gobject.source_remove(self.disable_initiator_tag)
383
self.disable_initiator_tag = (gobject.timeout_add
384
(self.timeout_milliseconds(),
314
387
def start_checker(self):
315
388
"""Start a new checker subprocess if one is not running.
316
390
If a checker already exists, leave it running and do
318
392
# The reason for not killing a running checker is that if we
373
474
if error.errno != errno.ESRCH: # No such process
375
476
self.checker = None
376
478
def still_valid(self):
377
479
"""Has the timeout not yet passed for this client?"""
378
now = datetime.datetime.now()
480
if not getattr(self, u"enabled", False):
482
now = datetime.datetime.utcnow()
379
483
if self.last_checked_ok is None:
380
484
return now < (self.created + self.timeout)
382
486
return now < (self.last_checked_ok + self.timeout)
385
def peer_certificate(session):
386
"Return the peer's OpenPGP certificate as a bytestring"
387
# If not an OpenPGP certificate...
388
if gnutls.library.functions.gnutls_certificate_type_get\
389
(session._c_object) \
390
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
391
# ...do the normal thing
392
return session.peer_certificate
393
list_size = ctypes.c_uint()
394
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
395
(session._c_object, ctypes.byref(list_size))
396
if list_size.value == 0:
399
return ctypes.string_at(cert.data, cert.size)
402
def fingerprint(openpgp):
403
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
404
# New GnuTLS "datum" with the OpenPGP public key
405
datum = gnutls.library.types.gnutls_datum_t\
406
(ctypes.cast(ctypes.c_char_p(openpgp),
407
ctypes.POINTER(ctypes.c_ubyte)),
408
ctypes.c_uint(len(openpgp)))
409
# New empty GnuTLS certificate
410
crt = gnutls.library.types.gnutls_openpgp_crt_t()
411
gnutls.library.functions.gnutls_openpgp_crt_init\
413
# Import the OpenPGP public key into the certificate
414
gnutls.library.functions.gnutls_openpgp_crt_import\
415
(crt, ctypes.byref(datum),
416
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
417
# Verify the self signature in the key
418
crtverify = ctypes.c_uint();
419
gnutls.library.functions.gnutls_openpgp_crt_verify_self\
420
(crt, 0, ctypes.byref(crtverify))
421
if crtverify.value != 0:
422
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
423
raise gnutls.errors.CertificateSecurityError("Verify failed")
424
# New buffer for the fingerprint
425
buffer = ctypes.create_string_buffer(20)
426
buffer_length = ctypes.c_size_t()
427
# Get the fingerprint from the certificate into the buffer
428
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
429
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
430
# Deinit the certificate
431
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
432
# Convert the buffer to a Python bytestring
433
fpr = ctypes.string_at(buffer, buffer_length.value)
434
# Convert the bytestring to hexadecimal notation
435
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
439
class tcp_handler(SocketServer.BaseRequestHandler, object):
440
"""A TCP request handler class.
441
Instantiated by IPv6_TCPServer for each request to handle it.
489
def dbus_service_property(dbus_interface, signature=u"v",
490
access=u"readwrite", byte_arrays=False):
491
"""Decorators for marking methods of a DBusObjectWithProperties to
492
become properties on the D-Bus.
494
The decorated method will be called with no arguments by "Get"
495
and with one argument by "Set".
497
The parameters, where they are supported, are the same as
498
dbus.service.method, except there is only "signature", since the
499
type from Get() and the type sent to Set() is the same.
502
func._dbus_is_property = True
503
func._dbus_interface = dbus_interface
504
func._dbus_signature = signature
505
func._dbus_access = access
506
func._dbus_name = func.__name__
507
if func._dbus_name.endswith(u"_dbus_property"):
508
func._dbus_name = func._dbus_name[:-14]
509
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
514
class DBusPropertyException(dbus.exceptions.DBusException):
515
"""A base class for D-Bus property-related exceptions
517
def __unicode__(self):
518
return unicode(str(self))
521
class DBusPropertyAccessException(DBusPropertyException):
522
"""A property's access permissions disallows an operation.
527
class DBusPropertyNotFound(DBusPropertyException):
528
"""An attempt was made to access a non-existing property.
533
class DBusObjectWithProperties(dbus.service.Object):
534
"""A D-Bus object with properties.
536
Classes inheriting from this can use the dbus_service_property
537
decorator to expose methods as D-Bus properties. It exposes the
538
standard Get(), Set(), and GetAll() methods on the D-Bus.
542
def _is_dbus_property(obj):
543
return getattr(obj, u"_dbus_is_property", False)
545
def _get_all_dbus_properties(self):
546
"""Returns a generator of (name, attribute) pairs
548
return ((prop._dbus_name, prop)
550
inspect.getmembers(self, self._is_dbus_property))
552
def _get_dbus_property(self, interface_name, property_name):
553
"""Returns a bound method if one exists which is a D-Bus
554
property with the specified name and interface.
556
for name in (property_name,
557
property_name + u"_dbus_property"):
558
prop = getattr(self, name, None)
560
or not self._is_dbus_property(prop)
561
or prop._dbus_name != property_name
562
or (interface_name and prop._dbus_interface
563
and interface_name != prop._dbus_interface)):
567
raise DBusPropertyNotFound(self.dbus_object_path + u":"
568
+ interface_name + u"."
571
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
573
def Get(self, interface_name, property_name):
574
"""Standard D-Bus property Get() method, see D-Bus standard.
576
prop = self._get_dbus_property(interface_name, property_name)
577
if prop._dbus_access == u"write":
578
raise DBusPropertyAccessException(property_name)
580
if not hasattr(value, u"variant_level"):
582
return type(value)(value, variant_level=value.variant_level+1)
584
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
585
def Set(self, interface_name, property_name, value):
586
"""Standard D-Bus property Set() method, see D-Bus standard.
588
prop = self._get_dbus_property(interface_name, property_name)
589
if prop._dbus_access == u"read":
590
raise DBusPropertyAccessException(property_name)
591
if prop._dbus_get_args_options[u"byte_arrays"]:
592
value = dbus.ByteArray(''.join(unichr(byte)
596
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
597
out_signature=u"a{sv}")
598
def GetAll(self, interface_name):
599
"""Standard D-Bus property GetAll() method, see D-Bus
602
Note: Will not include properties with access="write".
605
for name, prop in self._get_all_dbus_properties():
607
and interface_name != prop._dbus_interface):
608
# Interface non-empty but did not match
610
# Ignore write-only properties
611
if prop._dbus_access == u"write":
614
if not hasattr(value, u"variant_level"):
617
all[name] = type(value)(value, variant_level=
618
value.variant_level+1)
619
return dbus.Dictionary(all, signature=u"sv")
621
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
623
path_keyword='object_path',
624
connection_keyword='connection')
625
def Introspect(self, object_path, connection):
626
"""Standard D-Bus method, overloaded to insert property tags.
628
xmlstring = dbus.service.Object.Introspect(self, object_path,
630
document = xml.dom.minidom.parseString(xmlstring)
632
def make_tag(document, name, prop):
633
e = document.createElement(u"property")
634
e.setAttribute(u"name", name)
635
e.setAttribute(u"type", prop._dbus_signature)
636
e.setAttribute(u"access", prop._dbus_access)
638
for if_tag in document.getElementsByTagName(u"interface"):
639
for tag in (make_tag(document, name, prop)
641
in self._get_all_dbus_properties()
642
if prop._dbus_interface
643
== if_tag.getAttribute(u"name")):
644
if_tag.appendChild(tag)
645
# Add the names to the return values for the
646
# "org.freedesktop.DBus.Properties" methods
647
if (if_tag.getAttribute(u"name")
648
== u"org.freedesktop.DBus.Properties"):
649
for cn in if_tag.getElementsByTagName(u"method"):
650
if cn.getAttribute(u"name") == u"Get":
651
for arg in cn.getElementsByTagName(u"arg"):
652
if (arg.getAttribute(u"direction")
654
arg.setAttribute(u"name", u"value")
655
elif cn.getAttribute(u"name") == u"GetAll":
656
for arg in cn.getElementsByTagName(u"arg"):
657
if (arg.getAttribute(u"direction")
659
arg.setAttribute(u"name", u"props")
660
xmlstring = document.toxml(u"utf-8")
665
class ClientDBus(Client, DBusObjectWithProperties):
666
"""A Client class using D-Bus
669
dbus_object_path: dbus.ObjectPath
670
bus: dbus.SystemBus()
672
# dbus.service.Object doesn't use super(), so we can't either.
674
def __init__(self, bus = None, *args, **kwargs):
676
Client.__init__(self, *args, **kwargs)
677
# Only now, when this client is initialized, can it show up on
679
self.dbus_object_path = (dbus.ObjectPath
681
+ self.name.replace(u".", u"_")))
682
DBusObjectWithProperties.__init__(self, self.bus,
683
self.dbus_object_path)
686
def _datetime_to_dbus(dt, variant_level=0):
687
"""Convert a UTC datetime.datetime() to a D-Bus type."""
688
return dbus.String(dt.isoformat(),
689
variant_level=variant_level)
692
oldstate = getattr(self, u"enabled", False)
693
r = Client.enable(self)
694
if oldstate != self.enabled:
696
self.PropertyChanged(dbus.String(u"enabled"),
697
dbus.Boolean(True, variant_level=1))
698
self.PropertyChanged(
699
dbus.String(u"last_enabled"),
700
self._datetime_to_dbus(self.last_enabled,
704
def disable(self, signal = True):
705
oldstate = getattr(self, u"enabled", False)
706
r = Client.disable(self)
707
if signal and oldstate != self.enabled:
709
self.PropertyChanged(dbus.String(u"enabled"),
710
dbus.Boolean(False, variant_level=1))
713
def __del__(self, *args, **kwargs):
715
self.remove_from_connection()
718
if hasattr(DBusObjectWithProperties, u"__del__"):
719
DBusObjectWithProperties.__del__(self, *args, **kwargs)
720
Client.__del__(self, *args, **kwargs)
722
def checker_callback(self, pid, condition, command,
724
self.checker_callback_tag = None
727
self.PropertyChanged(dbus.String(u"checker_running"),
728
dbus.Boolean(False, variant_level=1))
729
if os.WIFEXITED(condition):
730
exitstatus = os.WEXITSTATUS(condition)
732
self.CheckerCompleted(dbus.Int16(exitstatus),
733
dbus.Int64(condition),
734
dbus.String(command))
737
self.CheckerCompleted(dbus.Int16(-1),
738
dbus.Int64(condition),
739
dbus.String(command))
741
return Client.checker_callback(self, pid, condition, command,
744
def checked_ok(self, *args, **kwargs):
745
r = Client.checked_ok(self, *args, **kwargs)
747
self.PropertyChanged(
748
dbus.String(u"last_checked_ok"),
749
(self._datetime_to_dbus(self.last_checked_ok,
753
def start_checker(self, *args, **kwargs):
754
old_checker = self.checker
755
if self.checker is not None:
756
old_checker_pid = self.checker.pid
758
old_checker_pid = None
759
r = Client.start_checker(self, *args, **kwargs)
760
# Only if new checker process was started
761
if (self.checker is not None
762
and old_checker_pid != self.checker.pid):
764
self.CheckerStarted(self.current_checker_command)
765
self.PropertyChanged(
766
dbus.String(u"checker_running"),
767
dbus.Boolean(True, variant_level=1))
770
def stop_checker(self, *args, **kwargs):
771
old_checker = getattr(self, u"checker", None)
772
r = Client.stop_checker(self, *args, **kwargs)
773
if (old_checker is not None
774
and getattr(self, u"checker", None) is None):
775
self.PropertyChanged(dbus.String(u"checker_running"),
776
dbus.Boolean(False, variant_level=1))
779
## D-Bus methods & signals
780
_interface = u"se.bsnet.fukt.Mandos.Client"
783
@dbus.service.method(_interface)
785
return self.checked_ok()
787
# CheckerCompleted - signal
788
@dbus.service.signal(_interface, signature=u"nxs")
789
def CheckerCompleted(self, exitcode, waitstatus, command):
793
# CheckerStarted - signal
794
@dbus.service.signal(_interface, signature=u"s")
795
def CheckerStarted(self, command):
799
# PropertyChanged - signal
800
@dbus.service.signal(_interface, signature=u"sv")
801
def PropertyChanged(self, property, value):
805
# ReceivedSecret - signal
806
@dbus.service.signal(_interface)
807
def ReceivedSecret(self):
812
@dbus.service.signal(_interface)
818
@dbus.service.method(_interface)
823
# StartChecker - method
824
@dbus.service.method(_interface)
825
def StartChecker(self):
830
@dbus.service.method(_interface)
835
# StopChecker - method
836
@dbus.service.method(_interface)
837
def StopChecker(self):
841
@dbus_service_property(_interface, signature=u"s", access=u"read")
842
def name_dbus_property(self):
843
return dbus.String(self.name)
845
# fingerprint - property
846
@dbus_service_property(_interface, signature=u"s", access=u"read")
847
def fingerprint_dbus_property(self):
848
return dbus.String(self.fingerprint)
851
@dbus_service_property(_interface, signature=u"s",
853
def host_dbus_property(self, value=None):
854
if value is None: # get
855
return dbus.String(self.host)
858
self.PropertyChanged(dbus.String(u"host"),
859
dbus.String(value, variant_level=1))
862
@dbus_service_property(_interface, signature=u"s", access=u"read")
863
def created_dbus_property(self):
864
return dbus.String(self._datetime_to_dbus(self.created))
866
# last_enabled - property
867
@dbus_service_property(_interface, signature=u"s", access=u"read")
868
def last_enabled_dbus_property(self):
869
if self.last_enabled is None:
870
return dbus.String(u"")
871
return dbus.String(self._datetime_to_dbus(self.last_enabled))
874
@dbus_service_property(_interface, signature=u"b",
876
def enabled_dbus_property(self, value=None):
877
if value is None: # get
878
return dbus.Boolean(self.enabled)
884
# last_checked_ok - property
885
@dbus_service_property(_interface, signature=u"s",
887
def last_checked_ok_dbus_property(self, value=None):
888
if value is not None:
891
if self.last_checked_ok is None:
892
return dbus.String(u"")
893
return dbus.String(self._datetime_to_dbus(self
897
@dbus_service_property(_interface, signature=u"t",
899
def timeout_dbus_property(self, value=None):
900
if value is None: # get
901
return dbus.UInt64(self.timeout_milliseconds())
902
self.timeout = datetime.timedelta(0, 0, 0, value)
904
self.PropertyChanged(dbus.String(u"timeout"),
905
dbus.UInt64(value, variant_level=1))
906
if getattr(self, u"disable_initiator_tag", None) is None:
909
gobject.source_remove(self.disable_initiator_tag)
910
self.disable_initiator_tag = None
912
_timedelta_to_milliseconds((self
918
# The timeout has passed
921
self.disable_initiator_tag = (gobject.timeout_add
922
(time_to_die, self.disable))
924
# interval - property
925
@dbus_service_property(_interface, signature=u"t",
927
def interval_dbus_property(self, value=None):
928
if value is None: # get
929
return dbus.UInt64(self.interval_milliseconds())
930
self.interval = datetime.timedelta(0, 0, 0, value)
932
self.PropertyChanged(dbus.String(u"interval"),
933
dbus.UInt64(value, variant_level=1))
934
if getattr(self, u"checker_initiator_tag", None) is None:
936
# Reschedule checker run
937
gobject.source_remove(self.checker_initiator_tag)
938
self.checker_initiator_tag = (gobject.timeout_add
939
(value, self.start_checker))
940
self.start_checker() # Start one now, too
943
@dbus_service_property(_interface, signature=u"s",
945
def checker_dbus_property(self, value=None):
946
if value is None: # get
947
return dbus.String(self.checker_command)
948
self.checker_command = value
950
self.PropertyChanged(dbus.String(u"checker"),
951
dbus.String(self.checker_command,
954
# checker_running - property
955
@dbus_service_property(_interface, signature=u"b",
957
def checker_running_dbus_property(self, value=None):
958
if value is None: # get
959
return dbus.Boolean(self.checker is not None)
965
# object_path - property
966
@dbus_service_property(_interface, signature=u"o", access=u"read")
967
def object_path_dbus_property(self):
968
return self.dbus_object_path # is already a dbus.ObjectPath
971
@dbus_service_property(_interface, signature=u"ay",
972
access=u"write", byte_arrays=True)
973
def secret_dbus_property(self, value):
974
self.secret = str(value)
979
class ClientHandler(socketserver.BaseRequestHandler, object):
980
"""A class to handle client connections.
982
Instantiated once for each connection to handle it.
442
983
Note: This will run in its own forked process."""
444
985
def handle(self):
445
986
logger.info(u"TCP connection from: %s",
446
unicode(self.client_address))
447
session = gnutls.connection.ClientSession\
448
(self.request, gnutls.connection.X509Credentials())
450
line = self.request.makefile().readline()
451
logger.debug(u"Protocol version: %r", line)
453
if int(line.strip().split()[0]) > 1:
455
except (ValueError, IndexError, RuntimeError), error:
456
logger.error(u"Unknown protocol version: %s", error)
459
# Note: gnutls.connection.X509Credentials is really a generic
460
# GnuTLS certificate credentials object so long as no X.509
461
# keys are added to it. Therefore, we can use it here despite
462
# using OpenPGP certificates.
464
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
465
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
467
priority = "NORMAL" # Fallback default, since this
469
if self.server.settings["priority"]:
470
priority = self.server.settings["priority"]
471
gnutls.library.functions.gnutls_priority_set_direct\
472
(session._c_object, priority, None);
476
except gnutls.errors.GNUTLSError, error:
477
logger.warning(u"Handshake failed: %s", error)
478
# Do not run session.bye() here: the session is not
479
# established. Just abandon the request.
482
fpr = fingerprint(peer_certificate(session))
483
except (TypeError, gnutls.errors.GNUTLSError), error:
484
logger.warning(u"Bad certificate: %s", error)
487
logger.debug(u"Fingerprint: %s", fpr)
489
for c in self.server.clients:
490
if c.fingerprint == fpr:
494
logger.warning(u"Client not found for fingerprint: %s",
498
# Have to check if client.still_valid(), since it is possible
499
# that the client timed out while establishing the GnuTLS
501
if not client.still_valid():
502
logger.warning(u"Client %(name)s is invalid",
507
while sent_size < len(client.secret):
508
sent = session.send(client.secret[sent_size:])
509
logger.debug(u"Sent: %d, remaining: %d",
510
sent, len(client.secret)
511
- (sent_size + sent))
516
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
517
"""IPv6 TCP server. Accepts 'None' as address and/or port.
987
unicode(self.client_address))
988
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
989
# Open IPC pipe to parent process
990
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
991
session = (gnutls.connection
992
.ClientSession(self.request,
996
line = self.request.makefile().readline()
997
logger.debug(u"Protocol version: %r", line)
999
if int(line.strip().split()[0]) > 1:
1001
except (ValueError, IndexError, RuntimeError), error:
1002
logger.error(u"Unknown protocol version: %s", error)
1005
# Note: gnutls.connection.X509Credentials is really a
1006
# generic GnuTLS certificate credentials object so long as
1007
# no X.509 keys are added to it. Therefore, we can use it
1008
# here despite using OpenPGP certificates.
1010
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1011
# u"+AES-256-CBC", u"+SHA1",
1012
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1014
# Use a fallback default, since this MUST be set.
1015
priority = self.server.gnutls_priority
1016
if priority is None:
1017
priority = u"NORMAL"
1018
(gnutls.library.functions
1019
.gnutls_priority_set_direct(session._c_object,
1024
except gnutls.errors.GNUTLSError, error:
1025
logger.warning(u"Handshake failed: %s", error)
1026
# Do not run session.bye() here: the session is not
1027
# established. Just abandon the request.
1029
logger.debug(u"Handshake succeeded")
1031
fpr = self.fingerprint(self.peer_certificate(session))
1032
except (TypeError, gnutls.errors.GNUTLSError), error:
1033
logger.warning(u"Bad certificate: %s", error)
1036
logger.debug(u"Fingerprint: %s", fpr)
1038
for c in self.server.clients:
1039
if c.fingerprint == fpr:
1043
ipc.write(u"NOTFOUND %s %s\n"
1044
% (fpr, unicode(self.client_address)))
1047
# Have to check if client.still_valid(), since it is
1048
# possible that the client timed out while establishing
1049
# the GnuTLS session.
1050
if not client.still_valid():
1051
ipc.write(u"INVALID %s\n" % client.name)
1054
ipc.write(u"SENDING %s\n" % client.name)
1056
while sent_size < len(client.secret):
1057
sent = session.send(client.secret[sent_size:])
1058
logger.debug(u"Sent: %d, remaining: %d",
1059
sent, len(client.secret)
1060
- (sent_size + sent))
1065
def peer_certificate(session):
1066
"Return the peer's OpenPGP certificate as a bytestring"
1067
# If not an OpenPGP certificate...
1068
if (gnutls.library.functions
1069
.gnutls_certificate_type_get(session._c_object)
1070
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1071
# ...do the normal thing
1072
return session.peer_certificate
1073
list_size = ctypes.c_uint(1)
1074
cert_list = (gnutls.library.functions
1075
.gnutls_certificate_get_peers
1076
(session._c_object, ctypes.byref(list_size)))
1077
if not bool(cert_list) and list_size.value != 0:
1078
raise gnutls.errors.GNUTLSError(u"error getting peer"
1080
if list_size.value == 0:
1083
return ctypes.string_at(cert.data, cert.size)
1086
def fingerprint(openpgp):
1087
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1088
# New GnuTLS "datum" with the OpenPGP public key
1089
datum = (gnutls.library.types
1090
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1093
ctypes.c_uint(len(openpgp))))
1094
# New empty GnuTLS certificate
1095
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1096
(gnutls.library.functions
1097
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1098
# Import the OpenPGP public key into the certificate
1099
(gnutls.library.functions
1100
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1101
gnutls.library.constants
1102
.GNUTLS_OPENPGP_FMT_RAW))
1103
# Verify the self signature in the key
1104
crtverify = ctypes.c_uint()
1105
(gnutls.library.functions
1106
.gnutls_openpgp_crt_verify_self(crt, 0,
1107
ctypes.byref(crtverify)))
1108
if crtverify.value != 0:
1109
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1110
raise (gnutls.errors.CertificateSecurityError
1112
# New buffer for the fingerprint
1113
buf = ctypes.create_string_buffer(20)
1114
buf_len = ctypes.c_size_t()
1115
# Get the fingerprint from the certificate into the buffer
1116
(gnutls.library.functions
1117
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1118
ctypes.byref(buf_len)))
1119
# Deinit the certificate
1120
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1121
# Convert the buffer to a Python bytestring
1122
fpr = ctypes.string_at(buf, buf_len.value)
1123
# Convert the bytestring to hexadecimal notation
1124
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1128
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1129
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1130
def process_request(self, request, client_address):
1131
"""Overrides and wraps the original process_request().
1133
This function creates a new pipe in self.pipe
1135
self.pipe = os.pipe()
1136
super(ForkingMixInWithPipe,
1137
self).process_request(request, client_address)
1138
os.close(self.pipe[1]) # close write end
1139
self.add_pipe(self.pipe[0])
1140
def add_pipe(self, pipe):
1141
"""Dummy function; override as necessary"""
1145
class IPv6_TCPServer(ForkingMixInWithPipe,
1146
socketserver.TCPServer, object):
1147
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
519
settings: Server settings
520
clients: Set() of Client objects
521
1150
enabled: Boolean; whether this server is activated yet
1151
interface: None or a network interface name (string)
1152
use_ipv6: Boolean; to use IPv6 or not
523
address_family = socket.AF_INET6
524
def __init__(self, *args, **kwargs):
525
if "settings" in kwargs:
526
self.settings = kwargs["settings"]
527
del kwargs["settings"]
528
if "clients" in kwargs:
529
self.clients = kwargs["clients"]
530
del kwargs["clients"]
532
return super(type(self), self).__init__(*args, **kwargs)
1154
def __init__(self, server_address, RequestHandlerClass,
1155
interface=None, use_ipv6=True):
1156
self.interface = interface
1158
self.address_family = socket.AF_INET6
1159
socketserver.TCPServer.__init__(self, server_address,
1160
RequestHandlerClass)
533
1161
def server_bind(self):
534
1162
"""This overrides the normal server_bind() function
535
1163
to bind to an interface if one was specified, and also NOT to
536
1164
bind to an address or port if they were not specified."""
537
if self.settings["interface"]:
538
# 25 is from /usr/include/asm-i486/socket.h
539
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
541
self.socket.setsockopt(socket.SOL_SOCKET,
543
self.settings["interface"])
544
except socket.error, error:
545
if error[0] == errno.EPERM:
546
logger.error(u"No permission to"
547
u" bind to interface %s",
548
self.settings["interface"])
1165
if self.interface is not None:
1166
if SO_BINDTODEVICE is None:
1167
logger.error(u"SO_BINDTODEVICE does not exist;"
1168
u" cannot bind to interface %s",
1172
self.socket.setsockopt(socket.SOL_SOCKET,
1176
except socket.error, error:
1177
if error[0] == errno.EPERM:
1178
logger.error(u"No permission to"
1179
u" bind to interface %s",
1181
elif error[0] == errno.ENOPROTOOPT:
1182
logger.error(u"SO_BINDTODEVICE not available;"
1183
u" cannot bind to interface %s",
551
1187
# Only bind(2) the socket if we really need to.
552
1188
if self.server_address[0] or self.server_address[1]:
553
1189
if not self.server_address[0]:
555
self.server_address = (in6addr_any,
1190
if self.address_family == socket.AF_INET6:
1191
any_address = u"::" # in6addr_any
1193
any_address = socket.INADDR_ANY
1194
self.server_address = (any_address,
556
1195
self.server_address[1])
557
1196
elif not self.server_address[1]:
558
1197
self.server_address = (self.server_address[0],
560
# if self.settings["interface"]:
1199
# if self.interface:
561
1200
# self.server_address = (self.server_address[0],
564
1203
# if_nametoindex
567
return super(type(self), self).server_bind()
1205
return socketserver.TCPServer.server_bind(self)
1208
class MandosServer(IPv6_TCPServer):
1212
clients: set of Client objects
1213
gnutls_priority GnuTLS priority string
1214
use_dbus: Boolean; to emit D-Bus signals or not
1216
Assumes a gobject.MainLoop event loop.
1218
def __init__(self, server_address, RequestHandlerClass,
1219
interface=None, use_ipv6=True, clients=None,
1220
gnutls_priority=None, use_dbus=True):
1221
self.enabled = False
1222
self.clients = clients
1223
if self.clients is None:
1224
self.clients = set()
1225
self.use_dbus = use_dbus
1226
self.gnutls_priority = gnutls_priority
1227
IPv6_TCPServer.__init__(self, server_address,
1228
RequestHandlerClass,
1229
interface = interface,
1230
use_ipv6 = use_ipv6)
568
1231
def server_activate(self):
569
1232
if self.enabled:
570
return super(type(self), self).server_activate()
1233
return socketserver.TCPServer.server_activate(self)
571
1234
def enable(self):
572
1235
self.enabled = True
1236
def add_pipe(self, pipe):
1237
# Call "handle_ipc" for both data and EOF events
1238
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1240
def handle_ipc(self, source, condition, file_objects={}):
1242
gobject.IO_IN: u"IN", # There is data to read.
1243
gobject.IO_OUT: u"OUT", # Data can be written (without
1245
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1246
gobject.IO_ERR: u"ERR", # Error condition.
1247
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1248
# broken, usually for pipes and
1251
conditions_string = ' | '.join(name
1253
condition_names.iteritems()
1254
if cond & condition)
1255
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1258
# Turn the pipe file descriptor into a Python file object
1259
if source not in file_objects:
1260
file_objects[source] = os.fdopen(source, u"r", 1)
1262
# Read a line from the file object
1263
cmdline = file_objects[source].readline()
1264
if not cmdline: # Empty line means end of file
1265
# close the IPC pipe
1266
file_objects[source].close()
1267
del file_objects[source]
1269
# Stop calling this function
1272
logger.debug(u"IPC command: %r", cmdline)
1274
# Parse and act on command
1275
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1277
if cmd == u"NOTFOUND":
1278
logger.warning(u"Client not found for fingerprint: %s",
1282
mandos_dbus_service.ClientNotFound(args)
1283
elif cmd == u"INVALID":
1284
for client in self.clients:
1285
if client.name == args:
1286
logger.warning(u"Client %s is invalid", args)
1292
logger.error(u"Unknown client %s is invalid", args)
1293
elif cmd == u"SENDING":
1294
for client in self.clients:
1295
if client.name == args:
1296
logger.info(u"Sending secret to %s", client.name)
1300
client.ReceivedSecret()
1303
logger.error(u"Sending secret to unknown client %s",
1306
logger.error(u"Unknown IPC command: %r", cmdline)
1308
# Keep calling this function
575
1312
def string_to_delta(interval):
576
1313
"""Parse a string and return a datetime.timedelta
578
>>> string_to_delta('7d')
1315
>>> string_to_delta(u'7d')
579
1316
datetime.timedelta(7)
580
>>> string_to_delta('60s')
1317
>>> string_to_delta(u'60s')
581
1318
datetime.timedelta(0, 60)
582
>>> string_to_delta('60m')
1319
>>> string_to_delta(u'60m')
583
1320
datetime.timedelta(0, 3600)
584
>>> string_to_delta('24h')
1321
>>> string_to_delta(u'24h')
585
1322
datetime.timedelta(1)
586
1323
>>> string_to_delta(u'1w')
587
1324
datetime.timedelta(7)
588
>>> string_to_delta('5m 30s')
1325
>>> string_to_delta(u'5m 30s')
589
1326
datetime.timedelta(0, 330)
591
1328
timevalue = datetime.timedelta(0)
592
1329
for s in interval.split():
594
suffix=unicode(s[-1])
1331
suffix = unicode(s[-1])
596
1333
if suffix == u"d":
597
1334
delta = datetime.timedelta(value)
598
1335
elif suffix == u"s":
716
1436
# Default values for config file for server-global settings
717
server_defaults = { "interface": "",
722
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
723
"servicename": "Mandos",
1437
server_defaults = { u"interface": u"",
1442
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1443
u"servicename": u"Mandos",
1444
u"use_dbus": u"True",
1445
u"use_ipv6": u"True",
726
1448
# Parse config file for server-global settings
727
server_config = ConfigParser.SafeConfigParser(server_defaults)
1449
server_config = configparser.SafeConfigParser(server_defaults)
728
1450
del server_defaults
729
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1451
server_config.read(os.path.join(options.configdir,
730
1453
# Convert the SafeConfigParser object to a dict
731
1454
server_settings = server_config.defaults()
732
# Use getboolean on the boolean config option
733
server_settings["debug"] = server_config.getboolean\
1455
# Use the appropriate methods on the non-string config options
1456
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1457
server_settings[option] = server_config.getboolean(u"DEFAULT",
1459
if server_settings["port"]:
1460
server_settings["port"] = server_config.getint(u"DEFAULT",
735
1462
del server_config
737
1464
# Override the settings from the config file with command line
738
1465
# options, if set.
739
for option in ("interface", "address", "port", "debug",
740
"priority", "servicename", "configdir"):
1466
for option in (u"interface", u"address", u"port", u"debug",
1467
u"priority", u"servicename", u"configdir",
1468
u"use_dbus", u"use_ipv6"):
741
1469
value = getattr(options, option)
742
1470
if value is not None:
743
1471
server_settings[option] = value
1473
# Force all strings to be unicode
1474
for option in server_settings.keys():
1475
if type(server_settings[option]) is str:
1476
server_settings[option] = unicode(server_settings[option])
745
1477
# Now we have our good server settings in "server_settings"
747
debug = server_settings["debug"]
1479
##################################################################
1482
debug = server_settings[u"debug"]
1483
use_dbus = server_settings[u"use_dbus"]
1484
use_ipv6 = server_settings[u"use_ipv6"]
750
1487
syslogger.setLevel(logging.WARNING)
751
1488
console.setLevel(logging.WARNING)
753
if server_settings["servicename"] != "Mandos":
754
syslogger.setFormatter(logging.Formatter\
755
('Mandos (%s): %%(levelname)s:'
757
% server_settings["servicename"]))
1490
if server_settings[u"servicename"] != u"Mandos":
1491
syslogger.setFormatter(logging.Formatter
1492
(u'Mandos (%s) [%%(process)d]:'
1493
u' %%(levelname)s: %%(message)s'
1494
% server_settings[u"servicename"]))
759
1496
# Parse config file with clients
760
client_defaults = { "timeout": "1h",
762
"checker": "fping -q -- %(host)s",
1497
client_defaults = { u"timeout": u"1h",
1499
u"checker": u"fping -q -- %%(host)s",
765
client_config = ConfigParser.SafeConfigParser(client_defaults)
766
client_config.read(os.path.join(server_settings["configdir"],
770
tcp_server = IPv6_TCPServer((server_settings["address"],
771
server_settings["port"]),
773
settings=server_settings,
775
pidfilename = "/var/run/mandos.pid"
777
pidfile = open(pidfilename, "w")
778
except IOError, error:
779
logger.error("Could not open file %r", pidfilename)
784
uid = pwd.getpwnam("mandos").pw_uid
787
uid = pwd.getpwnam("nobody").pw_uid
791
gid = pwd.getpwnam("mandos").pw_gid
794
gid = pwd.getpwnam("nogroup").pw_gid
1502
client_config = configparser.SafeConfigParser(client_defaults)
1503
client_config.read(os.path.join(server_settings[u"configdir"],
1506
global mandos_dbus_service
1507
mandos_dbus_service = None
1509
tcp_server = MandosServer((server_settings[u"address"],
1510
server_settings[u"port"]),
1512
interface=server_settings[u"interface"],
1515
server_settings[u"priority"],
1517
pidfilename = u"/var/run/mandos.pid"
1519
pidfile = open(pidfilename, u"w")
1521
logger.error(u"Could not open file %r", pidfilename)
1524
uid = pwd.getpwnam(u"_mandos").pw_uid
1525
gid = pwd.getpwnam(u"_mandos").pw_gid
1528
uid = pwd.getpwnam(u"mandos").pw_uid
1529
gid = pwd.getpwnam(u"mandos").pw_gid
1532
uid = pwd.getpwnam(u"nobody").pw_uid
1533
gid = pwd.getpwnam(u"nobody").pw_gid
800
1540
except OSError, error:
801
1541
if error[0] != errno.EPERM:
805
service = AvahiService(name = server_settings["servicename"],
806
type = "_mandos._tcp", );
807
if server_settings["interface"]:
808
service.interface = if_nametoindex\
809
(server_settings["interface"])
1544
# Enable all possible GnuTLS debugging
1546
# "Use a log level over 10 to enable all debugging options."
1548
gnutls.library.functions.gnutls_global_set_log_level(11)
1550
@gnutls.library.types.gnutls_log_func
1551
def debug_gnutls(level, string):
1552
logger.debug(u"GnuTLS: %s", string[:-1])
1554
(gnutls.library.functions
1555
.gnutls_global_set_log_function(debug_gnutls))
811
1557
global main_loop
814
1558
# From the Avahi example code
815
1559
DBusGMainLoop(set_as_default=True )
816
1560
main_loop = gobject.MainLoop()
817
1561
bus = dbus.SystemBus()
818
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
819
avahi.DBUS_PATH_SERVER),
820
avahi.DBUS_INTERFACE_SERVER)
821
1562
# End of Avahi example code
823
def remove_from_clients(client):
824
clients.remove(client)
826
logger.critical(u"No clients left, exiting")
829
clients.update(Set(Client(name = section,
830
stop_hook = remove_from_clients,
832
= dict(client_config.items(section)))
833
for section in client_config.sections()))
835
logger.critical(u"No clients defined")
1564
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1565
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1566
service = AvahiService(name = server_settings[u"servicename"],
1567
servicetype = u"_mandos._tcp",
1568
protocol = protocol, bus = bus)
1569
if server_settings["interface"]:
1570
service.interface = (if_nametoindex
1571
(str(server_settings[u"interface"])))
1573
client_class = Client
1575
client_class = functools.partial(ClientDBus, bus = bus)
1576
tcp_server.clients.update(set(
1577
client_class(name = section,
1578
config= dict(client_config.items(section)))
1579
for section in client_config.sections()))
1580
if not tcp_server.clients:
1581
logger.warning(u"No clients defined")
839
1584
# Redirect stdin so all checkers get /dev/null