126
106
max_renames: integer; maximum number of renames
127
107
rename_count: integer; counter so we only rename after collisions
128
108
a sensible number of times
129
group: D-Bus Entry Group
131
bus: dbus.SystemBus()
133
110
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
134
servicetype = None, port = None, TXT = None,
135
domain = u"", host = u"", max_renames = 32768,
136
protocol = avahi.PROTO_UNSPEC, bus = None):
111
type = None, port = None, TXT = None, domain = "",
112
host = "", max_renames = 32768):
137
113
self.interface = interface
139
self.type = servicetype
141
self.TXT = TXT if TXT is not None else []
142
121
self.domain = domain
144
123
self.rename_count = 0
145
124
self.max_renames = max_renames
146
self.protocol = protocol
147
self.group = None # our entry group
150
125
def rename(self):
151
126
"""Derived from the Avahi example code"""
152
127
if self.rename_count >= self.max_renames:
153
128
logger.critical(u"No suitable Zeroconf service name found"
154
129
u" after %i retries, exiting.",
156
raise AvahiServiceError(u"Too many renames")
157
self.name = self.server.GetAlternativeServiceName(self.name)
131
raise AvahiServiceError("Too many renames")
132
self.name = server.GetAlternativeServiceName(self.name)
158
133
logger.info(u"Changing Zeroconf service name to %r ...",
160
syslogger.setFormatter(logging.Formatter
161
(u'Mandos (%s) [%%(process)d]:'
162
u' %%(levelname)s: %%(message)s'
135
syslogger.setFormatter(logging.Formatter\
136
('Mandos (%s): %%(levelname)s:'
137
' %%(message)s' % self.name))
166
140
self.rename_count += 1
167
141
def remove(self):
168
142
"""Derived from the Avahi example code"""
169
if self.group is not None:
143
if group is not None:
172
146
"""Derived from the Avahi example code"""
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)
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)
181
155
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
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())
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
230
172
class Client(object):
231
173
"""A representation of a client host served by this server.
234
name: string; from the config file, used in log messages and
175
name: string; from the config file, used in log messages
236
176
fingerprint: string (40 or 32 hexadecimal digits); used to
237
177
uniquely identify the client
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.
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.
251
189
checker_initiator_tag: a gobject event source tag, or None
252
disable_initiator_tag: - '' -
190
stop_initiator_tag: - '' -
253
191
checker_callback_tag: - '' -
254
192
checker_command: string; External command which is run to check if
255
193
client lives. %() expansions are done at
256
194
runtime with vars(self) as dict, so that for
257
195
instance %(name)s can be used in the command.
258
current_checker_command: string; current running checker_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: - '' -
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):
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={}):
277
226
"""Note: the 'checker' key in 'config' sets the
278
227
'checker_command' attribute and *not* the 'checker'
283
230
logger.debug(u"Creating client %r", self.name)
284
231
# Uppercase and remove spaces from fingerprint for later
285
232
# comparison purposes with return value from the fingerprint()
287
self.fingerprint = (config[u"fingerprint"].upper()
234
self.fingerprint = config["fingerprint"].upper()\
289
236
logger.debug(u" Fingerprint: %s", self.fingerprint)
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"])))) as secfile:
296
self.secret = secfile.read()
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()
298
244
raise TypeError(u"No secret or secfile for client %s"
300
self.host = config.get(u"host", u"")
301
self.created = datetime.datetime.utcnow()
303
self.last_enabled = None
246
self.host = config.get("host", "")
247
self.created = datetime.datetime.now()
304
248
self.last_checked_ok = None
305
self.timeout = string_to_delta(config[u"timeout"])
306
self.interval = string_to_delta(config[u"interval"])
307
self.disable_hook = disable_hook
249
self.timeout = string_to_delta(config["timeout"])
250
self.interval = string_to_delta(config["interval"])
251
self.stop_hook = stop_hook
308
252
self.checker = None
309
253
self.checker_initiator_tag = None
310
self.disable_initiator_tag = None
254
self.stop_initiator_tag = None
311
255
self.checker_callback_tag = None
312
self.checker_command = config[u"checker"]
313
self.current_checker_command = None
314
self.last_connect = None
256
self.check_command = config["checker"]
317
258
"""Start this client's checker and timeout hooks"""
318
if getattr(self, u"enabled", False):
321
self.last_enabled = datetime.datetime.utcnow()
322
259
# Schedule a new checker to be started an 'interval' from now,
323
260
# and every interval from then on.
324
self.checker_initiator_tag = (gobject.timeout_add
325
(self.interval_milliseconds(),
261
self.checker_initiator_tag = gobject.timeout_add\
262
(self._interval_milliseconds,
327
264
# Also start a new checker *right now*.
328
265
self.start_checker()
329
# Schedule a disable() when 'timeout' has passed
330
self.disable_initiator_tag = (gobject.timeout_add
331
(self.timeout_milliseconds(),
336
"""Disable this client."""
337
if not getattr(self, "enabled", False):
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)
339
logger.info(u"Disabling client %s", self.name)
340
if getattr(self, u"disable_initiator_tag", False):
341
gobject.source_remove(self.disable_initiator_tag)
342
self.disable_initiator_tag = None
343
if getattr(self, u"checker_initiator_tag", 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):
344
284
gobject.source_remove(self.checker_initiator_tag)
345
285
self.checker_initiator_tag = None
346
286
self.stop_checker()
347
if self.disable_hook:
348
self.disable_hook(self)
350
289
# Do not run this again if called by a gobject.timeout_add
353
291
def __del__(self):
354
self.disable_hook = None
357
def checker_callback(self, pid, condition, command):
292
self.stop_hook = None
294
def checker_callback(self, pid, condition):
358
295
"""The checker has completed, so take appropriate actions."""
296
now = datetime.datetime.now()
359
297
self.checker_callback_tag = None
360
298
self.checker = None
361
if os.WIFEXITED(condition):
362
exitstatus = os.WEXITSTATUS(condition)
364
logger.info(u"Checker for %(name)s succeeded",
368
logger.info(u"Checker for %(name)s failed",
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):
371
309
logger.warning(u"Checker for %(name)s crashed?",
374
def checked_ok(self):
375
"""Bump up the timeout for this client.
377
This should only be called when the client has been seen,
380
self.last_checked_ok = datetime.datetime.utcnow()
381
gobject.source_remove(self.disable_initiator_tag)
382
self.disable_initiator_tag = (gobject.timeout_add
383
(self.timeout_milliseconds(),
312
logger.info(u"Checker for %(name)s failed",
386
314
def start_checker(self):
387
315
"""Start a new checker subprocess if one is not running.
389
316
If a checker already exists, leave it running and do
391
318
# The reason for not killing a running checker is that if we
468
373
if error.errno != errno.ESRCH: # No such process
470
375
self.checker = None
472
376
def still_valid(self):
473
377
"""Has the timeout not yet passed for this client?"""
474
if not getattr(self, u"enabled", False):
476
now = datetime.datetime.utcnow()
378
now = datetime.datetime.now()
477
379
if self.last_checked_ok is None:
478
380
return now < (self.created + self.timeout)
480
382
return now < (self.last_checked_ok + self.timeout)
483
def dbus_service_property(dbus_interface, signature=u"v",
484
access=u"readwrite", byte_arrays=False):
485
"""Decorators for marking methods of a DBusObjectWithProperties to
486
become properties on the D-Bus.
488
The decorated method will be called with no arguments by "Get"
489
and with one argument by "Set".
491
The parameters, where they are supported, are the same as
492
dbus.service.method, except there is only "signature", since the
493
type from Get() and the type sent to Set() is the same.
496
func._dbus_is_property = True
497
func._dbus_interface = dbus_interface
498
func._dbus_signature = signature
499
func._dbus_access = access
500
func._dbus_name = func.__name__
501
if func._dbus_name.endswith(u"_dbus_property"):
502
func._dbus_name = func._dbus_name[:-14]
503
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
508
class DBusPropertyException(dbus.exceptions.DBusException):
509
"""A base class for D-Bus property-related exceptions
511
def __unicode__(self):
512
return unicode(str(self))
515
class DBusPropertyAccessException(DBusPropertyException):
516
"""A property's access permissions disallows an operation.
521
class DBusPropertyNotFound(DBusPropertyException):
522
"""An attempt was made to access a non-existing property.
527
class DBusObjectWithProperties(dbus.service.Object):
528
"""A D-Bus object with properties.
530
Classes inheriting from this can use the dbus_service_property
531
decorator to expose methods as D-Bus properties. It exposes the
532
standard Get(), Set(), and GetAll() methods on the D-Bus.
536
def _is_dbus_property(obj):
537
return getattr(obj, u"_dbus_is_property", False)
539
def _get_all_dbus_properties(self):
540
"""Returns a generator of (name, attribute) pairs
542
return ((prop._dbus_name, prop)
544
inspect.getmembers(self, self._is_dbus_property))
546
def _get_dbus_property(self, interface_name, property_name):
547
"""Returns a bound method if one exists which is a D-Bus
548
property with the specified name and interface.
550
for name in (property_name,
551
property_name + u"_dbus_property"):
552
prop = getattr(self, name, None)
554
or not self._is_dbus_property(prop)
555
or prop._dbus_name != property_name
556
or (interface_name and prop._dbus_interface
557
and interface_name != prop._dbus_interface)):
561
raise DBusPropertyNotFound(self.dbus_object_path + u":"
562
+ interface_name + u"."
565
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
567
def Get(self, interface_name, property_name):
568
"""Standard D-Bus property Get() method, see D-Bus standard.
570
prop = self._get_dbus_property(interface_name, property_name)
571
if prop._dbus_access == u"write":
572
raise DBusPropertyAccessException(property_name)
574
if not hasattr(value, u"variant_level"):
576
return type(value)(value, variant_level=value.variant_level+1)
578
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
579
def Set(self, interface_name, property_name, value):
580
"""Standard D-Bus property Set() method, see D-Bus standard.
582
prop = self._get_dbus_property(interface_name, property_name)
583
if prop._dbus_access == u"read":
584
raise DBusPropertyAccessException(property_name)
585
if prop._dbus_get_args_options[u"byte_arrays"]:
586
value = dbus.ByteArray(''.join(unichr(byte)
590
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
591
out_signature=u"a{sv}")
592
def GetAll(self, interface_name):
593
"""Standard D-Bus property GetAll() method, see D-Bus
596
Note: Will not include properties with access="write".
599
for name, prop in self._get_all_dbus_properties():
601
and interface_name != prop._dbus_interface):
602
# Interface non-empty but did not match
604
# Ignore write-only properties
605
if prop._dbus_access == u"write":
608
if not hasattr(value, u"variant_level"):
611
all[name] = type(value)(value, variant_level=
612
value.variant_level+1)
613
return dbus.Dictionary(all, signature=u"sv")
615
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
617
path_keyword='object_path',
618
connection_keyword='connection')
619
def Introspect(self, object_path, connection):
620
"""Standard D-Bus method, overloaded to insert property tags.
622
xmlstring = dbus.service.Object.Introspect(self, object_path,
624
document = xml.dom.minidom.parseString(xmlstring)
626
def make_tag(document, name, prop):
627
e = document.createElement(u"property")
628
e.setAttribute(u"name", name)
629
e.setAttribute(u"type", prop._dbus_signature)
630
e.setAttribute(u"access", prop._dbus_access)
632
for if_tag in document.getElementsByTagName(u"interface"):
633
for tag in (make_tag(document, name, prop)
635
in self._get_all_dbus_properties()
636
if prop._dbus_interface
637
== if_tag.getAttribute(u"name")):
638
if_tag.appendChild(tag)
639
xmlstring = document.toxml(u"utf-8")
644
class ClientDBus(Client, DBusObjectWithProperties):
645
"""A Client class using D-Bus
648
dbus_object_path: dbus.ObjectPath
649
bus: dbus.SystemBus()
651
# dbus.service.Object doesn't use super(), so we can't either.
653
def __init__(self, bus = None, *args, **kwargs):
655
Client.__init__(self, *args, **kwargs)
656
# Only now, when this client is initialized, can it show up on
658
self.dbus_object_path = (dbus.ObjectPath
660
+ self.name.replace(u".", u"_")))
661
DBusObjectWithProperties.__init__(self, self.bus,
662
self.dbus_object_path)
665
def _datetime_to_dbus(dt, variant_level=0):
666
"""Convert a UTC datetime.datetime() to a D-Bus type."""
667
return dbus.String(dt.isoformat(),
668
variant_level=variant_level)
671
oldstate = getattr(self, u"enabled", False)
672
r = Client.enable(self)
673
if oldstate != self.enabled:
675
self.PropertyChanged(dbus.String(u"enabled"),
676
dbus.Boolean(True, variant_level=1))
677
self.PropertyChanged(
678
dbus.String(u"last_enabled"),
679
self._datetime_to_dbus(self.last_enabled,
683
def disable(self, signal = True):
684
oldstate = getattr(self, u"enabled", False)
685
r = Client.disable(self)
686
if signal and oldstate != self.enabled:
688
self.PropertyChanged(dbus.String(u"enabled"),
689
dbus.Boolean(False, variant_level=1))
692
def __del__(self, *args, **kwargs):
694
self.remove_from_connection()
697
if hasattr(DBusObjectWithProperties, u"__del__"):
698
DBusObjectWithProperties.__del__(self, *args, **kwargs)
699
Client.__del__(self, *args, **kwargs)
701
def checker_callback(self, pid, condition, command,
703
self.checker_callback_tag = None
706
self.PropertyChanged(dbus.String(u"checker_running"),
707
dbus.Boolean(False, variant_level=1))
708
if os.WIFEXITED(condition):
709
exitstatus = os.WEXITSTATUS(condition)
711
self.CheckerCompleted(dbus.Int16(exitstatus),
712
dbus.Int64(condition),
713
dbus.String(command))
716
self.CheckerCompleted(dbus.Int16(-1),
717
dbus.Int64(condition),
718
dbus.String(command))
720
return Client.checker_callback(self, pid, condition, command,
723
def checked_ok(self, *args, **kwargs):
724
r = Client.checked_ok(self, *args, **kwargs)
726
self.PropertyChanged(
727
dbus.String(u"last_checked_ok"),
728
(self._datetime_to_dbus(self.last_checked_ok,
732
def start_checker(self, *args, **kwargs):
733
old_checker = self.checker
734
if self.checker is not None:
735
old_checker_pid = self.checker.pid
737
old_checker_pid = None
738
r = Client.start_checker(self, *args, **kwargs)
739
# Only if new checker process was started
740
if (self.checker is not None
741
and old_checker_pid != self.checker.pid):
743
self.CheckerStarted(self.current_checker_command)
744
self.PropertyChanged(
745
dbus.String(u"checker_running"),
746
dbus.Boolean(True, variant_level=1))
749
def stop_checker(self, *args, **kwargs):
750
old_checker = getattr(self, u"checker", None)
751
r = Client.stop_checker(self, *args, **kwargs)
752
if (old_checker is not None
753
and getattr(self, u"checker", None) is None):
754
self.PropertyChanged(dbus.String(u"checker_running"),
755
dbus.Boolean(False, variant_level=1))
758
## D-Bus methods & signals
759
_interface = u"se.bsnet.fukt.Mandos.Client"
762
@dbus.service.method(_interface)
764
return self.checked_ok()
766
# CheckerCompleted - signal
767
@dbus.service.signal(_interface, signature=u"nxs")
768
def CheckerCompleted(self, exitcode, waitstatus, command):
772
# CheckerStarted - signal
773
@dbus.service.signal(_interface, signature=u"s")
774
def CheckerStarted(self, command):
778
# PropertyChanged - signal
779
@dbus.service.signal(_interface, signature=u"sv")
780
def PropertyChanged(self, property, value):
784
# ReceivedSecret - signal
785
@dbus.service.signal(_interface)
786
def ReceivedSecret(self):
791
@dbus.service.signal(_interface)
797
@dbus.service.method(_interface)
802
# StartChecker - method
803
@dbus.service.method(_interface)
804
def StartChecker(self):
809
@dbus.service.method(_interface)
814
# StopChecker - method
815
@dbus.service.method(_interface)
816
def StopChecker(self):
820
@dbus_service_property(_interface, signature=u"s", access=u"read")
821
def name_dbus_property(self):
822
return dbus.String(self.name)
824
# fingerprint - property
825
@dbus_service_property(_interface, signature=u"s", access=u"read")
826
def fingerprint_dbus_property(self):
827
return dbus.String(self.fingerprint)
830
@dbus_service_property(_interface, signature=u"s",
832
def host_dbus_property(self, value=None):
833
if value is None: # get
834
return dbus.String(self.host)
837
self.PropertyChanged(dbus.String(u"host"),
838
dbus.String(value, variant_level=1))
841
@dbus_service_property(_interface, signature=u"s", access=u"read")
842
def created_dbus_property(self):
843
return dbus.String(self._datetime_to_dbus(self.created))
845
# last_enabled - property
846
@dbus_service_property(_interface, signature=u"s", access=u"read")
847
def last_enabled_dbus_property(self):
848
if self.last_enabled is None:
849
return dbus.String(u"")
850
return dbus.String(self._datetime_to_dbus(self.last_enabled))
853
@dbus_service_property(_interface, signature=u"b",
855
def enabled_dbus_property(self, value=None):
856
if value is None: # get
857
return dbus.Boolean(self.enabled)
863
# last_checked_ok - property
864
@dbus_service_property(_interface, signature=u"s",
866
def last_checked_ok_dbus_property(self, value=None):
867
if value is not None:
870
if self.last_checked_ok is None:
871
return dbus.String(u"")
872
return dbus.String(self._datetime_to_dbus(self
876
@dbus_service_property(_interface, signature=u"t",
878
def timeout_dbus_property(self, value=None):
879
if value is None: # get
880
return dbus.UInt64(self.timeout_milliseconds())
881
self.timeout = datetime.timedelta(0, 0, 0, value)
883
self.PropertyChanged(dbus.String(u"timeout"),
884
dbus.UInt64(value, variant_level=1))
885
if getattr(self, u"disable_initiator_tag", None) is None:
888
gobject.source_remove(self.disable_initiator_tag)
889
self.disable_initiator_tag = None
891
_timedelta_to_milliseconds((self
897
# The timeout has passed
900
self.disable_initiator_tag = (gobject.timeout_add
901
(time_to_die, self.disable))
903
# interval - property
904
@dbus_service_property(_interface, signature=u"t",
906
def interval_dbus_property(self, value=None):
907
if value is None: # get
908
return dbus.UInt64(self.interval_milliseconds())
909
self.interval = datetime.timedelta(0, 0, 0, value)
911
self.PropertyChanged(dbus.String(u"interval"),
912
dbus.UInt64(value, variant_level=1))
913
if getattr(self, u"checker_initiator_tag", None) is None:
915
# Reschedule checker run
916
gobject.source_remove(self.checker_initiator_tag)
917
self.checker_initiator_tag = (gobject.timeout_add
918
(value, self.start_checker))
919
self.start_checker() # Start one now, too
922
@dbus_service_property(_interface, signature=u"s",
924
def checker_dbus_property(self, value=None):
925
if value is None: # get
926
return dbus.String(self.checker_command)
927
self.checker_command = value
929
self.PropertyChanged(dbus.String(u"checker"),
930
dbus.String(self.checker_command,
933
# checker_running - property
934
@dbus_service_property(_interface, signature=u"b",
936
def checker_running_dbus_property(self, value=None):
937
if value is None: # get
938
return dbus.Boolean(self.checker is not None)
944
# object_path - property
945
@dbus_service_property(_interface, signature=u"o", access=u"read")
946
def object_path_dbus_property(self):
947
return self.dbus_object_path # is already a dbus.ObjectPath
950
@dbus_service_property(_interface, signature=u"ay",
951
access=u"write", byte_arrays=True)
952
def secret_dbus_property(self, value):
953
self.secret = str(value)
958
class ClientHandler(socketserver.BaseRequestHandler, object):
959
"""A class to handle client connections.
961
Instantiated once for each connection to handle it.
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.
962
442
Note: This will run in its own forked process."""
964
444
def handle(self):
965
445
logger.info(u"TCP connection from: %s",
966
unicode(self.client_address))
967
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
968
# Open IPC pipe to parent process
969
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
970
session = (gnutls.connection
971
.ClientSession(self.request,
975
line = self.request.makefile().readline()
976
logger.debug(u"Protocol version: %r", line)
978
if int(line.strip().split()[0]) > 1:
980
except (ValueError, IndexError, RuntimeError), error:
981
logger.error(u"Unknown protocol version: %s", error)
984
# Note: gnutls.connection.X509Credentials is really a
985
# generic GnuTLS certificate credentials object so long as
986
# no X.509 keys are added to it. Therefore, we can use it
987
# here despite using OpenPGP certificates.
989
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
990
# u"+AES-256-CBC", u"+SHA1",
991
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
993
# Use a fallback default, since this MUST be set.
994
priority = self.server.gnutls_priority
997
(gnutls.library.functions
998
.gnutls_priority_set_direct(session._c_object,
1003
except gnutls.errors.GNUTLSError, error:
1004
logger.warning(u"Handshake failed: %s", error)
1005
# Do not run session.bye() here: the session is not
1006
# established. Just abandon the request.
1008
logger.debug(u"Handshake succeeded")
1010
fpr = self.fingerprint(self.peer_certificate(session))
1011
except (TypeError, gnutls.errors.GNUTLSError), error:
1012
logger.warning(u"Bad certificate: %s", error)
1015
logger.debug(u"Fingerprint: %s", fpr)
1017
for c in self.server.clients:
1018
if c.fingerprint == fpr:
1022
ipc.write(u"NOTFOUND %s %s\n"
1023
% (fpr, unicode(self.client_address)))
1026
# Have to check if client.still_valid(), since it is
1027
# possible that the client timed out while establishing
1028
# the GnuTLS session.
1029
if not client.still_valid():
1030
ipc.write(u"INVALID %s\n" % client.name)
1033
ipc.write(u"SENDING %s\n" % client.name)
1035
while sent_size < len(client.secret):
1036
sent = session.send(client.secret[sent_size:])
1037
logger.debug(u"Sent: %d, remaining: %d",
1038
sent, len(client.secret)
1039
- (sent_size + sent))
1044
def peer_certificate(session):
1045
"Return the peer's OpenPGP certificate as a bytestring"
1046
# If not an OpenPGP certificate...
1047
if (gnutls.library.functions
1048
.gnutls_certificate_type_get(session._c_object)
1049
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1050
# ...do the normal thing
1051
return session.peer_certificate
1052
list_size = ctypes.c_uint(1)
1053
cert_list = (gnutls.library.functions
1054
.gnutls_certificate_get_peers
1055
(session._c_object, ctypes.byref(list_size)))
1056
if not bool(cert_list) and list_size.value != 0:
1057
raise gnutls.errors.GNUTLSError(u"error getting peer"
1059
if list_size.value == 0:
1062
return ctypes.string_at(cert.data, cert.size)
1065
def fingerprint(openpgp):
1066
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1067
# New GnuTLS "datum" with the OpenPGP public key
1068
datum = (gnutls.library.types
1069
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1072
ctypes.c_uint(len(openpgp))))
1073
# New empty GnuTLS certificate
1074
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1075
(gnutls.library.functions
1076
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1077
# Import the OpenPGP public key into the certificate
1078
(gnutls.library.functions
1079
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1080
gnutls.library.constants
1081
.GNUTLS_OPENPGP_FMT_RAW))
1082
# Verify the self signature in the key
1083
crtverify = ctypes.c_uint()
1084
(gnutls.library.functions
1085
.gnutls_openpgp_crt_verify_self(crt, 0,
1086
ctypes.byref(crtverify)))
1087
if crtverify.value != 0:
1088
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1089
raise (gnutls.errors.CertificateSecurityError
1091
# New buffer for the fingerprint
1092
buf = ctypes.create_string_buffer(20)
1093
buf_len = ctypes.c_size_t()
1094
# Get the fingerprint from the certificate into the buffer
1095
(gnutls.library.functions
1096
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1097
ctypes.byref(buf_len)))
1098
# Deinit the certificate
1099
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1100
# Convert the buffer to a Python bytestring
1101
fpr = ctypes.string_at(buf, buf_len.value)
1102
# Convert the bytestring to hexadecimal notation
1103
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1107
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1108
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1109
def process_request(self, request, client_address):
1110
"""Overrides and wraps the original process_request().
1112
This function creates a new pipe in self.pipe
1114
self.pipe = os.pipe()
1115
super(ForkingMixInWithPipe,
1116
self).process_request(request, client_address)
1117
os.close(self.pipe[1]) # close write end
1118
self.add_pipe(self.pipe[0])
1119
def add_pipe(self, pipe):
1120
"""Dummy function; override as necessary"""
1124
class IPv6_TCPServer(ForkingMixInWithPipe,
1125
socketserver.TCPServer, object):
1126
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
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.
519
settings: Server settings
520
clients: Set() of Client objects
1129
521
enabled: Boolean; whether this server is activated yet
1130
interface: None or a network interface name (string)
1131
use_ipv6: Boolean; to use IPv6 or not
1133
def __init__(self, server_address, RequestHandlerClass,
1134
interface=None, use_ipv6=True):
1135
self.interface = interface
1137
self.address_family = socket.AF_INET6
1138
socketserver.TCPServer.__init__(self, server_address,
1139
RequestHandlerClass)
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)
1140
533
def server_bind(self):
1141
534
"""This overrides the normal server_bind() function
1142
535
to bind to an interface if one was specified, and also NOT to
1143
536
bind to an address or port if they were not specified."""
1144
if self.interface is not None:
1145
if SO_BINDTODEVICE is None:
1146
logger.error(u"SO_BINDTODEVICE does not exist;"
1147
u" cannot bind to interface %s",
1151
self.socket.setsockopt(socket.SOL_SOCKET,
1155
except socket.error, error:
1156
if error[0] == errno.EPERM:
1157
logger.error(u"No permission to"
1158
u" bind to interface %s",
1160
elif error[0] == errno.ENOPROTOOPT:
1161
logger.error(u"SO_BINDTODEVICE not available;"
1162
u" cannot bind to interface %s",
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"])
1166
551
# Only bind(2) the socket if we really need to.
1167
552
if self.server_address[0] or self.server_address[1]:
1168
553
if not self.server_address[0]:
1169
if self.address_family == socket.AF_INET6:
1170
any_address = u"::" # in6addr_any
1172
any_address = socket.INADDR_ANY
1173
self.server_address = (any_address,
555
self.server_address = (in6addr_any,
1174
556
self.server_address[1])
1175
557
elif not self.server_address[1]:
1176
558
self.server_address = (self.server_address[0],
1178
# if self.interface:
560
# if self.settings["interface"]:
1179
561
# self.server_address = (self.server_address[0],
1182
564
# if_nametoindex
1184
return socketserver.TCPServer.server_bind(self)
1187
class MandosServer(IPv6_TCPServer):
1191
clients: set of Client objects
1192
gnutls_priority GnuTLS priority string
1193
use_dbus: Boolean; to emit D-Bus signals or not
1195
Assumes a gobject.MainLoop event loop.
1197
def __init__(self, server_address, RequestHandlerClass,
1198
interface=None, use_ipv6=True, clients=None,
1199
gnutls_priority=None, use_dbus=True):
1200
self.enabled = False
1201
self.clients = clients
1202
if self.clients is None:
1203
self.clients = set()
1204
self.use_dbus = use_dbus
1205
self.gnutls_priority = gnutls_priority
1206
IPv6_TCPServer.__init__(self, server_address,
1207
RequestHandlerClass,
1208
interface = interface,
1209
use_ipv6 = use_ipv6)
567
return super(type(self), self).server_bind()
1210
568
def server_activate(self):
1211
569
if self.enabled:
1212
return socketserver.TCPServer.server_activate(self)
570
return super(type(self), self).server_activate()
1213
571
def enable(self):
1214
572
self.enabled = True
1215
def add_pipe(self, pipe):
1216
# Call "handle_ipc" for both data and EOF events
1217
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1219
def handle_ipc(self, source, condition, file_objects={}):
1221
gobject.IO_IN: u"IN", # There is data to read.
1222
gobject.IO_OUT: u"OUT", # Data can be written (without
1224
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1225
gobject.IO_ERR: u"ERR", # Error condition.
1226
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1227
# broken, usually for pipes and
1230
conditions_string = ' | '.join(name
1232
condition_names.iteritems()
1233
if cond & condition)
1234
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1237
# Turn the pipe file descriptor into a Python file object
1238
if source not in file_objects:
1239
file_objects[source] = os.fdopen(source, u"r", 1)
1241
# Read a line from the file object
1242
cmdline = file_objects[source].readline()
1243
if not cmdline: # Empty line means end of file
1244
# close the IPC pipe
1245
file_objects[source].close()
1246
del file_objects[source]
1248
# Stop calling this function
1251
logger.debug(u"IPC command: %r", cmdline)
1253
# Parse and act on command
1254
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1256
if cmd == u"NOTFOUND":
1257
logger.warning(u"Client not found for fingerprint: %s",
1261
mandos_dbus_service.ClientNotFound(args)
1262
elif cmd == u"INVALID":
1263
for client in self.clients:
1264
if client.name == args:
1265
logger.warning(u"Client %s is invalid", args)
1271
logger.error(u"Unknown client %s is invalid", args)
1272
elif cmd == u"SENDING":
1273
for client in self.clients:
1274
if client.name == args:
1275
logger.info(u"Sending secret to %s", client.name)
1279
client.ReceivedSecret()
1282
logger.error(u"Sending secret to unknown client %s",
1285
logger.error(u"Unknown IPC command: %r", cmdline)
1287
# Keep calling this function
1291
575
def string_to_delta(interval):
1292
576
"""Parse a string and return a datetime.timedelta
1294
>>> string_to_delta(u'7d')
578
>>> string_to_delta('7d')
1295
579
datetime.timedelta(7)
1296
>>> string_to_delta(u'60s')
580
>>> string_to_delta('60s')
1297
581
datetime.timedelta(0, 60)
1298
>>> string_to_delta(u'60m')
582
>>> string_to_delta('60m')
1299
583
datetime.timedelta(0, 3600)
1300
>>> string_to_delta(u'24h')
584
>>> string_to_delta('24h')
1301
585
datetime.timedelta(1)
1302
586
>>> string_to_delta(u'1w')
1303
587
datetime.timedelta(7)
1304
>>> string_to_delta(u'5m 30s')
588
>>> string_to_delta('5m 30s')
1305
589
datetime.timedelta(0, 330)
1307
591
timevalue = datetime.timedelta(0)
1308
592
for s in interval.split():
1310
suffix = unicode(s[-1])
594
suffix=unicode(s[-1])
1312
596
if suffix == u"d":
1313
597
delta = datetime.timedelta(value)
1314
598
elif suffix == u"s":
1415
716
# Default values for config file for server-global settings
1416
server_defaults = { u"interface": u"",
1421
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1422
u"servicename": u"Mandos",
1423
u"use_dbus": u"True",
1424
u"use_ipv6": u"True",
717
server_defaults = { "interface": "",
722
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
723
"servicename": "Mandos",
1427
726
# Parse config file for server-global settings
1428
server_config = configparser.SafeConfigParser(server_defaults)
727
server_config = ConfigParser.SafeConfigParser(server_defaults)
1429
728
del server_defaults
1430
server_config.read(os.path.join(options.configdir,
729
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1432
730
# Convert the SafeConfigParser object to a dict
1433
731
server_settings = server_config.defaults()
1434
# Use the appropriate methods on the non-string config options
1435
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1436
server_settings[option] = server_config.getboolean(u"DEFAULT",
1438
if server_settings["port"]:
1439
server_settings["port"] = server_config.getint(u"DEFAULT",
732
# Use getboolean on the boolean config option
733
server_settings["debug"] = server_config.getboolean\
1441
735
del server_config
1443
737
# Override the settings from the config file with command line
1444
738
# options, if set.
1445
for option in (u"interface", u"address", u"port", u"debug",
1446
u"priority", u"servicename", u"configdir",
1447
u"use_dbus", u"use_ipv6"):
739
for option in ("interface", "address", "port", "debug",
740
"priority", "servicename", "configdir"):
1448
741
value = getattr(options, option)
1449
742
if value is not None:
1450
743
server_settings[option] = value
1452
# Force all strings to be unicode
1453
for option in server_settings.keys():
1454
if type(server_settings[option]) is str:
1455
server_settings[option] = unicode(server_settings[option])
1456
745
# Now we have our good server settings in "server_settings"
1458
##################################################################
1461
debug = server_settings[u"debug"]
1462
use_dbus = server_settings[u"use_dbus"]
1463
use_ipv6 = server_settings[u"use_ipv6"]
747
debug = server_settings["debug"]
1466
750
syslogger.setLevel(logging.WARNING)
1467
751
console.setLevel(logging.WARNING)
1469
if server_settings[u"servicename"] != u"Mandos":
1470
syslogger.setFormatter(logging.Formatter
1471
(u'Mandos (%s) [%%(process)d]:'
1472
u' %%(levelname)s: %%(message)s'
1473
% server_settings[u"servicename"]))
753
if server_settings["servicename"] != "Mandos":
754
syslogger.setFormatter(logging.Formatter\
755
('Mandos (%s): %%(levelname)s:'
757
% server_settings["servicename"]))
1475
759
# Parse config file with clients
1476
client_defaults = { u"timeout": u"1h",
1478
u"checker": u"fping -q -- %%(host)s",
760
client_defaults = { "timeout": "1h",
762
"checker": "fping -q -- %(host)s",
1481
client_config = configparser.SafeConfigParser(client_defaults)
1482
client_config.read(os.path.join(server_settings[u"configdir"],
1485
global mandos_dbus_service
1486
mandos_dbus_service = None
1488
tcp_server = MandosServer((server_settings[u"address"],
1489
server_settings[u"port"]),
1491
interface=server_settings[u"interface"],
1494
server_settings[u"priority"],
1496
pidfilename = u"/var/run/mandos.pid"
1498
pidfile = open(pidfilename, u"w")
1500
logger.error(u"Could not open file %r", pidfilename)
1503
uid = pwd.getpwnam(u"_mandos").pw_uid
1504
gid = pwd.getpwnam(u"_mandos").pw_gid
1507
uid = pwd.getpwnam(u"mandos").pw_uid
1508
gid = pwd.getpwnam(u"mandos").pw_gid
1511
uid = pwd.getpwnam(u"nobody").pw_uid
1512
gid = pwd.getpwnam(u"nobody").pw_gid
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
1519
800
except OSError, error:
1520
801
if error[0] != errno.EPERM:
1523
# Enable all possible GnuTLS debugging
1525
# "Use a log level over 10 to enable all debugging options."
1527
gnutls.library.functions.gnutls_global_set_log_level(11)
1529
@gnutls.library.types.gnutls_log_func
1530
def debug_gnutls(level, string):
1531
logger.debug(u"GnuTLS: %s", string[:-1])
1533
(gnutls.library.functions
1534
.gnutls_global_set_log_function(debug_gnutls))
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"])
1536
811
global main_loop
1537
814
# From the Avahi example code
1538
815
DBusGMainLoop(set_as_default=True )
1539
816
main_loop = gobject.MainLoop()
1540
817
bus = dbus.SystemBus()
818
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
819
avahi.DBUS_PATH_SERVER),
820
avahi.DBUS_INTERFACE_SERVER)
1541
821
# End of Avahi example code
1543
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1544
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1545
service = AvahiService(name = server_settings[u"servicename"],
1546
servicetype = u"_mandos._tcp",
1547
protocol = protocol, bus = bus)
1548
if server_settings["interface"]:
1549
service.interface = (if_nametoindex
1550
(str(server_settings[u"interface"])))
1552
client_class = Client
1554
client_class = functools.partial(ClientDBus, bus = bus)
1555
tcp_server.clients.update(set(
1556
client_class(name = section,
1557
config= dict(client_config.items(section)))
1558
for section in client_config.sections()))
1559
if not tcp_server.clients:
1560
logger.warning(u"No clients defined")
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")
1563
839
# Redirect stdin so all checkers get /dev/null