126
99
max_renames: integer; maximum number of renames
127
100
rename_count: integer; counter so we only rename after collisions
128
101
a sensible number of times
129
group: D-Bus Entry Group
131
bus: dbus.SystemBus()
133
103
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):
104
type = None, port = None, TXT = None, domain = "",
105
host = "", max_renames = 32768):
137
106
self.interface = interface
139
self.type = servicetype
141
self.TXT = TXT if TXT is not None else []
142
114
self.domain = domain
144
116
self.rename_count = 0
145
self.max_renames = max_renames
146
self.protocol = protocol
147
self.group = None # our entry group
150
117
def rename(self):
151
118
"""Derived from the Avahi example code"""
152
119
if self.rename_count >= self.max_renames:
153
logger.critical(u"No suitable Zeroconf service name found"
154
u" after %i retries, exiting.",
156
raise AvahiServiceError(u"Too many renames")
157
self.name = self.server.GetAlternativeServiceName(self.name)
158
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'
120
logger.critical(u"No suitable service name found after %i"
121
u" retries, exiting.", rename_count)
122
raise AvahiServiceError("Too many renames")
123
name = server.GetAlternativeServiceName(name)
124
logger.error(u"Changing name to %r ...", name)
125
syslogger.setFormatter(logging.Formatter\
126
('Mandos (%s): %%(levelname)s:'
127
' %%(message)s' % name))
166
130
self.rename_count += 1
167
131
def remove(self):
168
132
"""Derived from the Avahi example code"""
169
if self.group is not None:
133
if group is not None:
172
136
"""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)
181
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())
139
group = dbus.Interface\
140
(bus.get_object(avahi.DBUS_NAME,
141
server.EntryGroupNew()),
142
avahi.DBUS_INTERFACE_ENTRY_GROUP)
143
group.connect_to_signal('StateChanged',
144
entry_group_state_changed)
145
logger.debug(u"Adding service '%s' of type '%s' ...",
146
service.name, service.type)
148
self.interface, # interface
149
avahi.PROTO_INET6, # protocol
150
dbus.UInt32(0), # flags
151
self.name, self.type,
152
self.domain, self.host,
153
dbus.UInt16(self.port),
154
avahi.string_array_to_txt_array(self.TXT))
157
# From the Avahi example code:
158
group = None # our entry group
159
# End of Avahi example code
230
162
class Client(object):
231
163
"""A representation of a client host served by this server.
234
name: string; from the config file, used in log messages and
165
name: string; from the config file, used in log messages
236
166
fingerprint: string (40 or 32 hexadecimal digits); used to
237
167
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.
168
secret: bytestring; sent verbatim (over TLS) to client
169
host: string; available for use by the checker command
170
created: datetime.datetime(); object creation, not client host
171
last_checked_ok: datetime.datetime() or None if not yet checked OK
172
timeout: datetime.timedelta(); How long from last_checked_ok
173
until this client is invalid
174
interval: datetime.timedelta(); How often to start a new checker
175
stop_hook: If set, called by stop() as stop_hook(self)
176
checker: subprocess.Popen(); a running checker process used
177
to see if the client lives.
178
'None' if no process is running.
251
179
checker_initiator_tag: a gobject event source tag, or None
252
disable_initiator_tag: - '' -
180
stop_initiator_tag: - '' -
253
181
checker_callback_tag: - '' -
254
182
checker_command: string; External command which is run to check if
255
183
client lives. %() expansions are done at
256
184
runtime with vars(self) as dict, so that for
257
185
instance %(name)s can be used in the command.
258
current_checker_command: string; current running checker_command
187
_timeout: Real variable for 'timeout'
188
_interval: Real variable for 'interval'
189
_timeout_milliseconds: Used when calling gobject.timeout_add()
190
_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):
192
def _set_timeout(self, timeout):
193
"Setter function for 'timeout' attribute"
194
self._timeout = timeout
195
self._timeout_milliseconds = ((self.timeout.days
196
* 24 * 60 * 60 * 1000)
197
+ (self.timeout.seconds * 1000)
198
+ (self.timeout.microseconds
200
timeout = property(lambda self: self._timeout,
203
def _set_interval(self, interval):
204
"Setter function for 'interval' attribute"
205
self._interval = interval
206
self._interval_milliseconds = ((self.interval.days
207
* 24 * 60 * 60 * 1000)
208
+ (self.interval.seconds
210
+ (self.interval.microseconds
212
interval = property(lambda self: self._interval,
215
def __init__(self, name = None, stop_hook=None, config={}):
277
216
"""Note: the 'checker' key in 'config' sets the
278
217
'checker_command' attribute and *not* the 'checker'
283
220
logger.debug(u"Creating client %r", self.name)
284
221
# Uppercase and remove spaces from fingerprint for later
285
222
# comparison purposes with return value from the fingerprint()
287
self.fingerprint = (config[u"fingerprint"].upper()
224
self.fingerprint = config["fingerprint"].upper()\
289
226
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()
227
if "secret" in config:
228
self.secret = config["secret"].decode(u"base64")
229
elif "secfile" in config:
230
sf = open(config["secfile"])
231
self.secret = sf.read()
298
234
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
236
self.host = config.get("host", "")
237
self.created = datetime.datetime.now()
304
238
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
239
self.timeout = string_to_delta(config["timeout"])
240
self.interval = string_to_delta(config["interval"])
241
self.stop_hook = stop_hook
308
242
self.checker = None
309
243
self.checker_initiator_tag = None
310
self.disable_initiator_tag = None
244
self.stop_initiator_tag = None
311
245
self.checker_callback_tag = None
312
self.checker_command = config[u"checker"]
313
self.current_checker_command = None
314
self.last_connect = None
246
self.check_command = config["checker"]
317
248
"""Start this client's checker and timeout hooks"""
318
if getattr(self, u"enabled", False):
321
self.last_enabled = datetime.datetime.utcnow()
322
249
# Schedule a new checker to be started an 'interval' from now,
323
250
# and every interval from then on.
324
self.checker_initiator_tag = (gobject.timeout_add
325
(self.interval_milliseconds(),
251
self.checker_initiator_tag = gobject.timeout_add\
252
(self._interval_milliseconds,
327
254
# Also start a new checker *right now*.
328
255
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):
256
# Schedule a stop() when 'timeout' has passed
257
self.stop_initiator_tag = gobject.timeout_add\
258
(self._timeout_milliseconds,
262
The possibility that a client might be restarted is left open,
263
but not currently used."""
264
# If this client doesn't have a secret, it is already stopped.
265
if hasattr(self, "secret") and self.secret:
266
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):
270
if getattr(self, "stop_initiator_tag", False):
271
gobject.source_remove(self.stop_initiator_tag)
272
self.stop_initiator_tag = None
273
if getattr(self, "checker_initiator_tag", False):
344
274
gobject.source_remove(self.checker_initiator_tag)
345
275
self.checker_initiator_tag = None
346
276
self.stop_checker()
347
if self.disable_hook:
348
self.disable_hook(self)
350
279
# Do not run this again if called by a gobject.timeout_add
353
281
def __del__(self):
354
self.disable_hook = None
357
def checker_callback(self, pid, condition, command):
282
self.stop_hook = None
284
def checker_callback(self, pid, condition):
358
285
"""The checker has completed, so take appropriate actions."""
286
now = datetime.datetime.now()
359
287
self.checker_callback_tag = None
360
288
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",
289
if os.WIFEXITED(condition) \
290
and (os.WEXITSTATUS(condition) == 0):
291
logger.info(u"Checker for %(name)s succeeded",
293
self.last_checked_ok = now
294
gobject.source_remove(self.stop_initiator_tag)
295
self.stop_initiator_tag = gobject.timeout_add\
296
(self._timeout_milliseconds,
298
elif not os.WIFEXITED(condition):
371
299
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(),
302
logger.info(u"Checker for %(name)s failed",
386
304
def start_checker(self):
387
305
"""Start a new checker subprocess if one is not running.
389
306
If a checker already exists, leave it running and do
391
308
# The reason for not killing a running checker is that if we
473
359
if error.errno != errno.ESRCH: # No such process
475
361
self.checker = None
477
362
def still_valid(self):
478
363
"""Has the timeout not yet passed for this client?"""
479
if not getattr(self, u"enabled", False):
481
now = datetime.datetime.utcnow()
364
now = datetime.datetime.now()
482
365
if self.last_checked_ok is None:
483
366
return now < (self.created + self.timeout)
485
368
return now < (self.last_checked_ok + self.timeout)
488
def dbus_service_property(dbus_interface, signature=u"v",
489
access=u"readwrite", byte_arrays=False):
490
"""Decorators for marking methods of a DBusObjectWithProperties to
491
become properties on the D-Bus.
493
The decorated method will be called with no arguments by "Get"
494
and with one argument by "Set".
496
The parameters, where they are supported, are the same as
497
dbus.service.method, except there is only "signature", since the
498
type from Get() and the type sent to Set() is the same.
501
func._dbus_is_property = True
502
func._dbus_interface = dbus_interface
503
func._dbus_signature = signature
504
func._dbus_access = access
505
func._dbus_name = func.__name__
506
if func._dbus_name.endswith(u"_dbus_property"):
507
func._dbus_name = func._dbus_name[:-14]
508
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
513
class DBusPropertyException(dbus.exceptions.DBusException):
514
"""A base class for D-Bus property-related exceptions
516
def __unicode__(self):
517
return unicode(str(self))
520
class DBusPropertyAccessException(DBusPropertyException):
521
"""A property's access permissions disallows an operation.
526
class DBusPropertyNotFound(DBusPropertyException):
527
"""An attempt was made to access a non-existing property.
532
class DBusObjectWithProperties(dbus.service.Object):
533
"""A D-Bus object with properties.
535
Classes inheriting from this can use the dbus_service_property
536
decorator to expose methods as D-Bus properties. It exposes the
537
standard Get(), Set(), and GetAll() methods on the D-Bus.
541
def _is_dbus_property(obj):
542
return getattr(obj, u"_dbus_is_property", False)
544
def _get_all_dbus_properties(self):
545
"""Returns a generator of (name, attribute) pairs
547
return ((prop._dbus_name, prop)
549
inspect.getmembers(self, self._is_dbus_property))
551
def _get_dbus_property(self, interface_name, property_name):
552
"""Returns a bound method if one exists which is a D-Bus
553
property with the specified name and interface.
555
for name in (property_name,
556
property_name + u"_dbus_property"):
557
prop = getattr(self, name, None)
559
or not self._is_dbus_property(prop)
560
or prop._dbus_name != property_name
561
or (interface_name and prop._dbus_interface
562
and interface_name != prop._dbus_interface)):
566
raise DBusPropertyNotFound(self.dbus_object_path + u":"
567
+ interface_name + u"."
570
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
572
def Get(self, interface_name, property_name):
573
"""Standard D-Bus property Get() method, see D-Bus standard.
575
prop = self._get_dbus_property(interface_name, property_name)
576
if prop._dbus_access == u"write":
577
raise DBusPropertyAccessException(property_name)
579
if not hasattr(value, u"variant_level"):
581
return type(value)(value, variant_level=value.variant_level+1)
583
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
584
def Set(self, interface_name, property_name, value):
585
"""Standard D-Bus property Set() method, see D-Bus standard.
587
prop = self._get_dbus_property(interface_name, property_name)
588
if prop._dbus_access == u"read":
589
raise DBusPropertyAccessException(property_name)
590
if prop._dbus_get_args_options[u"byte_arrays"]:
591
value = dbus.ByteArray(''.join(unichr(byte)
595
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
596
out_signature=u"a{sv}")
597
def GetAll(self, interface_name):
598
"""Standard D-Bus property GetAll() method, see D-Bus
601
Note: Will not include properties with access="write".
604
for name, prop in self._get_all_dbus_properties():
606
and interface_name != prop._dbus_interface):
607
# Interface non-empty but did not match
609
# Ignore write-only properties
610
if prop._dbus_access == u"write":
613
if not hasattr(value, u"variant_level"):
616
all[name] = type(value)(value, variant_level=
617
value.variant_level+1)
618
return dbus.Dictionary(all, signature=u"sv")
620
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
622
path_keyword='object_path',
623
connection_keyword='connection')
624
def Introspect(self, object_path, connection):
625
"""Standard D-Bus method, overloaded to insert property tags.
627
xmlstring = dbus.service.Object.Introspect(self, object_path,
629
document = xml.dom.minidom.parseString(xmlstring)
631
def make_tag(document, name, prop):
632
e = document.createElement(u"property")
633
e.setAttribute(u"name", name)
634
e.setAttribute(u"type", prop._dbus_signature)
635
e.setAttribute(u"access", prop._dbus_access)
637
for if_tag in document.getElementsByTagName(u"interface"):
638
for tag in (make_tag(document, name, prop)
640
in self._get_all_dbus_properties()
641
if prop._dbus_interface
642
== if_tag.getAttribute(u"name")):
643
if_tag.appendChild(tag)
644
xmlstring = document.toxml(u"utf-8")
649
class ClientDBus(Client, DBusObjectWithProperties):
650
"""A Client class using D-Bus
653
dbus_object_path: dbus.ObjectPath
654
bus: dbus.SystemBus()
656
# dbus.service.Object doesn't use super(), so we can't either.
658
def __init__(self, bus = None, *args, **kwargs):
660
Client.__init__(self, *args, **kwargs)
661
# Only now, when this client is initialized, can it show up on
663
self.dbus_object_path = (dbus.ObjectPath
665
+ self.name.replace(u".", u"_")))
666
DBusObjectWithProperties.__init__(self, self.bus,
667
self.dbus_object_path)
670
def _datetime_to_dbus(dt, variant_level=0):
671
"""Convert a UTC datetime.datetime() to a D-Bus type."""
672
return dbus.String(dt.isoformat(),
673
variant_level=variant_level)
676
oldstate = getattr(self, u"enabled", False)
677
r = Client.enable(self)
678
if oldstate != self.enabled:
680
self.PropertyChanged(dbus.String(u"enabled"),
681
dbus.Boolean(True, variant_level=1))
682
self.PropertyChanged(
683
dbus.String(u"last_enabled"),
684
self._datetime_to_dbus(self.last_enabled,
688
def disable(self, signal = True):
689
oldstate = getattr(self, u"enabled", False)
690
r = Client.disable(self)
691
if signal and oldstate != self.enabled:
693
self.PropertyChanged(dbus.String(u"enabled"),
694
dbus.Boolean(False, variant_level=1))
697
def __del__(self, *args, **kwargs):
699
self.remove_from_connection()
702
if hasattr(DBusObjectWithProperties, u"__del__"):
703
DBusObjectWithProperties.__del__(self, *args, **kwargs)
704
Client.__del__(self, *args, **kwargs)
706
def checker_callback(self, pid, condition, command,
708
self.checker_callback_tag = None
711
self.PropertyChanged(dbus.String(u"checker_running"),
712
dbus.Boolean(False, variant_level=1))
713
if os.WIFEXITED(condition):
714
exitstatus = os.WEXITSTATUS(condition)
716
self.CheckerCompleted(dbus.Int16(exitstatus),
717
dbus.Int64(condition),
718
dbus.String(command))
721
self.CheckerCompleted(dbus.Int16(-1),
722
dbus.Int64(condition),
723
dbus.String(command))
725
return Client.checker_callback(self, pid, condition, command,
728
def checked_ok(self, *args, **kwargs):
729
r = Client.checked_ok(self, *args, **kwargs)
731
self.PropertyChanged(
732
dbus.String(u"last_checked_ok"),
733
(self._datetime_to_dbus(self.last_checked_ok,
737
def start_checker(self, *args, **kwargs):
738
old_checker = self.checker
739
if self.checker is not None:
740
old_checker_pid = self.checker.pid
742
old_checker_pid = None
743
r = Client.start_checker(self, *args, **kwargs)
744
# Only if new checker process was started
745
if (self.checker is not None
746
and old_checker_pid != self.checker.pid):
748
self.CheckerStarted(self.current_checker_command)
749
self.PropertyChanged(
750
dbus.String(u"checker_running"),
751
dbus.Boolean(True, variant_level=1))
754
def stop_checker(self, *args, **kwargs):
755
old_checker = getattr(self, u"checker", None)
756
r = Client.stop_checker(self, *args, **kwargs)
757
if (old_checker is not None
758
and getattr(self, u"checker", None) is None):
759
self.PropertyChanged(dbus.String(u"checker_running"),
760
dbus.Boolean(False, variant_level=1))
763
## D-Bus methods & signals
764
_interface = u"se.bsnet.fukt.Mandos.Client"
767
@dbus.service.method(_interface)
769
return self.checked_ok()
771
# CheckerCompleted - signal
772
@dbus.service.signal(_interface, signature=u"nxs")
773
def CheckerCompleted(self, exitcode, waitstatus, command):
777
# CheckerStarted - signal
778
@dbus.service.signal(_interface, signature=u"s")
779
def CheckerStarted(self, command):
783
# PropertyChanged - signal
784
@dbus.service.signal(_interface, signature=u"sv")
785
def PropertyChanged(self, property, value):
789
# ReceivedSecret - signal
790
@dbus.service.signal(_interface)
791
def ReceivedSecret(self):
796
@dbus.service.signal(_interface)
802
@dbus.service.method(_interface)
807
# StartChecker - method
808
@dbus.service.method(_interface)
809
def StartChecker(self):
814
@dbus.service.method(_interface)
819
# StopChecker - method
820
@dbus.service.method(_interface)
821
def StopChecker(self):
825
@dbus_service_property(_interface, signature=u"s", access=u"read")
826
def name_dbus_property(self):
827
return dbus.String(self.name)
829
# fingerprint - property
830
@dbus_service_property(_interface, signature=u"s", access=u"read")
831
def fingerprint_dbus_property(self):
832
return dbus.String(self.fingerprint)
835
@dbus_service_property(_interface, signature=u"s",
837
def host_dbus_property(self, value=None):
838
if value is None: # get
839
return dbus.String(self.host)
842
self.PropertyChanged(dbus.String(u"host"),
843
dbus.String(value, variant_level=1))
846
@dbus_service_property(_interface, signature=u"s", access=u"read")
847
def created_dbus_property(self):
848
return dbus.String(self._datetime_to_dbus(self.created))
850
# last_enabled - property
851
@dbus_service_property(_interface, signature=u"s", access=u"read")
852
def last_enabled_dbus_property(self):
853
if self.last_enabled is None:
854
return dbus.String(u"")
855
return dbus.String(self._datetime_to_dbus(self.last_enabled))
858
@dbus_service_property(_interface, signature=u"b",
860
def enabled_dbus_property(self, value=None):
861
if value is None: # get
862
return dbus.Boolean(self.enabled)
868
# last_checked_ok - property
869
@dbus_service_property(_interface, signature=u"s",
871
def last_checked_ok_dbus_property(self, value=None):
872
if value is not None:
875
if self.last_checked_ok is None:
876
return dbus.String(u"")
877
return dbus.String(self._datetime_to_dbus(self
881
@dbus_service_property(_interface, signature=u"t",
883
def timeout_dbus_property(self, value=None):
884
if value is None: # get
885
return dbus.UInt64(self.timeout_milliseconds())
886
self.timeout = datetime.timedelta(0, 0, 0, value)
888
self.PropertyChanged(dbus.String(u"timeout"),
889
dbus.UInt64(value, variant_level=1))
890
if getattr(self, u"disable_initiator_tag", None) is None:
893
gobject.source_remove(self.disable_initiator_tag)
894
self.disable_initiator_tag = None
896
_timedelta_to_milliseconds((self
902
# The timeout has passed
905
self.disable_initiator_tag = (gobject.timeout_add
906
(time_to_die, self.disable))
908
# interval - property
909
@dbus_service_property(_interface, signature=u"t",
911
def interval_dbus_property(self, value=None):
912
if value is None: # get
913
return dbus.UInt64(self.interval_milliseconds())
914
self.interval = datetime.timedelta(0, 0, 0, value)
916
self.PropertyChanged(dbus.String(u"interval"),
917
dbus.UInt64(value, variant_level=1))
918
if getattr(self, u"checker_initiator_tag", None) is None:
920
# Reschedule checker run
921
gobject.source_remove(self.checker_initiator_tag)
922
self.checker_initiator_tag = (gobject.timeout_add
923
(value, self.start_checker))
924
self.start_checker() # Start one now, too
927
@dbus_service_property(_interface, signature=u"s",
929
def checker_dbus_property(self, value=None):
930
if value is None: # get
931
return dbus.String(self.checker_command)
932
self.checker_command = value
934
self.PropertyChanged(dbus.String(u"checker"),
935
dbus.String(self.checker_command,
938
# checker_running - property
939
@dbus_service_property(_interface, signature=u"b",
941
def checker_running_dbus_property(self, value=None):
942
if value is None: # get
943
return dbus.Boolean(self.checker is not None)
949
# object_path - property
950
@dbus_service_property(_interface, signature=u"o", access=u"read")
951
def object_path_dbus_property(self):
952
return self.dbus_object_path # is already a dbus.ObjectPath
955
@dbus_service_property(_interface, signature=u"ay",
956
access=u"write", byte_arrays=True)
957
def secret_dbus_property(self, value):
958
self.secret = str(value)
963
class ClientHandler(socketserver.BaseRequestHandler, object):
964
"""A class to handle client connections.
966
Instantiated once for each connection to handle it.
371
def peer_certificate(session):
372
"Return the peer's OpenPGP certificate as a bytestring"
373
# If not an OpenPGP certificate...
374
if gnutls.library.functions.gnutls_certificate_type_get\
375
(session._c_object) \
376
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
377
# ...do the normal thing
378
return session.peer_certificate
379
list_size = ctypes.c_uint()
380
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
381
(session._c_object, ctypes.byref(list_size))
382
if list_size.value == 0:
385
return ctypes.string_at(cert.data, cert.size)
388
def fingerprint(openpgp):
389
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
390
# New GnuTLS "datum" with the OpenPGP public key
391
datum = gnutls.library.types.gnutls_datum_t\
392
(ctypes.cast(ctypes.c_char_p(openpgp),
393
ctypes.POINTER(ctypes.c_ubyte)),
394
ctypes.c_uint(len(openpgp)))
395
# New empty GnuTLS certificate
396
crt = gnutls.library.types.gnutls_openpgp_crt_t()
397
gnutls.library.functions.gnutls_openpgp_crt_init\
399
# Import the OpenPGP public key into the certificate
400
gnutls.library.functions.gnutls_openpgp_crt_import\
401
(crt, ctypes.byref(datum),
402
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
403
# New buffer for the fingerprint
404
buffer = ctypes.create_string_buffer(20)
405
buffer_length = ctypes.c_size_t()
406
# Get the fingerprint from the certificate into the buffer
407
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
408
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
409
# Deinit the certificate
410
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
411
# Convert the buffer to a Python bytestring
412
fpr = ctypes.string_at(buffer, buffer_length.value)
413
# Convert the bytestring to hexadecimal notation
414
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
418
class tcp_handler(SocketServer.BaseRequestHandler, object):
419
"""A TCP request handler class.
420
Instantiated by IPv6_TCPServer for each request to handle it.
967
421
Note: This will run in its own forked process."""
969
423
def handle(self):
970
424
logger.info(u"TCP connection from: %s",
971
unicode(self.client_address))
972
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
973
# Open IPC pipe to parent process
974
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
975
session = (gnutls.connection
976
.ClientSession(self.request,
980
line = self.request.makefile().readline()
981
logger.debug(u"Protocol version: %r", line)
983
if int(line.strip().split()[0]) > 1:
985
except (ValueError, IndexError, RuntimeError), error:
986
logger.error(u"Unknown protocol version: %s", error)
989
# Note: gnutls.connection.X509Credentials is really a
990
# generic GnuTLS certificate credentials object so long as
991
# no X.509 keys are added to it. Therefore, we can use it
992
# here despite using OpenPGP certificates.
994
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
995
# u"+AES-256-CBC", u"+SHA1",
996
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
998
# Use a fallback default, since this MUST be set.
999
priority = self.server.gnutls_priority
1000
if priority is None:
1001
priority = u"NORMAL"
1002
(gnutls.library.functions
1003
.gnutls_priority_set_direct(session._c_object,
1008
except gnutls.errors.GNUTLSError, error:
1009
logger.warning(u"Handshake failed: %s", error)
1010
# Do not run session.bye() here: the session is not
1011
# established. Just abandon the request.
1013
logger.debug(u"Handshake succeeded")
1015
fpr = self.fingerprint(self.peer_certificate(session))
1016
except (TypeError, gnutls.errors.GNUTLSError), error:
1017
logger.warning(u"Bad certificate: %s", error)
1020
logger.debug(u"Fingerprint: %s", fpr)
1022
for c in self.server.clients:
1023
if c.fingerprint == fpr:
1027
ipc.write(u"NOTFOUND %s %s\n"
1028
% (fpr, unicode(self.client_address)))
1031
# Have to check if client.still_valid(), since it is
1032
# possible that the client timed out while establishing
1033
# the GnuTLS session.
1034
if not client.still_valid():
1035
ipc.write(u"INVALID %s\n" % client.name)
1038
ipc.write(u"SENDING %s\n" % client.name)
1040
while sent_size < len(client.secret):
1041
sent = session.send(client.secret[sent_size:])
1042
logger.debug(u"Sent: %d, remaining: %d",
1043
sent, len(client.secret)
1044
- (sent_size + sent))
1049
def peer_certificate(session):
1050
"Return the peer's OpenPGP certificate as a bytestring"
1051
# If not an OpenPGP certificate...
1052
if (gnutls.library.functions
1053
.gnutls_certificate_type_get(session._c_object)
1054
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1055
# ...do the normal thing
1056
return session.peer_certificate
1057
list_size = ctypes.c_uint(1)
1058
cert_list = (gnutls.library.functions
1059
.gnutls_certificate_get_peers
1060
(session._c_object, ctypes.byref(list_size)))
1061
if not bool(cert_list) and list_size.value != 0:
1062
raise gnutls.errors.GNUTLSError(u"error getting peer"
1064
if list_size.value == 0:
1067
return ctypes.string_at(cert.data, cert.size)
1070
def fingerprint(openpgp):
1071
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1072
# New GnuTLS "datum" with the OpenPGP public key
1073
datum = (gnutls.library.types
1074
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1077
ctypes.c_uint(len(openpgp))))
1078
# New empty GnuTLS certificate
1079
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1080
(gnutls.library.functions
1081
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1082
# Import the OpenPGP public key into the certificate
1083
(gnutls.library.functions
1084
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1085
gnutls.library.constants
1086
.GNUTLS_OPENPGP_FMT_RAW))
1087
# Verify the self signature in the key
1088
crtverify = ctypes.c_uint()
1089
(gnutls.library.functions
1090
.gnutls_openpgp_crt_verify_self(crt, 0,
1091
ctypes.byref(crtverify)))
1092
if crtverify.value != 0:
1093
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1094
raise (gnutls.errors.CertificateSecurityError
1096
# New buffer for the fingerprint
1097
buf = ctypes.create_string_buffer(20)
1098
buf_len = ctypes.c_size_t()
1099
# Get the fingerprint from the certificate into the buffer
1100
(gnutls.library.functions
1101
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1102
ctypes.byref(buf_len)))
1103
# Deinit the certificate
1104
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1105
# Convert the buffer to a Python bytestring
1106
fpr = ctypes.string_at(buf, buf_len.value)
1107
# Convert the bytestring to hexadecimal notation
1108
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1112
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1113
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
1114
def process_request(self, request, client_address):
1115
"""Overrides and wraps the original process_request().
1117
This function creates a new pipe in self.pipe
1119
self.pipe = os.pipe()
1120
super(ForkingMixInWithPipe,
1121
self).process_request(request, client_address)
1122
os.close(self.pipe[1]) # close write end
1123
self.add_pipe(self.pipe[0])
1124
def add_pipe(self, pipe):
1125
"""Dummy function; override as necessary"""
1129
class IPv6_TCPServer(ForkingMixInWithPipe,
1130
socketserver.TCPServer, object):
1131
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
425
unicode(self.client_address))
426
session = gnutls.connection.ClientSession\
427
(self.request, gnutls.connection.X509Credentials())
429
line = self.request.makefile().readline()
430
logger.debug(u"Protocol version: %r", line)
432
if int(line.strip().split()[0]) > 1:
434
except (ValueError, IndexError, RuntimeError), error:
435
logger.error(u"Unknown protocol version: %s", error)
438
# Note: gnutls.connection.X509Credentials is really a generic
439
# GnuTLS certificate credentials object so long as no X.509
440
# keys are added to it. Therefore, we can use it here despite
441
# using OpenPGP certificates.
443
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
444
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
446
priority = "NORMAL" # Fallback default, since this
448
if self.server.settings["priority"]:
449
priority = self.server.settings["priority"]
450
gnutls.library.functions.gnutls_priority_set_direct\
451
(session._c_object, priority, None);
455
except gnutls.errors.GNUTLSError, error:
456
logger.warning(u"Handshake failed: %s", error)
457
# Do not run session.bye() here: the session is not
458
# established. Just abandon the request.
461
fpr = fingerprint(peer_certificate(session))
462
except (TypeError, gnutls.errors.GNUTLSError), error:
463
logger.warning(u"Bad certificate: %s", error)
466
logger.debug(u"Fingerprint: %s", fpr)
468
for c in self.server.clients:
469
if c.fingerprint == fpr:
473
logger.warning(u"Client not found for fingerprint: %s",
477
# Have to check if client.still_valid(), since it is possible
478
# that the client timed out while establishing the GnuTLS
480
if not client.still_valid():
481
logger.warning(u"Client %(name)s is invalid",
486
while sent_size < len(client.secret):
487
sent = session.send(client.secret[sent_size:])
488
logger.debug(u"Sent: %d, remaining: %d",
489
sent, len(client.secret)
490
- (sent_size + sent))
495
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
496
"""IPv6 TCP server. Accepts 'None' as address and/or port.
1134
enabled: Boolean; whether this server is activated yet
1135
interface: None or a network interface name (string)
1136
use_ipv6: Boolean; to use IPv6 or not
498
settings: Server settings
499
clients: Set() of Client objects
1138
def __init__(self, server_address, RequestHandlerClass,
1139
interface=None, use_ipv6=True):
1140
self.interface = interface
1142
self.address_family = socket.AF_INET6
1143
socketserver.TCPServer.__init__(self, server_address,
1144
RequestHandlerClass)
501
address_family = socket.AF_INET6
502
def __init__(self, *args, **kwargs):
503
if "settings" in kwargs:
504
self.settings = kwargs["settings"]
505
del kwargs["settings"]
506
if "clients" in kwargs:
507
self.clients = kwargs["clients"]
508
del kwargs["clients"]
509
return super(type(self), self).__init__(*args, **kwargs)
1145
510
def server_bind(self):
1146
511
"""This overrides the normal server_bind() function
1147
512
to bind to an interface if one was specified, and also NOT to
1148
513
bind to an address or port if they were not specified."""
1149
if self.interface is not None:
1150
if SO_BINDTODEVICE is None:
1151
logger.error(u"SO_BINDTODEVICE does not exist;"
1152
u" cannot bind to interface %s",
1156
self.socket.setsockopt(socket.SOL_SOCKET,
1160
except socket.error, error:
1161
if error[0] == errno.EPERM:
1162
logger.error(u"No permission to"
1163
u" bind to interface %s",
1165
elif error[0] == errno.ENOPROTOOPT:
1166
logger.error(u"SO_BINDTODEVICE not available;"
1167
u" cannot bind to interface %s",
514
if self.settings["interface"]:
515
# 25 is from /usr/include/asm-i486/socket.h
516
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
518
self.socket.setsockopt(socket.SOL_SOCKET,
520
self.settings["interface"])
521
except socket.error, error:
522
if error[0] == errno.EPERM:
523
logger.error(u"No permission to"
524
u" bind to interface %s",
525
self.settings["interface"])
1171
528
# Only bind(2) the socket if we really need to.
1172
529
if self.server_address[0] or self.server_address[1]:
1173
530
if not self.server_address[0]:
1174
if self.address_family == socket.AF_INET6:
1175
any_address = u"::" # in6addr_any
1177
any_address = socket.INADDR_ANY
1178
self.server_address = (any_address,
532
self.server_address = (in6addr_any,
1179
533
self.server_address[1])
1180
534
elif not self.server_address[1]:
1181
535
self.server_address = (self.server_address[0],
1183
# if self.interface:
537
# if self.settings["interface"]:
1184
538
# self.server_address = (self.server_address[0],
1187
541
# if_nametoindex
1189
return socketserver.TCPServer.server_bind(self)
1192
class MandosServer(IPv6_TCPServer):
1196
clients: set of Client objects
1197
gnutls_priority GnuTLS priority string
1198
use_dbus: Boolean; to emit D-Bus signals or not
1200
Assumes a gobject.MainLoop event loop.
1202
def __init__(self, server_address, RequestHandlerClass,
1203
interface=None, use_ipv6=True, clients=None,
1204
gnutls_priority=None, use_dbus=True):
1205
self.enabled = False
1206
self.clients = clients
1207
if self.clients is None:
1208
self.clients = set()
1209
self.use_dbus = use_dbus
1210
self.gnutls_priority = gnutls_priority
1211
IPv6_TCPServer.__init__(self, server_address,
1212
RequestHandlerClass,
1213
interface = interface,
1214
use_ipv6 = use_ipv6)
1215
def server_activate(self):
1217
return socketserver.TCPServer.server_activate(self)
1220
def add_pipe(self, pipe):
1221
# Call "handle_ipc" for both data and EOF events
1222
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1224
def handle_ipc(self, source, condition, file_objects={}):
1226
gobject.IO_IN: u"IN", # There is data to read.
1227
gobject.IO_OUT: u"OUT", # Data can be written (without
1229
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1230
gobject.IO_ERR: u"ERR", # Error condition.
1231
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1232
# broken, usually for pipes and
1235
conditions_string = ' | '.join(name
1237
condition_names.iteritems()
1238
if cond & condition)
1239
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1242
# Turn the pipe file descriptor into a Python file object
1243
if source not in file_objects:
1244
file_objects[source] = os.fdopen(source, u"r", 1)
1246
# Read a line from the file object
1247
cmdline = file_objects[source].readline()
1248
if not cmdline: # Empty line means end of file
1249
# close the IPC pipe
1250
file_objects[source].close()
1251
del file_objects[source]
1253
# Stop calling this function
1256
logger.debug(u"IPC command: %r", cmdline)
1258
# Parse and act on command
1259
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1261
if cmd == u"NOTFOUND":
1262
logger.warning(u"Client not found for fingerprint: %s",
1266
mandos_dbus_service.ClientNotFound(args)
1267
elif cmd == u"INVALID":
1268
for client in self.clients:
1269
if client.name == args:
1270
logger.warning(u"Client %s is invalid", args)
1276
logger.error(u"Unknown client %s is invalid", args)
1277
elif cmd == u"SENDING":
1278
for client in self.clients:
1279
if client.name == args:
1280
logger.info(u"Sending secret to %s", client.name)
1284
client.ReceivedSecret()
1287
logger.error(u"Sending secret to unknown client %s",
1290
logger.error(u"Unknown IPC command: %r", cmdline)
1292
# Keep calling this function
544
return super(type(self), self).server_bind()
1296
547
def string_to_delta(interval):
1297
548
"""Parse a string and return a datetime.timedelta
1299
>>> string_to_delta(u'7d')
550
>>> string_to_delta('7d')
1300
551
datetime.timedelta(7)
1301
>>> string_to_delta(u'60s')
552
>>> string_to_delta('60s')
1302
553
datetime.timedelta(0, 60)
1303
>>> string_to_delta(u'60m')
554
>>> string_to_delta('60m')
1304
555
datetime.timedelta(0, 3600)
1305
>>> string_to_delta(u'24h')
556
>>> string_to_delta('24h')
1306
557
datetime.timedelta(1)
1307
558
>>> string_to_delta(u'1w')
1308
559
datetime.timedelta(7)
1309
>>> string_to_delta(u'5m 30s')
1310
datetime.timedelta(0, 330)
1312
timevalue = datetime.timedelta(0)
1313
for s in interval.split():
1315
suffix = unicode(s[-1])
1318
delta = datetime.timedelta(value)
1319
elif suffix == u"s":
1320
delta = datetime.timedelta(0, value)
1321
elif suffix == u"m":
1322
delta = datetime.timedelta(0, 0, 0, 0, value)
1323
elif suffix == u"h":
1324
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
1325
elif suffix == u"w":
1326
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1329
except (ValueError, IndexError):
562
suffix=unicode(interval[-1])
563
value=int(interval[:-1])
565
delta = datetime.timedelta(value)
567
delta = datetime.timedelta(0, value)
569
delta = datetime.timedelta(0, 0, 0, 0, value)
571
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
573
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1330
575
raise ValueError
576
except (ValueError, IndexError):
581
def server_state_changed(state):
582
"""Derived from the Avahi example code"""
583
if state == avahi.SERVER_COLLISION:
584
logger.error(u"Server name collision")
586
elif state == avahi.SERVER_RUNNING:
590
def entry_group_state_changed(state, error):
591
"""Derived from the Avahi example code"""
592
logger.debug(u"state change: %i", state)
594
if state == avahi.ENTRY_GROUP_ESTABLISHED:
595
logger.debug(u"Service established.")
596
elif state == avahi.ENTRY_GROUP_COLLISION:
597
logger.warning(u"Service name collision.")
599
elif state == avahi.ENTRY_GROUP_FAILURE:
600
logger.critical(u"Error in group state changed %s",
602
raise AvahiGroupError("State changed: %s", str(error))
1335
604
def if_nametoindex(interface):
1336
"""Call the C function if_nametoindex(), or equivalent
1338
Note: This function cannot accept a unicode string."""
605
"""Call the C function if_nametoindex(), or equivalent"""
1339
606
global if_nametoindex
1341
if_nametoindex = (ctypes.cdll.LoadLibrary
1342
(ctypes.util.find_library(u"c"))
608
if "ctypes.util" not in sys.modules:
610
if_nametoindex = ctypes.cdll.LoadLibrary\
611
(ctypes.util.find_library("c")).if_nametoindex
1344
612
except (OSError, AttributeError):
1345
logger.warning(u"Doing if_nametoindex the hard way")
613
if "struct" not in sys.modules:
615
if "fcntl" not in sys.modules:
1346
617
def if_nametoindex(interface):
1347
618
"Get an interface index the hard way, i.e. using fcntl()"
1348
619
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1349
with closing(socket.socket()) as s:
1350
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1351
struct.pack(str(u"16s16x"),
1353
interface_index = struct.unpack(str(u"I"),
621
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
622
struct.pack("16s16x", interface))
624
interface_index = struct.unpack("I", ifreq[16:20])[0]
1355
625
return interface_index
1356
626
return if_nametoindex(interface)
1359
629
def daemon(nochdir = False, noclose = False):
1360
630
"""See daemon(3). Standard BSD Unix function.
1362
631
This should really exist as os.daemon, but it doesn't (yet)."""
1420
683
# Default values for config file for server-global settings
1421
server_defaults = { u"interface": u"",
1426
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1427
u"servicename": u"Mandos",
1428
u"use_dbus": u"True",
1429
u"use_ipv6": u"True",
684
server_defaults = { "interface": "",
689
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
690
"servicename": "Mandos",
1432
693
# Parse config file for server-global settings
1433
server_config = configparser.SafeConfigParser(server_defaults)
694
server_config = ConfigParser.SafeConfigParser(server_defaults)
1434
695
del server_defaults
1435
server_config.read(os.path.join(options.configdir,
696
server_config.read(os.path.join(options.configdir, "mandos.conf"))
697
server_section = "server"
1437
698
# Convert the SafeConfigParser object to a dict
1438
server_settings = server_config.defaults()
1439
# Use the appropriate methods on the non-string config options
1440
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1441
server_settings[option] = server_config.getboolean(u"DEFAULT",
1443
if server_settings["port"]:
1444
server_settings["port"] = server_config.getint(u"DEFAULT",
699
server_settings = dict(server_config.items(server_section))
700
# Use getboolean on the boolean config option
701
server_settings["debug"] = server_config.getboolean\
702
(server_section, "debug")
1446
703
del server_config
1448
705
# Override the settings from the config file with command line
1449
706
# options, if set.
1450
for option in (u"interface", u"address", u"port", u"debug",
1451
u"priority", u"servicename", u"configdir",
1452
u"use_dbus", u"use_ipv6"):
707
for option in ("interface", "address", "port", "debug",
708
"priority", "servicename", "configdir"):
1453
709
value = getattr(options, option)
1454
710
if value is not None:
1455
711
server_settings[option] = value
1457
# Force all strings to be unicode
1458
for option in server_settings.keys():
1459
if type(server_settings[option]) is str:
1460
server_settings[option] = unicode(server_settings[option])
1461
713
# Now we have our good server settings in "server_settings"
1463
##################################################################
1466
debug = server_settings[u"debug"]
1467
use_dbus = server_settings[u"use_dbus"]
1468
use_ipv6 = server_settings[u"use_ipv6"]
715
debug = server_settings["debug"]
1471
718
syslogger.setLevel(logging.WARNING)
1472
console.setLevel(logging.WARNING)
1474
if server_settings[u"servicename"] != u"Mandos":
1475
syslogger.setFormatter(logging.Formatter
1476
(u'Mandos (%s) [%%(process)d]:'
1477
u' %%(levelname)s: %%(message)s'
1478
% server_settings[u"servicename"]))
720
if server_settings["servicename"] != "Mandos":
721
syslogger.setFormatter(logging.Formatter\
722
('Mandos (%s): %%(levelname)s:'
724
% server_settings["servicename"]))
1480
726
# Parse config file with clients
1481
client_defaults = { u"timeout": u"1h",
1483
u"checker": u"fping -q -- %%(host)s",
727
client_defaults = { "timeout": "1h",
729
"checker": "fping -q -- %%(host)s",
1486
client_config = configparser.SafeConfigParser(client_defaults)
1487
client_config.read(os.path.join(server_settings[u"configdir"],
1490
global mandos_dbus_service
1491
mandos_dbus_service = None
1493
tcp_server = MandosServer((server_settings[u"address"],
1494
server_settings[u"port"]),
1496
interface=server_settings[u"interface"],
1499
server_settings[u"priority"],
1501
pidfilename = u"/var/run/mandos.pid"
1503
pidfile = open(pidfilename, u"w")
1505
logger.error(u"Could not open file %r", pidfilename)
1508
uid = pwd.getpwnam(u"_mandos").pw_uid
1509
gid = pwd.getpwnam(u"_mandos").pw_gid
1512
uid = pwd.getpwnam(u"mandos").pw_uid
1513
gid = pwd.getpwnam(u"mandos").pw_gid
1516
uid = pwd.getpwnam(u"nobody").pw_uid
1517
gid = pwd.getpwnam(u"nobody").pw_gid
1524
except OSError, error:
1525
if error[0] != errno.EPERM:
1528
# Enable all possible GnuTLS debugging
1530
# "Use a log level over 10 to enable all debugging options."
1532
gnutls.library.functions.gnutls_global_set_log_level(11)
1534
@gnutls.library.types.gnutls_log_func
1535
def debug_gnutls(level, string):
1536
logger.debug(u"GnuTLS: %s", string[:-1])
1538
(gnutls.library.functions
1539
.gnutls_global_set_log_function(debug_gnutls))
731
client_config = ConfigParser.SafeConfigParser(client_defaults)
732
client_config.read(os.path.join(server_settings["configdir"],
736
service = AvahiService(name = server_settings["servicename"],
737
type = "_mandos._tcp", );
738
if server_settings["interface"]:
739
service.interface = if_nametoindex(server_settings["interface"])
1541
741
global main_loop
1542
744
# From the Avahi example code
1543
745
DBusGMainLoop(set_as_default=True )
1544
746
main_loop = gobject.MainLoop()
1545
747
bus = dbus.SystemBus()
748
server = dbus.Interface(
749
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
750
avahi.DBUS_INTERFACE_SERVER )
1546
751
# End of Avahi example code
1548
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1549
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1550
service = AvahiService(name = server_settings[u"servicename"],
1551
servicetype = u"_mandos._tcp",
1552
protocol = protocol, bus = bus)
1553
if server_settings["interface"]:
1554
service.interface = (if_nametoindex
1555
(str(server_settings[u"interface"])))
1557
client_class = Client
1559
client_class = functools.partial(ClientDBus, bus = bus)
1560
tcp_server.clients.update(set(
1561
client_class(name = section,
1562
config= dict(client_config.items(section)))
1563
for section in client_config.sections()))
1564
if not tcp_server.clients:
1565
logger.warning(u"No clients defined")
1568
# Redirect stdin so all checkers get /dev/null
1569
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1570
os.dup2(null, sys.stdin.fileno())
1574
# No console logging
1575
logger.removeHandler(console)
1576
# Close all input and output, do double fork, etc.
754
console = logging.StreamHandler()
755
# console.setLevel(logging.DEBUG)
756
console.setFormatter(logging.Formatter\
757
('%(levelname)s: %(message)s'))
758
logger.addHandler(console)
762
def remove_from_clients(client):
763
clients.remove(client)
765
logger.critical(u"No clients left, exiting")
768
clients.update(Set(Client(name = section,
769
stop_hook = remove_from_clients,
771
= dict(client_config.items(section)))
772
for section in client_config.sections()))
774
logger.critical(u"No clients defined")
780
pidfilename = "/var/run/mandos/mandos.pid"
1580
with closing(pidfile):
1582
pidfile.write(str(pid) + "\n")
783
pidfile = open(pidfilename, "w")
784
pidfile.write(str(pid) + "\n")
1585
logger.error(u"Could not write to file %r with PID %d",
1588
# "pidfile" was never created
788
logger.error(u"Could not write %s file with PID %d",
789
pidfilename, os.getpid())
1593
792
"Cleanup function; run on exit"
794
# From the Avahi example code
795
if not group is None:
798
# End of Avahi example code
1596
while tcp_server.clients:
1597
client = tcp_server.clients.pop()
1598
client.disable_hook = None
801
client = clients.pop()
802
client.stop_hook = None
1601
805
atexit.register(cleanup)