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