127
106
max_renames: integer; maximum number of renames
128
107
rename_count: integer; counter so we only rename after collisions
129
108
a sensible number of times
130
group: D-Bus Entry Group
132
bus: dbus.SystemBus()
134
110
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
135
servicetype = None, port = None, TXT = None,
136
domain = u"", host = u"", max_renames = 32768,
137
protocol = avahi.PROTO_UNSPEC, bus = None):
111
type = None, port = None, TXT = None, domain = "",
112
host = "", max_renames = 32768):
138
113
self.interface = interface
140
self.type = servicetype
142
self.TXT = TXT if TXT is not None else []
143
121
self.domain = domain
145
123
self.rename_count = 0
146
124
self.max_renames = max_renames
147
self.protocol = protocol
148
self.group = None # our entry group
151
125
def rename(self):
152
126
"""Derived from the Avahi example code"""
153
127
if self.rename_count >= self.max_renames:
154
128
logger.critical(u"No suitable Zeroconf service name found"
155
129
u" after %i retries, exiting.",
157
raise AvahiServiceError(u"Too many renames")
158
self.name = self.server.GetAlternativeServiceName(self.name)
131
raise AvahiServiceError("Too many renames")
132
self.name = server.GetAlternativeServiceName(self.name)
159
133
logger.info(u"Changing Zeroconf service name to %r ...",
161
syslogger.setFormatter(logging.Formatter
162
(u'Mandos (%s) [%%(process)d]:'
163
u' %%(levelname)s: %%(message)s'
135
syslogger.setFormatter(logging.Formatter\
136
('Mandos (%s): %%(levelname)s:'
137
' %%(message)s' % self.name))
167
140
self.rename_count += 1
168
141
def remove(self):
169
142
"""Derived from the Avahi example code"""
170
if self.group is not None:
143
if group is not None:
173
146
"""Derived from the Avahi example code"""
174
if self.group is None:
175
self.group = dbus.Interface(
176
self.bus.get_object(avahi.DBUS_NAME,
177
self.server.EntryGroupNew()),
178
avahi.DBUS_INTERFACE_ENTRY_GROUP)
179
self.group.connect_to_signal('StateChanged',
181
.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)
182
155
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
183
self.name, self.type)
184
self.group.AddService(
187
dbus.UInt32(0), # flags
188
self.name, self.type,
189
self.domain, self.host,
190
dbus.UInt16(self.port),
191
avahi.string_array_to_txt_array(self.TXT))
193
def entry_group_state_changed(self, state, error):
194
"""Derived from the Avahi example code"""
195
logger.debug(u"Avahi state change: %i", state)
197
if state == avahi.ENTRY_GROUP_ESTABLISHED:
198
logger.debug(u"Zeroconf service established.")
199
elif state == avahi.ENTRY_GROUP_COLLISION:
200
logger.warning(u"Zeroconf service name collision.")
202
elif state == avahi.ENTRY_GROUP_FAILURE:
203
logger.critical(u"Avahi: Error in group state changed %s",
205
raise AvahiGroupError(u"State changed: %s"
208
"""Derived from the Avahi example code"""
209
if self.group is not None:
212
def server_state_changed(self, state):
213
"""Derived from the Avahi example code"""
214
if state == avahi.SERVER_COLLISION:
215
logger.error(u"Zeroconf server name collision")
217
elif state == avahi.SERVER_RUNNING:
220
"""Derived from the Avahi example code"""
221
if self.server is None:
222
self.server = dbus.Interface(
223
self.bus.get_object(avahi.DBUS_NAME,
224
avahi.DBUS_PATH_SERVER),
225
avahi.DBUS_INTERFACE_SERVER)
226
self.server.connect_to_signal(u"StateChanged",
227
self.server_state_changed)
228
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
231
172
class Client(object):
232
173
"""A representation of a client host served by this server.
235
name: string; from the config file, used in log messages and
175
name: string; from the config file, used in log messages
237
176
fingerprint: string (40 or 32 hexadecimal digits); used to
238
177
uniquely identify the client
239
secret: bytestring; sent verbatim (over TLS) to client
240
host: string; available for use by the checker command
241
created: datetime.datetime(); (UTC) object creation
242
last_enabled: datetime.datetime(); (UTC)
244
last_checked_ok: datetime.datetime(); (UTC) or None
245
timeout: datetime.timedelta(); How long from last_checked_ok
246
until this client is disabled
247
interval: datetime.timedelta(); How often to start a new checker
248
disable_hook: If set, called by disable() as disable_hook(self)
249
checker: subprocess.Popen(); a running checker process used
250
to see if the client lives.
251
'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.
252
189
checker_initiator_tag: a gobject event source tag, or None
253
disable_initiator_tag: - '' -
190
stop_initiator_tag: - '' -
254
191
checker_callback_tag: - '' -
255
192
checker_command: string; External command which is run to check if
256
193
client lives. %() expansions are done at
257
194
runtime with vars(self) as dict, so that for
258
195
instance %(name)s can be used in the command.
259
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: - '' -
263
def _timedelta_to_milliseconds(td):
264
"Convert a datetime.timedelta() to milliseconds"
265
return ((td.days * 24 * 60 * 60 * 1000)
266
+ (td.seconds * 1000)
267
+ (td.microseconds // 1000))
269
def timeout_milliseconds(self):
270
"Return the 'timeout' attribute in milliseconds"
271
return self._timedelta_to_milliseconds(self.timeout)
273
def interval_milliseconds(self):
274
"Return the 'interval' attribute in milliseconds"
275
return self._timedelta_to_milliseconds(self.interval)
277
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={}):
278
226
"""Note: the 'checker' key in 'config' sets the
279
227
'checker_command' attribute and *not* the 'checker'
284
230
logger.debug(u"Creating client %r", self.name)
285
231
# Uppercase and remove spaces from fingerprint for later
286
232
# comparison purposes with return value from the fingerprint()
288
self.fingerprint = (config[u"fingerprint"].upper()
234
self.fingerprint = config["fingerprint"].upper()\
290
236
logger.debug(u" Fingerprint: %s", self.fingerprint)
291
if u"secret" in config:
292
self.secret = config[u"secret"].decode(u"base64")
293
elif u"secfile" in config:
294
with open(os.path.expanduser(os.path.expandvars
295
(config[u"secfile"])),
297
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()
299
244
raise TypeError(u"No secret or secfile for client %s"
301
self.host = config.get(u"host", u"")
302
self.created = datetime.datetime.utcnow()
304
self.last_enabled = None
246
self.host = config.get("host", "")
247
self.created = datetime.datetime.now()
305
248
self.last_checked_ok = None
306
self.timeout = string_to_delta(config[u"timeout"])
307
self.interval = string_to_delta(config[u"interval"])
308
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
309
252
self.checker = None
310
253
self.checker_initiator_tag = None
311
self.disable_initiator_tag = None
254
self.stop_initiator_tag = None
312
255
self.checker_callback_tag = None
313
self.checker_command = config[u"checker"]
314
self.current_checker_command = None
315
self.last_connect = None
256
self.check_command = config["checker"]
318
258
"""Start this client's checker and timeout hooks"""
319
if getattr(self, u"enabled", False):
322
self.last_enabled = datetime.datetime.utcnow()
323
259
# Schedule a new checker to be started an 'interval' from now,
324
260
# and every interval from then on.
325
self.checker_initiator_tag = (gobject.timeout_add
326
(self.interval_milliseconds(),
328
# Schedule a disable() when 'timeout' has passed
329
self.disable_initiator_tag = (gobject.timeout_add
330
(self.timeout_milliseconds(),
261
self.checker_initiator_tag = gobject.timeout_add\
262
(self._interval_milliseconds,
333
264
# Also start a new checker *right now*.
334
265
self.start_checker()
336
def disable(self, quiet=True):
337
"""Disable this client."""
338
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)
341
logger.info(u"Disabling client %s", self.name)
342
if getattr(self, u"disable_initiator_tag", False):
343
gobject.source_remove(self.disable_initiator_tag)
344
self.disable_initiator_tag = None
345
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):
346
284
gobject.source_remove(self.checker_initiator_tag)
347
285
self.checker_initiator_tag = None
348
286
self.stop_checker()
349
if self.disable_hook:
350
self.disable_hook(self)
352
289
# Do not run this again if called by a gobject.timeout_add
355
291
def __del__(self):
356
self.disable_hook = None
359
def checker_callback(self, pid, condition, command):
292
self.stop_hook = None
294
def checker_callback(self, pid, condition):
360
295
"""The checker has completed, so take appropriate actions."""
296
now = datetime.datetime.now()
361
297
self.checker_callback_tag = None
362
298
self.checker = None
363
if os.WIFEXITED(condition):
364
exitstatus = os.WEXITSTATUS(condition)
366
logger.info(u"Checker for %(name)s succeeded",
370
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):
373
309
logger.warning(u"Checker for %(name)s crashed?",
376
def checked_ok(self):
377
"""Bump up the timeout for this client.
379
This should only be called when the client has been seen,
382
self.last_checked_ok = datetime.datetime.utcnow()
383
gobject.source_remove(self.disable_initiator_tag)
384
self.disable_initiator_tag = (gobject.timeout_add
385
(self.timeout_milliseconds(),
312
logger.info(u"Checker for %(name)s failed",
388
314
def start_checker(self):
389
315
"""Start a new checker subprocess if one is not running.
391
316
If a checker already exists, leave it running and do
393
318
# The reason for not killing a running checker is that if we
441
347
# always replaced by /dev/null.)
442
348
self.checker = subprocess.Popen(command,
444
shell=True, cwd=u"/")
445
self.checker_callback_tag = (gobject.child_watch_add
447
self.checker_callback,
449
# The checker may have completed before the gobject
450
# watch was added. Check for this.
451
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
453
gobject.source_remove(self.checker_callback_tag)
454
self.checker_callback(pid, status, command)
351
self.checker_callback_tag = gobject.child_watch_add\
353
self.checker_callback)
455
354
except OSError, error:
456
355
logger.error(u"Failed to start subprocess: %s",
458
357
# Re-run this periodically if run by gobject.timeout_add
461
359
def stop_checker(self):
462
360
"""Force the checker process, if any, to stop."""
463
361
if self.checker_callback_tag:
464
362
gobject.source_remove(self.checker_callback_tag)
465
363
self.checker_callback_tag = None
466
if getattr(self, u"checker", None) is None:
364
if getattr(self, "checker", None) is None:
468
366
logger.debug(u"Stopping checker for %(name)s", vars(self))
470
368
os.kill(self.checker.pid, signal.SIGTERM)
472
370
#if self.checker.poll() is None:
473
371
# os.kill(self.checker.pid, signal.SIGKILL)
474
372
except OSError, error:
475
373
if error.errno != errno.ESRCH: # No such process
477
375
self.checker = None
480
def dbus_service_property(dbus_interface, signature=u"v",
481
access=u"readwrite", byte_arrays=False):
482
"""Decorators for marking methods of a DBusObjectWithProperties to
483
become properties on the D-Bus.
485
The decorated method will be called with no arguments by "Get"
486
and with one argument by "Set".
488
The parameters, where they are supported, are the same as
489
dbus.service.method, except there is only "signature", since the
490
type from Get() and the type sent to Set() is the same.
492
# Encoding deeply encoded byte arrays is not supported yet by the
493
# "Set" method, so we fail early here:
494
if byte_arrays and signature != u"ay":
495
raise ValueError(u"Byte arrays not supported for non-'ay'"
496
u" signature %r" % signature)
498
func._dbus_is_property = True
499
func._dbus_interface = dbus_interface
500
func._dbus_signature = signature
501
func._dbus_access = access
502
func._dbus_name = func.__name__
503
if func._dbus_name.endswith(u"_dbus_property"):
504
func._dbus_name = func._dbus_name[:-14]
505
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
510
class DBusPropertyException(dbus.exceptions.DBusException):
511
"""A base class for D-Bus property-related exceptions
513
def __unicode__(self):
514
return unicode(str(self))
517
class DBusPropertyAccessException(DBusPropertyException):
518
"""A property's access permissions disallows an operation.
523
class DBusPropertyNotFound(DBusPropertyException):
524
"""An attempt was made to access a non-existing property.
529
class DBusObjectWithProperties(dbus.service.Object):
530
"""A D-Bus object with properties.
532
Classes inheriting from this can use the dbus_service_property
533
decorator to expose methods as D-Bus properties. It exposes the
534
standard Get(), Set(), and GetAll() methods on the D-Bus.
538
def _is_dbus_property(obj):
539
return getattr(obj, u"_dbus_is_property", False)
541
def _get_all_dbus_properties(self):
542
"""Returns a generator of (name, attribute) pairs
544
return ((prop._dbus_name, prop)
546
inspect.getmembers(self, self._is_dbus_property))
548
def _get_dbus_property(self, interface_name, property_name):
549
"""Returns a bound method if one exists which is a D-Bus
550
property with the specified name and interface.
552
for name in (property_name,
553
property_name + u"_dbus_property"):
554
prop = getattr(self, name, None)
556
or not self._is_dbus_property(prop)
557
or prop._dbus_name != property_name
558
or (interface_name and prop._dbus_interface
559
and interface_name != prop._dbus_interface)):
563
raise DBusPropertyNotFound(self.dbus_object_path + u":"
564
+ interface_name + u"."
567
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
569
def Get(self, interface_name, property_name):
570
"""Standard D-Bus property Get() method, see D-Bus standard.
572
prop = self._get_dbus_property(interface_name, property_name)
573
if prop._dbus_access == u"write":
574
raise DBusPropertyAccessException(property_name)
576
if not hasattr(value, u"variant_level"):
578
return type(value)(value, variant_level=value.variant_level+1)
580
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
581
def Set(self, interface_name, property_name, value):
582
"""Standard D-Bus property Set() method, see D-Bus standard.
584
prop = self._get_dbus_property(interface_name, property_name)
585
if prop._dbus_access == u"read":
586
raise DBusPropertyAccessException(property_name)
587
if prop._dbus_get_args_options[u"byte_arrays"]:
588
# The byte_arrays option is not supported yet on
589
# signatures other than "ay".
590
if prop._dbus_signature != u"ay":
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,
631
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")
662
except (AttributeError, xml.dom.DOMException,
663
xml.parsers.expat.ExpatError), error:
664
logger.error(u"Failed to override Introspection method",
669
class ClientDBus(Client, DBusObjectWithProperties):
670
"""A Client class using D-Bus
673
dbus_object_path: dbus.ObjectPath
674
bus: dbus.SystemBus()
676
# dbus.service.Object doesn't use super(), so we can't either.
678
def __init__(self, bus = None, *args, **kwargs):
680
Client.__init__(self, *args, **kwargs)
681
# Only now, when this client is initialized, can it show up on
683
self.dbus_object_path = (dbus.ObjectPath
685
+ self.name.replace(u".", u"_")))
686
DBusObjectWithProperties.__init__(self, self.bus,
687
self.dbus_object_path)
690
def _datetime_to_dbus(dt, variant_level=0):
691
"""Convert a UTC datetime.datetime() to a D-Bus type."""
692
return dbus.String(dt.isoformat(),
693
variant_level=variant_level)
696
oldstate = getattr(self, u"enabled", False)
697
r = Client.enable(self)
698
if oldstate != self.enabled:
700
self.PropertyChanged(dbus.String(u"enabled"),
701
dbus.Boolean(True, variant_level=1))
702
self.PropertyChanged(
703
dbus.String(u"last_enabled"),
704
self._datetime_to_dbus(self.last_enabled,
708
def disable(self, quiet = False):
709
oldstate = getattr(self, u"enabled", False)
710
r = Client.disable(self, quiet=quiet)
711
if not quiet and oldstate != self.enabled:
713
self.PropertyChanged(dbus.String(u"enabled"),
714
dbus.Boolean(False, variant_level=1))
717
def __del__(self, *args, **kwargs):
719
self.remove_from_connection()
722
if hasattr(DBusObjectWithProperties, u"__del__"):
723
DBusObjectWithProperties.__del__(self, *args, **kwargs)
724
Client.__del__(self, *args, **kwargs)
726
def checker_callback(self, pid, condition, command,
728
self.checker_callback_tag = None
731
self.PropertyChanged(dbus.String(u"checker_running"),
732
dbus.Boolean(False, variant_level=1))
733
if os.WIFEXITED(condition):
734
exitstatus = os.WEXITSTATUS(condition)
736
self.CheckerCompleted(dbus.Int16(exitstatus),
737
dbus.Int64(condition),
738
dbus.String(command))
741
self.CheckerCompleted(dbus.Int16(-1),
742
dbus.Int64(condition),
743
dbus.String(command))
745
return Client.checker_callback(self, pid, condition, command,
748
def checked_ok(self, *args, **kwargs):
749
r = Client.checked_ok(self, *args, **kwargs)
751
self.PropertyChanged(
752
dbus.String(u"last_checked_ok"),
753
(self._datetime_to_dbus(self.last_checked_ok,
757
def start_checker(self, *args, **kwargs):
758
old_checker = self.checker
759
if self.checker is not None:
760
old_checker_pid = self.checker.pid
762
old_checker_pid = None
763
r = Client.start_checker(self, *args, **kwargs)
764
# Only if new checker process was started
765
if (self.checker is not None
766
and old_checker_pid != self.checker.pid):
768
self.CheckerStarted(self.current_checker_command)
769
self.PropertyChanged(
770
dbus.String(u"checker_running"),
771
dbus.Boolean(True, variant_level=1))
774
def stop_checker(self, *args, **kwargs):
775
old_checker = getattr(self, u"checker", None)
776
r = Client.stop_checker(self, *args, **kwargs)
777
if (old_checker is not None
778
and getattr(self, u"checker", None) is None):
779
self.PropertyChanged(dbus.String(u"checker_running"),
780
dbus.Boolean(False, variant_level=1))
783
## D-Bus methods, signals & properties
784
_interface = u"se.bsnet.fukt.Mandos.Client"
788
# CheckerCompleted - signal
789
@dbus.service.signal(_interface, signature=u"nxs")
790
def CheckerCompleted(self, exitcode, waitstatus, command):
794
# CheckerStarted - signal
795
@dbus.service.signal(_interface, signature=u"s")
796
def CheckerStarted(self, command):
800
# PropertyChanged - signal
801
@dbus.service.signal(_interface, signature=u"sv")
802
def PropertyChanged(self, property, value):
807
@dbus.service.signal(_interface)
813
@dbus.service.signal(_interface)
821
@dbus.service.method(_interface)
823
return self.checked_ok()
826
@dbus.service.method(_interface)
831
# StartChecker - method
832
@dbus.service.method(_interface)
833
def StartChecker(self):
838
@dbus.service.method(_interface)
843
# StopChecker - method
844
@dbus.service.method(_interface)
845
def StopChecker(self):
851
@dbus_service_property(_interface, signature=u"s", access=u"read")
852
def name_dbus_property(self):
853
return dbus.String(self.name)
855
# fingerprint - property
856
@dbus_service_property(_interface, signature=u"s", access=u"read")
857
def fingerprint_dbus_property(self):
858
return dbus.String(self.fingerprint)
861
@dbus_service_property(_interface, signature=u"s",
863
def host_dbus_property(self, value=None):
864
if value is None: # get
865
return dbus.String(self.host)
868
self.PropertyChanged(dbus.String(u"host"),
869
dbus.String(value, variant_level=1))
872
@dbus_service_property(_interface, signature=u"s", access=u"read")
873
def created_dbus_property(self):
874
return dbus.String(self._datetime_to_dbus(self.created))
876
# last_enabled - property
877
@dbus_service_property(_interface, signature=u"s", access=u"read")
878
def last_enabled_dbus_property(self):
879
if self.last_enabled is None:
880
return dbus.String(u"")
881
return dbus.String(self._datetime_to_dbus(self.last_enabled))
884
@dbus_service_property(_interface, signature=u"b",
886
def enabled_dbus_property(self, value=None):
887
if value is None: # get
888
return dbus.Boolean(self.enabled)
894
# last_checked_ok - property
895
@dbus_service_property(_interface, signature=u"s",
897
def last_checked_ok_dbus_property(self, value=None):
898
if value is not None:
376
def still_valid(self):
377
"""Has the timeout not yet passed for this client?"""
378
now = datetime.datetime.now()
901
379
if self.last_checked_ok is None:
902
return dbus.String(u"")
903
return dbus.String(self._datetime_to_dbus(self
907
@dbus_service_property(_interface, signature=u"t",
909
def timeout_dbus_property(self, value=None):
910
if value is None: # get
911
return dbus.UInt64(self.timeout_milliseconds())
912
self.timeout = datetime.timedelta(0, 0, 0, value)
914
self.PropertyChanged(dbus.String(u"timeout"),
915
dbus.UInt64(value, variant_level=1))
916
if getattr(self, u"disable_initiator_tag", None) is None:
919
gobject.source_remove(self.disable_initiator_tag)
920
self.disable_initiator_tag = None
922
_timedelta_to_milliseconds((self
928
# The timeout has passed
931
self.disable_initiator_tag = (gobject.timeout_add
932
(time_to_die, self.disable))
934
# interval - property
935
@dbus_service_property(_interface, signature=u"t",
937
def interval_dbus_property(self, value=None):
938
if value is None: # get
939
return dbus.UInt64(self.interval_milliseconds())
940
self.interval = datetime.timedelta(0, 0, 0, value)
942
self.PropertyChanged(dbus.String(u"interval"),
943
dbus.UInt64(value, variant_level=1))
944
if getattr(self, u"checker_initiator_tag", None) is None:
946
# Reschedule checker run
947
gobject.source_remove(self.checker_initiator_tag)
948
self.checker_initiator_tag = (gobject.timeout_add
949
(value, self.start_checker))
950
self.start_checker() # Start one now, too
953
@dbus_service_property(_interface, signature=u"s",
955
def checker_dbus_property(self, value=None):
956
if value is None: # get
957
return dbus.String(self.checker_command)
958
self.checker_command = value
960
self.PropertyChanged(dbus.String(u"checker"),
961
dbus.String(self.checker_command,
964
# checker_running - property
965
@dbus_service_property(_interface, signature=u"b",
967
def checker_running_dbus_property(self, value=None):
968
if value is None: # get
969
return dbus.Boolean(self.checker is not None)
975
# object_path - property
976
@dbus_service_property(_interface, signature=u"o", access=u"read")
977
def object_path_dbus_property(self):
978
return self.dbus_object_path # is already a dbus.ObjectPath
981
@dbus_service_property(_interface, signature=u"ay",
982
access=u"write", byte_arrays=True)
983
def secret_dbus_property(self, value):
984
self.secret = str(value)
989
class ClientHandler(socketserver.BaseRequestHandler, object):
990
"""A class to handle client connections.
992
Instantiated once for each connection to handle it.
380
return now < (self.created + self.timeout)
382
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.
993
442
Note: This will run in its own forked process."""
995
444
def handle(self):
996
445
logger.info(u"TCP connection from: %s",
997
unicode(self.client_address))
998
logger.debug(u"IPC Pipe FD: %d",
999
self.server.child_pipe[1].fileno())
1000
# Open IPC pipe to parent process
1001
with contextlib.nested(self.server.child_pipe[1],
1002
self.server.parent_pipe[0]
1003
) as (ipc, ipc_return):
1004
session = (gnutls.connection
1005
.ClientSession(self.request,
1007
.X509Credentials()))
1009
line = self.request.makefile().readline()
1010
logger.debug(u"Protocol version: %r", line)
1012
if int(line.strip().split()[0]) > 1:
1014
except (ValueError, IndexError, RuntimeError), error:
1015
logger.error(u"Unknown protocol version: %s", error)
1018
# Note: gnutls.connection.X509Credentials is really a
1019
# generic GnuTLS certificate credentials object so long as
1020
# no X.509 keys are added to it. Therefore, we can use it
1021
# here despite using OpenPGP certificates.
1023
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1024
# u"+AES-256-CBC", u"+SHA1",
1025
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1027
# Use a fallback default, since this MUST be set.
1028
priority = self.server.gnutls_priority
1029
if priority is None:
1030
priority = u"NORMAL"
1031
(gnutls.library.functions
1032
.gnutls_priority_set_direct(session._c_object,
1037
except gnutls.errors.GNUTLSError, error:
1038
logger.warning(u"Handshake failed: %s", error)
1039
# Do not run session.bye() here: the session is not
1040
# established. Just abandon the request.
1042
logger.debug(u"Handshake succeeded")
1045
fpr = self.fingerprint(self.peer_certificate
1047
except (TypeError, gnutls.errors.GNUTLSError), error:
1048
logger.warning(u"Bad certificate: %s", error)
1050
logger.debug(u"Fingerprint: %s", fpr)
1052
for c in self.server.clients:
1053
if c.fingerprint == fpr:
1057
ipc.write(u"NOTFOUND %s %s\n"
1058
% (fpr, unicode(self.client_address)))
1060
# Have to check if client.enabled, since it is
1061
# possible that the client was disabled since the
1062
# GnuTLS session was established.
1063
ipc.write(u"GETATTR enabled %s\n" % fpr)
1064
enabled = pickle.load(ipc_return)
1066
ipc.write(u"DISABLED %s\n" % client.name)
1068
# Send "NEED_APPROVAL" here and hang waiting
1069
# for response? Leave timeout to parent process?
1070
ipc.write(u"SENDING %s\n" % client.name)
1072
while sent_size < len(client.secret):
1073
sent = session.send(client.secret[sent_size:])
1074
logger.debug(u"Sent: %d, remaining: %d",
1075
sent, len(client.secret)
1076
- (sent_size + sent))
1082
def peer_certificate(session):
1083
"Return the peer's OpenPGP certificate as a bytestring"
1084
# If not an OpenPGP certificate...
1085
if (gnutls.library.functions
1086
.gnutls_certificate_type_get(session._c_object)
1087
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1088
# ...do the normal thing
1089
return session.peer_certificate
1090
list_size = ctypes.c_uint(1)
1091
cert_list = (gnutls.library.functions
1092
.gnutls_certificate_get_peers
1093
(session._c_object, ctypes.byref(list_size)))
1094
if not bool(cert_list) and list_size.value != 0:
1095
raise gnutls.errors.GNUTLSError(u"error getting peer"
1097
if list_size.value == 0:
1100
return ctypes.string_at(cert.data, cert.size)
1103
def fingerprint(openpgp):
1104
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1105
# New GnuTLS "datum" with the OpenPGP public key
1106
datum = (gnutls.library.types
1107
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1110
ctypes.c_uint(len(openpgp))))
1111
# New empty GnuTLS certificate
1112
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1113
(gnutls.library.functions
1114
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1115
# Import the OpenPGP public key into the certificate
1116
(gnutls.library.functions
1117
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1118
gnutls.library.constants
1119
.GNUTLS_OPENPGP_FMT_RAW))
1120
# Verify the self signature in the key
1121
crtverify = ctypes.c_uint()
1122
(gnutls.library.functions
1123
.gnutls_openpgp_crt_verify_self(crt, 0,
1124
ctypes.byref(crtverify)))
1125
if crtverify.value != 0:
1126
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1127
raise (gnutls.errors.CertificateSecurityError
1129
# New buffer for the fingerprint
1130
buf = ctypes.create_string_buffer(20)
1131
buf_len = ctypes.c_size_t()
1132
# Get the fingerprint from the certificate into the buffer
1133
(gnutls.library.functions
1134
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1135
ctypes.byref(buf_len)))
1136
# Deinit the certificate
1137
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1138
# Convert the buffer to a Python bytestring
1139
fpr = ctypes.string_at(buf, buf_len.value)
1140
# Convert the bytestring to hexadecimal notation
1141
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1145
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
1146
"""Like socketserver.ForkingMixIn, but also pass a pipe pair."""
1147
def process_request(self, request, client_address):
1148
"""Overrides and wraps the original process_request().
1150
This function creates a new pipe in self.pipe
1152
# Child writes to child_pipe
1153
self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1154
# Parent writes to parent_pipe
1155
self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1156
super(ForkingMixInWithPipes,
1157
self).process_request(request, client_address)
1158
# Close unused ends for parent
1159
self.parent_pipe[0].close() # close read end
1160
self.child_pipe[1].close() # close write end
1161
self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
1162
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1163
"""Dummy function; override as necessary"""
1164
child_pipe_fd.close()
1165
parent_pipe_fd.close()
1168
class IPv6_TCPServer(ForkingMixInWithPipes,
1169
socketserver.TCPServer, object):
1170
"""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
1173
521
enabled: Boolean; whether this server is activated yet
1174
interface: None or a network interface name (string)
1175
use_ipv6: Boolean; to use IPv6 or not
1177
def __init__(self, server_address, RequestHandlerClass,
1178
interface=None, use_ipv6=True):
1179
self.interface = interface
1181
self.address_family = socket.AF_INET6
1182
socketserver.TCPServer.__init__(self, server_address,
1183
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)
1184
533
def server_bind(self):
1185
534
"""This overrides the normal server_bind() function
1186
535
to bind to an interface if one was specified, and also NOT to
1187
536
bind to an address or port if they were not specified."""
1188
if self.interface is not None:
1189
if SO_BINDTODEVICE is None:
1190
logger.error(u"SO_BINDTODEVICE does not exist;"
1191
u" cannot bind to interface %s",
1195
self.socket.setsockopt(socket.SOL_SOCKET,
1199
except socket.error, error:
1200
if error[0] == errno.EPERM:
1201
logger.error(u"No permission to"
1202
u" bind to interface %s",
1204
elif error[0] == errno.ENOPROTOOPT:
1205
logger.error(u"SO_BINDTODEVICE not available;"
1206
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"])
1210
551
# Only bind(2) the socket if we really need to.
1211
552
if self.server_address[0] or self.server_address[1]:
1212
553
if not self.server_address[0]:
1213
if self.address_family == socket.AF_INET6:
1214
any_address = u"::" # in6addr_any
1216
any_address = socket.INADDR_ANY
1217
self.server_address = (any_address,
555
self.server_address = (in6addr_any,
1218
556
self.server_address[1])
1219
557
elif not self.server_address[1]:
1220
558
self.server_address = (self.server_address[0],
1222
# if self.interface:
560
# if self.settings["interface"]:
1223
561
# self.server_address = (self.server_address[0],
1226
564
# if_nametoindex
1228
return socketserver.TCPServer.server_bind(self)
1231
class MandosServer(IPv6_TCPServer):
1235
clients: set of Client objects
1236
gnutls_priority GnuTLS priority string
1237
use_dbus: Boolean; to emit D-Bus signals or not
1239
Assumes a gobject.MainLoop event loop.
1241
def __init__(self, server_address, RequestHandlerClass,
1242
interface=None, use_ipv6=True, clients=None,
1243
gnutls_priority=None, use_dbus=True):
1244
self.enabled = False
1245
self.clients = clients
1246
if self.clients is None:
1247
self.clients = set()
1248
self.use_dbus = use_dbus
1249
self.gnutls_priority = gnutls_priority
1250
IPv6_TCPServer.__init__(self, server_address,
1251
RequestHandlerClass,
1252
interface = interface,
1253
use_ipv6 = use_ipv6)
567
return super(type(self), self).server_bind()
1254
568
def server_activate(self):
1255
569
if self.enabled:
1256
return socketserver.TCPServer.server_activate(self)
570
return super(type(self), self).server_activate()
1257
571
def enable(self):
1258
572
self.enabled = True
1259
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1260
# Call "handle_ipc" for both data and EOF events
1261
gobject.io_add_watch(child_pipe_fd.fileno(),
1262
gobject.IO_IN | gobject.IO_HUP,
1263
functools.partial(self.handle_ipc,
1264
reply = parent_pipe_fd,
1265
sender= child_pipe_fd))
1266
def handle_ipc(self, source, condition, reply=None, sender=None):
1268
gobject.IO_IN: u"IN", # There is data to read.
1269
gobject.IO_OUT: u"OUT", # Data can be written (without
1271
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1272
gobject.IO_ERR: u"ERR", # Error condition.
1273
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1274
# broken, usually for pipes and
1277
conditions_string = ' | '.join(name
1279
condition_names.iteritems()
1280
if cond & condition)
1281
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1284
# Read a line from the file object
1285
cmdline = sender.readline()
1286
if not cmdline: # Empty line means end of file
1287
# close the IPC pipes
1291
# Stop calling this function
1294
logger.debug(u"IPC command: %r", cmdline)
1296
# Parse and act on command
1297
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1299
if cmd == u"NOTFOUND":
1300
fpr, address = args.split(None, 1)
1301
logger.warning(u"Client not found for fingerprint: %s, ad"
1302
u"dress: %s", fpr, address)
1305
mandos_dbus_service.ClientNotFound(fpr, address)
1306
elif cmd == u"DISABLED":
1307
for client in self.clients:
1308
if client.name == args:
1309
logger.warning(u"Client %s is disabled", args)
1315
logger.error(u"Unknown client %s is disabled", args)
1316
elif cmd == u"SENDING":
1317
for client in self.clients:
1318
if client.name == args:
1319
logger.info(u"Sending secret to %s", client.name)
1326
logger.error(u"Sending secret to unknown client %s",
1328
elif cmd == u"GETATTR":
1329
attr_name, fpr = args.split(None, 1)
1330
for client in self.clients:
1331
if client.fingerprint == fpr:
1332
attr_value = getattr(client, attr_name, None)
1333
logger.debug("IPC reply: %r", attr_value)
1334
pickle.dump(attr_value, reply)
1337
logger.error(u"Client %s on address %s requesting "
1338
u"attribute %s not found", fpr, address,
1340
pickle.dump(None, reply)
1342
logger.error(u"Unknown IPC command: %r", cmdline)
1344
# Keep calling this function
1348
575
def string_to_delta(interval):
1349
576
"""Parse a string and return a datetime.timedelta
1351
>>> string_to_delta(u'7d')
578
>>> string_to_delta('7d')
1352
579
datetime.timedelta(7)
1353
>>> string_to_delta(u'60s')
580
>>> string_to_delta('60s')
1354
581
datetime.timedelta(0, 60)
1355
>>> string_to_delta(u'60m')
582
>>> string_to_delta('60m')
1356
583
datetime.timedelta(0, 3600)
1357
>>> string_to_delta(u'24h')
584
>>> string_to_delta('24h')
1358
585
datetime.timedelta(1)
1359
586
>>> string_to_delta(u'1w')
1360
587
datetime.timedelta(7)
1361
>>> string_to_delta(u'5m 30s')
588
>>> string_to_delta('5m 30s')
1362
589
datetime.timedelta(0, 330)
1364
591
timevalue = datetime.timedelta(0)
1365
592
for s in interval.split():
1367
suffix = unicode(s[-1])
594
suffix=unicode(s[-1])
1369
596
if suffix == u"d":
1370
597
delta = datetime.timedelta(value)
1371
598
elif suffix == u"s":
1473
716
# Default values for config file for server-global settings
1474
server_defaults = { u"interface": u"",
1479
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1480
u"servicename": u"Mandos",
1481
u"use_dbus": u"True",
1482
u"use_ipv6": u"True",
717
server_defaults = { "interface": "",
722
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
723
"servicename": "Mandos",
1485
726
# Parse config file for server-global settings
1486
server_config = configparser.SafeConfigParser(server_defaults)
727
server_config = ConfigParser.SafeConfigParser(server_defaults)
1487
728
del server_defaults
1488
server_config.read(os.path.join(options.configdir,
729
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1490
730
# Convert the SafeConfigParser object to a dict
1491
731
server_settings = server_config.defaults()
1492
# Use the appropriate methods on the non-string config options
1493
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1494
server_settings[option] = server_config.getboolean(u"DEFAULT",
1496
if server_settings["port"]:
1497
server_settings["port"] = server_config.getint(u"DEFAULT",
732
# Use getboolean on the boolean config option
733
server_settings["debug"] = server_config.getboolean\
1499
735
del server_config
1501
737
# Override the settings from the config file with command line
1502
738
# options, if set.
1503
for option in (u"interface", u"address", u"port", u"debug",
1504
u"priority", u"servicename", u"configdir",
1505
u"use_dbus", u"use_ipv6"):
739
for option in ("interface", "address", "port", "debug",
740
"priority", "servicename", "configdir"):
1506
741
value = getattr(options, option)
1507
742
if value is not None:
1508
743
server_settings[option] = value
1510
# Force all strings to be unicode
1511
for option in server_settings.keys():
1512
if type(server_settings[option]) is str:
1513
server_settings[option] = unicode(server_settings[option])
1514
745
# Now we have our good server settings in "server_settings"
1516
##################################################################
1519
debug = server_settings[u"debug"]
1520
use_dbus = server_settings[u"use_dbus"]
1521
use_ipv6 = server_settings[u"use_ipv6"]
747
debug = server_settings["debug"]
1524
750
syslogger.setLevel(logging.WARNING)
1525
751
console.setLevel(logging.WARNING)
1527
if server_settings[u"servicename"] != u"Mandos":
1528
syslogger.setFormatter(logging.Formatter
1529
(u'Mandos (%s) [%%(process)d]:'
1530
u' %%(levelname)s: %%(message)s'
1531
% server_settings[u"servicename"]))
753
if server_settings["servicename"] != "Mandos":
754
syslogger.setFormatter(logging.Formatter\
755
('Mandos (%s): %%(levelname)s:'
757
% server_settings["servicename"]))
1533
759
# Parse config file with clients
1534
client_defaults = { u"timeout": u"1h",
1536
u"checker": u"fping -q -- %%(host)s",
760
client_defaults = { "timeout": "1h",
762
"checker": "fping -q -- %(host)s",
1539
client_config = configparser.SafeConfigParser(client_defaults)
1540
client_config.read(os.path.join(server_settings[u"configdir"],
1543
global mandos_dbus_service
1544
mandos_dbus_service = None
1546
tcp_server = MandosServer((server_settings[u"address"],
1547
server_settings[u"port"]),
1549
interface=server_settings[u"interface"],
1552
server_settings[u"priority"],
1554
pidfilename = u"/var/run/mandos.pid"
1556
pidfile = open(pidfilename, u"w")
1558
logger.error(u"Could not open file %r", pidfilename)
1561
uid = pwd.getpwnam(u"_mandos").pw_uid
1562
gid = pwd.getpwnam(u"_mandos").pw_gid
1565
uid = pwd.getpwnam(u"mandos").pw_uid
1566
gid = pwd.getpwnam(u"mandos").pw_gid
1569
uid = pwd.getpwnam(u"nobody").pw_uid
1570
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
1577
800
except OSError, error:
1578
801
if error[0] != errno.EPERM:
1581
# Enable all possible GnuTLS debugging
1583
# "Use a log level over 10 to enable all debugging options."
1585
gnutls.library.functions.gnutls_global_set_log_level(11)
1587
@gnutls.library.types.gnutls_log_func
1588
def debug_gnutls(level, string):
1589
logger.debug(u"GnuTLS: %s", string[:-1])
1591
(gnutls.library.functions
1592
.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"])
1594
811
global main_loop
1595
814
# From the Avahi example code
1596
815
DBusGMainLoop(set_as_default=True )
1597
816
main_loop = gobject.MainLoop()
1598
817
bus = dbus.SystemBus()
818
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
819
avahi.DBUS_PATH_SERVER),
820
avahi.DBUS_INTERFACE_SERVER)
1599
821
# End of Avahi example code
1602
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1603
bus, do_not_queue=True)
1604
except dbus.exceptions.NameExistsException, e:
1605
logger.error(unicode(e) + u", disabling D-Bus")
1607
server_settings[u"use_dbus"] = False
1608
tcp_server.use_dbus = False
1609
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1610
service = AvahiService(name = server_settings[u"servicename"],
1611
servicetype = u"_mandos._tcp",
1612
protocol = protocol, bus = bus)
1613
if server_settings["interface"]:
1614
service.interface = (if_nametoindex
1615
(str(server_settings[u"interface"])))
1617
client_class = Client
1619
client_class = functools.partial(ClientDBus, bus = bus)
1620
tcp_server.clients.update(set(
1621
client_class(name = section,
1622
config= dict(client_config.items(section)))
1623
for section in client_config.sections()))
1624
if not tcp_server.clients:
1625
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")
1628
839
# Redirect stdin so all checkers get /dev/null