151
145
self.group = None # our entry group
152
146
self.server = None
154
self.entry_group_state_changed_match = None
155
148
def rename(self):
156
149
"""Derived from the Avahi example code"""
157
150
if self.rename_count >= self.max_renames:
158
logger.critical("No suitable Zeroconf service name found"
159
" after %i retries, exiting.",
151
logger.critical(u"No suitable Zeroconf service name found"
152
u" after %i retries, exiting.",
160
153
self.rename_count)
161
raise AvahiServiceError("Too many renames")
162
self.name = unicode(self.server.GetAlternativeServiceName(self.name))
163
logger.info("Changing Zeroconf service name to %r ...",
154
raise AvahiServiceError(u"Too many renames")
155
self.name = self.server.GetAlternativeServiceName(self.name)
156
logger.info(u"Changing Zeroconf service name to %r ...",
165
158
syslogger.setFormatter(logging.Formatter
166
('Mandos (%s) [%%(process)d]:'
167
' %%(levelname)s: %%(message)s'
159
(u'Mandos (%s) [%%(process)d]:'
160
u' %%(levelname)s: %%(message)s'
172
except dbus.exceptions.DBusException as error:
173
logger.critical("DBusException: %s", error)
176
164
self.rename_count += 1
177
165
def remove(self):
178
166
"""Derived from the Avahi example code"""
179
if self.entry_group_state_changed_match is not None:
180
self.entry_group_state_changed_match.remove()
181
self.entry_group_state_changed_match = None
182
167
if self.group is not None:
183
168
self.group.Reset()
185
170
"""Derived from the Avahi example code"""
187
171
if self.group is None:
188
172
self.group = dbus.Interface(
189
173
self.bus.get_object(avahi.DBUS_NAME,
190
174
self.server.EntryGroupNew()),
191
175
avahi.DBUS_INTERFACE_ENTRY_GROUP)
192
self.entry_group_state_changed_match = (
193
self.group.connect_to_signal(
194
'StateChanged', self .entry_group_state_changed))
195
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
176
self.group.connect_to_signal('StateChanged',
177
self.entry_group_state_changed)
178
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
196
179
self.name, self.type)
197
180
self.group.AddService(
205
188
self.group.Commit()
206
189
def entry_group_state_changed(self, state, error):
207
190
"""Derived from the Avahi example code"""
208
logger.debug("Avahi entry group state change: %i", state)
191
logger.debug(u"Avahi state change: %i", state)
210
193
if state == avahi.ENTRY_GROUP_ESTABLISHED:
211
logger.debug("Zeroconf service established.")
194
logger.debug(u"Zeroconf service established.")
212
195
elif state == avahi.ENTRY_GROUP_COLLISION:
213
logger.info("Zeroconf service name collision.")
196
logger.warning(u"Zeroconf service name collision.")
215
198
elif state == avahi.ENTRY_GROUP_FAILURE:
216
logger.critical("Avahi: Error in group state changed %s",
199
logger.critical(u"Avahi: Error in group state changed %s",
218
raise AvahiGroupError("State changed: %s"
201
raise AvahiGroupError(u"State changed: %s"
219
202
% unicode(error))
220
203
def cleanup(self):
221
204
"""Derived from the Avahi example code"""
222
205
if self.group is not None:
225
except (dbus.exceptions.UnknownMethodException,
226
dbus.exceptions.DBusException) as e:
228
207
self.group = None
230
def server_state_changed(self, state, error=None):
208
def server_state_changed(self, state):
231
209
"""Derived from the Avahi example code"""
232
logger.debug("Avahi server state change: %i", state)
233
bad_states = { avahi.SERVER_INVALID:
234
"Zeroconf server invalid",
235
avahi.SERVER_REGISTERING: None,
236
avahi.SERVER_COLLISION:
237
"Zeroconf server name collision",
238
avahi.SERVER_FAILURE:
239
"Zeroconf server failure" }
240
if state in bad_states:
241
if bad_states[state] is not None:
243
logger.error(bad_states[state])
245
logger.error(bad_states[state] + ": %r", error)
210
if state == avahi.SERVER_COLLISION:
211
logger.error(u"Zeroconf server name collision")
247
213
elif state == avahi.SERVER_RUNNING:
251
logger.debug("Unknown state: %r", state)
253
logger.debug("Unknown state: %r: %r", state, error)
254
215
def activate(self):
255
216
"""Derived from the Avahi example code"""
256
217
if self.server is None:
257
218
self.server = dbus.Interface(
258
219
self.bus.get_object(avahi.DBUS_NAME,
259
avahi.DBUS_PATH_SERVER,
260
follow_name_owner_changes=True),
220
avahi.DBUS_PATH_SERVER),
261
221
avahi.DBUS_INTERFACE_SERVER)
262
self.server.connect_to_signal("StateChanged",
222
self.server.connect_to_signal(u"StateChanged",
263
223
self.server_state_changed)
264
224
self.server_state_changed(self.server.GetState())
268
228
"""A representation of a client host served by this server.
271
_approved: bool(); 'None' if not yet approved/disapproved
272
approval_delay: datetime.timedelta(); Time to wait for approval
273
approval_duration: datetime.timedelta(); Duration of one approval
231
name: string; from the config file, used in log messages and
233
fingerprint: string (40 or 32 hexadecimal digits); used to
234
uniquely identify the client
235
secret: bytestring; sent verbatim (over TLS) to client
236
host: string; available for use by the checker command
237
created: datetime.datetime(); (UTC) object creation
238
last_enabled: datetime.datetime(); (UTC)
240
last_checked_ok: datetime.datetime(); (UTC) or None
241
timeout: datetime.timedelta(); How long from last_checked_ok
242
until this client is invalid
243
interval: datetime.timedelta(); How often to start a new checker
244
disable_hook: If set, called by disable() as disable_hook(self)
274
245
checker: subprocess.Popen(); a running checker process used
275
246
to see if the client lives.
276
247
'None' if no process is running.
277
checker_callback_tag: a gobject event source tag, or None
278
checker_command: string; External command which is run to check
279
if client lives. %() expansions are done at
248
checker_initiator_tag: a gobject event source tag, or None
249
disable_initiator_tag: - '' -
250
checker_callback_tag: - '' -
251
checker_command: string; External command which is run to check if
252
client lives. %() expansions are done at
280
253
runtime with vars(self) as dict, so that for
281
254
instance %(name)s can be used in the command.
282
checker_initiator_tag: a gobject event source tag, or None
283
created: datetime.datetime(); (UTC) object creation
284
255
current_checker_command: string; current running checker_command
285
disable_hook: If set, called by disable() as disable_hook(self)
286
disable_initiator_tag: a gobject event source tag, or None
288
fingerprint: string (40 or 32 hexadecimal digits); used to
289
uniquely identify the client
290
host: string; available for use by the checker command
291
interval: datetime.timedelta(); How often to start a new checker
292
last_approval_request: datetime.datetime(); (UTC) or None
293
last_checked_ok: datetime.datetime(); (UTC) or None
294
last_enabled: datetime.datetime(); (UTC)
295
name: string; from the config file, used in log messages and
297
secret: bytestring; sent verbatim (over TLS) to client
298
timeout: datetime.timedelta(); How long from last_checked_ok
299
until this client is disabled
300
runtime_expansions: Allowed attributes for runtime expansion.
303
runtime_expansions = ("approval_delay", "approval_duration",
304
"created", "enabled", "fingerprint",
305
"host", "interval", "last_checked_ok",
306
"last_enabled", "name", "timeout")
309
def _timedelta_to_milliseconds(td):
310
"Convert a datetime.timedelta() to milliseconds"
311
return ((td.days * 24 * 60 * 60 * 1000)
312
+ (td.seconds * 1000)
313
+ (td.microseconds // 1000))
259
def _datetime_to_milliseconds(dt):
260
"Convert a datetime.datetime() to milliseconds"
261
return ((dt.days * 24 * 60 * 60 * 1000)
262
+ (dt.seconds * 1000)
263
+ (dt.microseconds // 1000))
315
265
def timeout_milliseconds(self):
316
266
"Return the 'timeout' attribute in milliseconds"
317
return self._timedelta_to_milliseconds(self.timeout)
267
return self._datetime_to_milliseconds(self.timeout)
319
269
def interval_milliseconds(self):
320
270
"Return the 'interval' attribute in milliseconds"
321
return self._timedelta_to_milliseconds(self.interval)
323
def approval_delay_milliseconds(self):
324
return self._timedelta_to_milliseconds(self.approval_delay)
271
return self._datetime_to_milliseconds(self.interval)
326
273
def __init__(self, name = None, disable_hook=None, config=None):
327
274
"""Note: the 'checker' key in 'config' sets the
331
278
if config is None:
333
logger.debug("Creating client %r", self.name)
280
logger.debug(u"Creating client %r", self.name)
334
281
# Uppercase and remove spaces from fingerprint for later
335
282
# comparison purposes with return value from the fingerprint()
337
self.fingerprint = (config["fingerprint"].upper()
339
logger.debug(" Fingerprint: %s", self.fingerprint)
340
if "secret" in config:
341
self.secret = config["secret"].decode("base64")
342
elif "secfile" in config:
343
with open(os.path.expanduser(os.path.expandvars
344
(config["secfile"])),
284
self.fingerprint = (config[u"fingerprint"].upper()
286
logger.debug(u" Fingerprint: %s", self.fingerprint)
287
if u"secret" in config:
288
self.secret = config[u"secret"].decode(u"base64")
289
elif u"secfile" in config:
290
with closing(open(os.path.expanduser
292
(config[u"secfile"])))) as secfile:
346
293
self.secret = secfile.read()
348
raise TypeError("No secret or secfile for client %s"
295
raise TypeError(u"No secret or secfile for client %s"
350
self.host = config.get("host", "")
297
self.host = config.get(u"host", u"")
351
298
self.created = datetime.datetime.utcnow()
352
299
self.enabled = False
353
self.last_approval_request = None
354
300
self.last_enabled = None
355
301
self.last_checked_ok = None
356
self.timeout = string_to_delta(config["timeout"])
357
self.interval = string_to_delta(config["interval"])
302
self.timeout = string_to_delta(config[u"timeout"])
303
self.interval = string_to_delta(config[u"interval"])
358
304
self.disable_hook = disable_hook
359
305
self.checker = None
360
306
self.checker_initiator_tag = None
361
307
self.disable_initiator_tag = None
362
308
self.checker_callback_tag = None
363
self.checker_command = config["checker"]
309
self.checker_command = config[u"checker"]
364
310
self.current_checker_command = None
365
311
self.last_connect = None
366
self._approved = None
367
self.approved_by_default = config.get("approved_by_default",
369
self.approvals_pending = 0
370
self.approval_delay = string_to_delta(
371
config["approval_delay"])
372
self.approval_duration = string_to_delta(
373
config["approval_duration"])
374
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
376
def send_changedstate(self):
377
self.changedstate.acquire()
378
self.changedstate.notify_all()
379
self.changedstate.release()
381
313
def enable(self):
382
314
"""Start this client's checker and timeout hooks"""
383
if getattr(self, "enabled", False):
386
self.send_changedstate()
387
315
self.last_enabled = datetime.datetime.utcnow()
388
316
# Schedule a new checker to be started an 'interval' from now,
389
317
# and every interval from then on.
390
318
self.checker_initiator_tag = (gobject.timeout_add
391
319
(self.interval_milliseconds(),
392
320
self.start_checker))
321
# Also start a new checker *right now*.
393
323
# Schedule a disable() when 'timeout' has passed
394
324
self.disable_initiator_tag = (gobject.timeout_add
395
325
(self.timeout_milliseconds(),
397
327
self.enabled = True
398
# Also start a new checker *right now*.
401
def disable(self, quiet=True):
402
330
"""Disable this client."""
403
331
if not getattr(self, "enabled", False):
406
self.send_changedstate()
408
logger.info("Disabling client %s", self.name)
409
if getattr(self, "disable_initiator_tag", False):
333
logger.info(u"Disabling client %s", self.name)
334
if getattr(self, u"disable_initiator_tag", False):
410
335
gobject.source_remove(self.disable_initiator_tag)
411
336
self.disable_initiator_tag = None
412
if getattr(self, "checker_initiator_tag", False):
337
if getattr(self, u"checker_initiator_tag", False):
413
338
gobject.source_remove(self.checker_initiator_tag)
414
339
self.checker_initiator_tag = None
415
340
self.stop_checker()
535
450
if self.checker_callback_tag:
536
451
gobject.source_remove(self.checker_callback_tag)
537
452
self.checker_callback_tag = None
538
if getattr(self, "checker", None) is None:
453
if getattr(self, u"checker", None) is None:
540
logger.debug("Stopping checker for %(name)s", vars(self))
455
logger.debug(u"Stopping checker for %(name)s", vars(self))
542
457
os.kill(self.checker.pid, signal.SIGTERM)
544
459
#if self.checker.poll() is None:
545
460
# os.kill(self.checker.pid, signal.SIGKILL)
546
except OSError as error:
461
except OSError, error:
547
462
if error.errno != errno.ESRCH: # No such process
549
464
self.checker = None
551
def dbus_service_property(dbus_interface, signature="v",
552
access="readwrite", byte_arrays=False):
553
"""Decorators for marking methods of a DBusObjectWithProperties to
554
become properties on the D-Bus.
556
The decorated method will be called with no arguments by "Get"
557
and with one argument by "Set".
559
The parameters, where they are supported, are the same as
560
dbus.service.method, except there is only "signature", since the
561
type from Get() and the type sent to Set() is the same.
563
# Encoding deeply encoded byte arrays is not supported yet by the
564
# "Set" method, so we fail early here:
565
if byte_arrays and signature != "ay":
566
raise ValueError("Byte arrays not supported for non-'ay'"
567
" signature %r" % signature)
569
func._dbus_is_property = True
570
func._dbus_interface = dbus_interface
571
func._dbus_signature = signature
572
func._dbus_access = access
573
func._dbus_name = func.__name__
574
if func._dbus_name.endswith("_dbus_property"):
575
func._dbus_name = func._dbus_name[:-14]
576
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
581
class DBusPropertyException(dbus.exceptions.DBusException):
582
"""A base class for D-Bus property-related exceptions
584
def __unicode__(self):
585
return unicode(str(self))
588
class DBusPropertyAccessException(DBusPropertyException):
589
"""A property's access permissions disallows an operation.
594
class DBusPropertyNotFound(DBusPropertyException):
595
"""An attempt was made to access a non-existing property.
600
class DBusObjectWithProperties(dbus.service.Object):
601
"""A D-Bus object with properties.
603
Classes inheriting from this can use the dbus_service_property
604
decorator to expose methods as D-Bus properties. It exposes the
605
standard Get(), Set(), and GetAll() methods on the D-Bus.
609
def _is_dbus_property(obj):
610
return getattr(obj, "_dbus_is_property", False)
612
def _get_all_dbus_properties(self):
613
"""Returns a generator of (name, attribute) pairs
615
return ((prop._dbus_name, prop)
617
inspect.getmembers(self, self._is_dbus_property))
619
def _get_dbus_property(self, interface_name, property_name):
620
"""Returns a bound method if one exists which is a D-Bus
621
property with the specified name and interface.
623
for name in (property_name,
624
property_name + "_dbus_property"):
625
prop = getattr(self, name, None)
627
or not self._is_dbus_property(prop)
628
or prop._dbus_name != property_name
629
or (interface_name and prop._dbus_interface
630
and interface_name != prop._dbus_interface)):
634
raise DBusPropertyNotFound(self.dbus_object_path + ":"
635
+ interface_name + "."
638
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
640
def Get(self, interface_name, property_name):
641
"""Standard D-Bus property Get() method, see D-Bus standard.
643
prop = self._get_dbus_property(interface_name, property_name)
644
if prop._dbus_access == "write":
645
raise DBusPropertyAccessException(property_name)
647
if not hasattr(value, "variant_level"):
649
return type(value)(value, variant_level=value.variant_level+1)
651
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
652
def Set(self, interface_name, property_name, value):
653
"""Standard D-Bus property Set() method, see D-Bus standard.
655
prop = self._get_dbus_property(interface_name, property_name)
656
if prop._dbus_access == "read":
657
raise DBusPropertyAccessException(property_name)
658
if prop._dbus_get_args_options["byte_arrays"]:
659
# The byte_arrays option is not supported yet on
660
# signatures other than "ay".
661
if prop._dbus_signature != "ay":
663
value = dbus.ByteArray(''.join(unichr(byte)
667
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
668
out_signature="a{sv}")
669
def GetAll(self, interface_name):
670
"""Standard D-Bus property GetAll() method, see D-Bus
673
Note: Will not include properties with access="write".
676
for name, prop in self._get_all_dbus_properties():
678
and interface_name != prop._dbus_interface):
679
# Interface non-empty but did not match
681
# Ignore write-only properties
682
if prop._dbus_access == "write":
685
if not hasattr(value, "variant_level"):
688
all[name] = type(value)(value, variant_level=
689
value.variant_level+1)
690
return dbus.Dictionary(all, signature="sv")
692
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
694
path_keyword='object_path',
695
connection_keyword='connection')
696
def Introspect(self, object_path, connection):
697
"""Standard D-Bus method, overloaded to insert property tags.
699
xmlstring = dbus.service.Object.Introspect(self, object_path,
702
document = xml.dom.minidom.parseString(xmlstring)
703
def make_tag(document, name, prop):
704
e = document.createElement("property")
705
e.setAttribute("name", name)
706
e.setAttribute("type", prop._dbus_signature)
707
e.setAttribute("access", prop._dbus_access)
709
for if_tag in document.getElementsByTagName("interface"):
710
for tag in (make_tag(document, name, prop)
712
in self._get_all_dbus_properties()
713
if prop._dbus_interface
714
== if_tag.getAttribute("name")):
715
if_tag.appendChild(tag)
716
# Add the names to the return values for the
717
# "org.freedesktop.DBus.Properties" methods
718
if (if_tag.getAttribute("name")
719
== "org.freedesktop.DBus.Properties"):
720
for cn in if_tag.getElementsByTagName("method"):
721
if cn.getAttribute("name") == "Get":
722
for arg in cn.getElementsByTagName("arg"):
723
if (arg.getAttribute("direction")
725
arg.setAttribute("name", "value")
726
elif cn.getAttribute("name") == "GetAll":
727
for arg in cn.getElementsByTagName("arg"):
728
if (arg.getAttribute("direction")
730
arg.setAttribute("name", "props")
731
xmlstring = document.toxml("utf-8")
733
except (AttributeError, xml.dom.DOMException,
734
xml.parsers.expat.ExpatError) as error:
735
logger.error("Failed to override Introspection method",
740
class ClientDBus(Client, DBusObjectWithProperties):
466
def still_valid(self):
467
"""Has the timeout not yet passed for this client?"""
468
if not getattr(self, u"enabled", False):
470
now = datetime.datetime.utcnow()
471
if self.last_checked_ok is None:
472
return now < (self.created + self.timeout)
474
return now < (self.last_checked_ok + self.timeout)
477
class ClientDBus(Client, dbus.service.Object):
741
478
"""A Client class using D-Bus
744
481
dbus_object_path: dbus.ObjectPath
745
482
bus: dbus.SystemBus()
748
runtime_expansions = (Client.runtime_expansions
749
+ ("dbus_object_path",))
751
484
# dbus.service.Object doesn't use super(), so we can't either.
753
486
def __init__(self, bus = None, *args, **kwargs):
754
self._approvals_pending = 0
756
488
Client.__init__(self, *args, **kwargs)
757
489
# Only now, when this client is initialized, can it show up on
759
client_object_name = unicode(self.name).translate(
762
491
self.dbus_object_path = (dbus.ObjectPath
763
("/clients/" + client_object_name))
764
DBusObjectWithProperties.__init__(self, self.bus,
765
self.dbus_object_path)
767
def _get_approvals_pending(self):
768
return self._approvals_pending
769
def _set_approvals_pending(self, value):
770
old_value = self._approvals_pending
771
self._approvals_pending = value
773
if (hasattr(self, "dbus_object_path")
774
and bval is not bool(old_value)):
775
dbus_bool = dbus.Boolean(bval, variant_level=1)
776
self.PropertyChanged(dbus.String("ApprovalPending"),
779
approvals_pending = property(_get_approvals_pending,
780
_set_approvals_pending)
781
del _get_approvals_pending, _set_approvals_pending
493
+ self.name.replace(u".", u"_")))
494
dbus.service.Object.__init__(self, self.bus,
495
self.dbus_object_path)
784
498
def _datetime_to_dbus(dt, variant_level=0):
869
575
# Emit D-Bus signal
870
576
self.CheckerStarted(self.current_checker_command)
871
577
self.PropertyChanged(
872
dbus.String("CheckerRunning"),
578
dbus.String(u"checker_running"),
873
579
dbus.Boolean(True, variant_level=1))
876
582
def stop_checker(self, *args, **kwargs):
877
old_checker = getattr(self, "checker", None)
583
old_checker = getattr(self, u"checker", None)
878
584
r = Client.stop_checker(self, *args, **kwargs)
879
585
if (old_checker is not None
880
and getattr(self, "checker", None) is None):
881
self.PropertyChanged(dbus.String("CheckerRunning"),
586
and getattr(self, u"checker", None) is None):
587
self.PropertyChanged(dbus.String(u"checker_running"),
882
588
dbus.Boolean(False, variant_level=1))
885
def _reset_approved(self):
886
self._approved = None
889
def approve(self, value=True):
890
self.send_changedstate()
891
self._approved = value
892
gobject.timeout_add(self._timedelta_to_milliseconds
893
(self.approval_duration),
894
self._reset_approved)
897
## D-Bus methods, signals & properties
898
_interface = "se.bsnet.fukt.Mandos.Client"
591
## D-Bus methods & signals
592
_interface = u"se.bsnet.fukt.Mandos.Client"
595
@dbus.service.method(_interface)
597
return self.checked_ok()
902
599
# CheckerCompleted - signal
903
@dbus.service.signal(_interface, signature="nxs")
600
@dbus.service.signal(_interface, signature=u"nxs")
904
601
def CheckerCompleted(self, exitcode, waitstatus, command):
908
605
# CheckerStarted - signal
909
@dbus.service.signal(_interface, signature="s")
606
@dbus.service.signal(_interface, signature=u"s")
910
607
def CheckerStarted(self, command):
611
# GetAllProperties - method
612
@dbus.service.method(_interface, out_signature=u"a{sv}")
613
def GetAllProperties(self):
615
return dbus.Dictionary({
616
dbus.String(u"name"):
617
dbus.String(self.name, variant_level=1),
618
dbus.String(u"fingerprint"):
619
dbus.String(self.fingerprint, variant_level=1),
620
dbus.String(u"host"):
621
dbus.String(self.host, variant_level=1),
622
dbus.String(u"created"):
623
self._datetime_to_dbus(self.created,
625
dbus.String(u"last_enabled"):
626
(self._datetime_to_dbus(self.last_enabled,
628
if self.last_enabled is not None
629
else dbus.Boolean(False, variant_level=1)),
630
dbus.String(u"enabled"):
631
dbus.Boolean(self.enabled, variant_level=1),
632
dbus.String(u"last_checked_ok"):
633
(self._datetime_to_dbus(self.last_checked_ok,
635
if self.last_checked_ok is not None
636
else dbus.Boolean (False, variant_level=1)),
637
dbus.String(u"timeout"):
638
dbus.UInt64(self.timeout_milliseconds(),
640
dbus.String(u"interval"):
641
dbus.UInt64(self.interval_milliseconds(),
643
dbus.String(u"checker"):
644
dbus.String(self.checker_command,
646
dbus.String(u"checker_running"):
647
dbus.Boolean(self.checker is not None,
649
dbus.String(u"object_path"):
650
dbus.ObjectPath(self.dbus_object_path,
654
# IsStillValid - method
655
@dbus.service.method(_interface, out_signature=u"b")
656
def IsStillValid(self):
657
return self.still_valid()
914
659
# PropertyChanged - signal
915
@dbus.service.signal(_interface, signature="sv")
660
@dbus.service.signal(_interface, signature=u"sv")
916
661
def PropertyChanged(self, property, value):
665
# ReceivedSecret - signal
921
666
@dbus.service.signal(_interface)
924
Is sent after a successful transfer of secret from the Mandos
925
server to mandos-client
667
def ReceivedSecret(self):
929
671
# Rejected - signal
930
@dbus.service.signal(_interface, signature="s")
931
def Rejected(self, reason):
672
@dbus.service.signal(_interface)
935
# NeedApproval - signal
936
@dbus.service.signal(_interface, signature="tb")
937
def NeedApproval(self, timeout, default):
939
return self.need_approval()
944
@dbus.service.method(_interface, in_signature="b")
945
def Approve(self, value):
949
@dbus.service.method(_interface)
677
# SetChecker - method
678
@dbus.service.method(_interface, in_signature=u"s")
679
def SetChecker(self, checker):
680
"D-Bus setter method"
681
self.checker_command = checker
683
self.PropertyChanged(dbus.String(u"checker"),
684
dbus.String(self.checker_command,
688
@dbus.service.method(_interface, in_signature=u"s")
689
def SetHost(self, host):
690
"D-Bus setter method"
693
self.PropertyChanged(dbus.String(u"host"),
694
dbus.String(self.host, variant_level=1))
696
# SetInterval - method
697
@dbus.service.method(_interface, in_signature=u"t")
698
def SetInterval(self, milliseconds):
699
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
701
self.PropertyChanged(dbus.String(u"interval"),
702
(dbus.UInt64(self.interval_milliseconds(),
706
@dbus.service.method(_interface, in_signature=u"ay",
708
def SetSecret(self, secret):
709
"D-Bus setter method"
710
self.secret = str(secret)
712
# SetTimeout - method
713
@dbus.service.method(_interface, in_signature=u"t")
714
def SetTimeout(self, milliseconds):
715
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
717
self.PropertyChanged(dbus.String(u"timeout"),
718
(dbus.UInt64(self.timeout_milliseconds(),
953
721
# Enable - method
954
722
@dbus.service.method(_interface)
973
741
def StopChecker(self):
974
742
self.stop_checker()
978
# ApprovalPending - property
979
@dbus_service_property(_interface, signature="b", access="read")
980
def ApprovalPending_dbus_property(self):
981
return dbus.Boolean(bool(self.approvals_pending))
983
# ApprovedByDefault - property
984
@dbus_service_property(_interface, signature="b",
986
def ApprovedByDefault_dbus_property(self, value=None):
987
if value is None: # get
988
return dbus.Boolean(self.approved_by_default)
989
self.approved_by_default = bool(value)
991
self.PropertyChanged(dbus.String("ApprovedByDefault"),
992
dbus.Boolean(value, variant_level=1))
994
# ApprovalDelay - property
995
@dbus_service_property(_interface, signature="t",
997
def ApprovalDelay_dbus_property(self, value=None):
998
if value is None: # get
999
return dbus.UInt64(self.approval_delay_milliseconds())
1000
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1002
self.PropertyChanged(dbus.String("ApprovalDelay"),
1003
dbus.UInt64(value, variant_level=1))
1005
# ApprovalDuration - property
1006
@dbus_service_property(_interface, signature="t",
1008
def ApprovalDuration_dbus_property(self, value=None):
1009
if value is None: # get
1010
return dbus.UInt64(self._timedelta_to_milliseconds(
1011
self.approval_duration))
1012
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1014
self.PropertyChanged(dbus.String("ApprovalDuration"),
1015
dbus.UInt64(value, variant_level=1))
1018
@dbus_service_property(_interface, signature="s", access="read")
1019
def Name_dbus_property(self):
1020
return dbus.String(self.name)
1022
# Fingerprint - property
1023
@dbus_service_property(_interface, signature="s", access="read")
1024
def Fingerprint_dbus_property(self):
1025
return dbus.String(self.fingerprint)
1028
@dbus_service_property(_interface, signature="s",
1030
def Host_dbus_property(self, value=None):
1031
if value is None: # get
1032
return dbus.String(self.host)
1035
self.PropertyChanged(dbus.String("Host"),
1036
dbus.String(value, variant_level=1))
1038
# Created - property
1039
@dbus_service_property(_interface, signature="s", access="read")
1040
def Created_dbus_property(self):
1041
return dbus.String(self._datetime_to_dbus(self.created))
1043
# LastEnabled - property
1044
@dbus_service_property(_interface, signature="s", access="read")
1045
def LastEnabled_dbus_property(self):
1046
if self.last_enabled is None:
1047
return dbus.String("")
1048
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1050
# Enabled - property
1051
@dbus_service_property(_interface, signature="b",
1053
def Enabled_dbus_property(self, value=None):
1054
if value is None: # get
1055
return dbus.Boolean(self.enabled)
1061
# LastCheckedOK - property
1062
@dbus_service_property(_interface, signature="s",
1064
def LastCheckedOK_dbus_property(self, value=None):
1065
if value is not None:
1068
if self.last_checked_ok is None:
1069
return dbus.String("")
1070
return dbus.String(self._datetime_to_dbus(self
1073
# LastApprovalRequest - property
1074
@dbus_service_property(_interface, signature="s", access="read")
1075
def LastApprovalRequest_dbus_property(self):
1076
if self.last_approval_request is None:
1077
return dbus.String("")
1078
return dbus.String(self.
1079
_datetime_to_dbus(self
1080
.last_approval_request))
1082
# Timeout - property
1083
@dbus_service_property(_interface, signature="t",
1085
def Timeout_dbus_property(self, value=None):
1086
if value is None: # get
1087
return dbus.UInt64(self.timeout_milliseconds())
1088
self.timeout = datetime.timedelta(0, 0, 0, value)
1090
self.PropertyChanged(dbus.String("Timeout"),
1091
dbus.UInt64(value, variant_level=1))
1092
if getattr(self, "disable_initiator_tag", None) is None:
1094
# Reschedule timeout
1095
gobject.source_remove(self.disable_initiator_tag)
1096
self.disable_initiator_tag = None
1097
time_to_die = (self.
1098
_timedelta_to_milliseconds((self
1103
if time_to_die <= 0:
1104
# The timeout has passed
1107
self.disable_initiator_tag = (gobject.timeout_add
1108
(time_to_die, self.disable))
1110
# Interval - property
1111
@dbus_service_property(_interface, signature="t",
1113
def Interval_dbus_property(self, value=None):
1114
if value is None: # get
1115
return dbus.UInt64(self.interval_milliseconds())
1116
self.interval = datetime.timedelta(0, 0, 0, value)
1118
self.PropertyChanged(dbus.String("Interval"),
1119
dbus.UInt64(value, variant_level=1))
1120
if getattr(self, "checker_initiator_tag", None) is None:
1122
# Reschedule checker run
1123
gobject.source_remove(self.checker_initiator_tag)
1124
self.checker_initiator_tag = (gobject.timeout_add
1125
(value, self.start_checker))
1126
self.start_checker() # Start one now, too
1128
# Checker - property
1129
@dbus_service_property(_interface, signature="s",
1131
def Checker_dbus_property(self, value=None):
1132
if value is None: # get
1133
return dbus.String(self.checker_command)
1134
self.checker_command = value
1136
self.PropertyChanged(dbus.String("Checker"),
1137
dbus.String(self.checker_command,
1140
# CheckerRunning - property
1141
@dbus_service_property(_interface, signature="b",
1143
def CheckerRunning_dbus_property(self, value=None):
1144
if value is None: # get
1145
return dbus.Boolean(self.checker is not None)
1147
self.start_checker()
1151
# ObjectPath - property
1152
@dbus_service_property(_interface, signature="o", access="read")
1153
def ObjectPath_dbus_property(self):
1154
return self.dbus_object_path # is already a dbus.ObjectPath
1157
@dbus_service_property(_interface, signature="ay",
1158
access="write", byte_arrays=True)
1159
def Secret_dbus_property(self, value):
1160
self.secret = str(value)
1165
class ProxyClient(object):
1166
def __init__(self, child_pipe, fpr, address):
1167
self._pipe = child_pipe
1168
self._pipe.send(('init', fpr, address))
1169
if not self._pipe.recv():
1172
def __getattribute__(self, name):
1173
if(name == '_pipe'):
1174
return super(ProxyClient, self).__getattribute__(name)
1175
self._pipe.send(('getattr', name))
1176
data = self._pipe.recv()
1177
if data[0] == 'data':
1179
if data[0] == 'function':
1180
def func(*args, **kwargs):
1181
self._pipe.send(('funcall', name, args, kwargs))
1182
return self._pipe.recv()[1]
1185
def __setattr__(self, name, value):
1186
if(name == '_pipe'):
1187
return super(ProxyClient, self).__setattr__(name, value)
1188
self._pipe.send(('setattr', name, value))
1191
747
class ClientHandler(socketserver.BaseRequestHandler, object):
1192
748
"""A class to handle client connections.
1195
751
Note: This will run in its own forked process."""
1197
753
def handle(self):
1198
with contextlib.closing(self.server.child_pipe) as child_pipe:
1199
logger.info("TCP connection from: %s",
1200
unicode(self.client_address))
1201
logger.debug("Pipe FD: %d",
1202
self.server.child_pipe.fileno())
754
logger.info(u"TCP connection from: %s",
755
unicode(self.client_address))
756
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
757
# Open IPC pipe to parent process
758
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
1204
759
session = (gnutls.connection
1205
760
.ClientSession(self.request,
1206
761
gnutls.connection
1207
762
.X509Credentials()))
764
line = self.request.makefile().readline()
765
logger.debug(u"Protocol version: %r", line)
767
if int(line.strip().split()[0]) > 1:
769
except (ValueError, IndexError, RuntimeError), error:
770
logger.error(u"Unknown protocol version: %s", error)
1209
773
# Note: gnutls.connection.X509Credentials is really a
1210
774
# generic GnuTLS certificate credentials object so long as
1211
775
# no X.509 keys are added to it. Therefore, we can use it
1212
776
# here despite using OpenPGP certificates.
1214
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1215
# "+AES-256-CBC", "+SHA1",
1216
# "+COMP-NULL", "+CTYPE-OPENPGP",
778
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
779
# u"+AES-256-CBC", u"+SHA1",
780
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1218
782
# Use a fallback default, since this MUST be set.
1219
783
priority = self.server.gnutls_priority
1220
784
if priority is None:
1222
786
(gnutls.library.functions
1223
787
.gnutls_priority_set_direct(session._c_object,
1224
788
priority, None))
1226
# Start communication using the Mandos protocol
1227
# Get protocol number
1228
line = self.request.makefile().readline()
1229
logger.debug("Protocol version: %r", line)
1231
if int(line.strip().split()[0]) > 1:
1233
except (ValueError, IndexError, RuntimeError) as error:
1234
logger.error("Unknown protocol version: %s", error)
1237
# Start GnuTLS connection
1239
791
session.handshake()
1240
except gnutls.errors.GNUTLSError as error:
1241
logger.warning("Handshake failed: %s", error)
792
except gnutls.errors.GNUTLSError, error:
793
logger.warning(u"Handshake failed: %s", error)
1242
794
# Do not run session.bye() here: the session is not
1243
795
# established. Just abandon the request.
1245
logger.debug("Handshake succeeded")
1247
approval_required = False
797
logger.debug(u"Handshake succeeded")
1250
fpr = self.fingerprint(self.peer_certificate
1253
gnutls.errors.GNUTLSError) as error:
1254
logger.warning("Bad certificate: %s", error)
1256
logger.debug("Fingerprint: %s", fpr)
1259
client = ProxyClient(child_pipe, fpr,
1260
self.client_address)
1264
if client.approval_delay:
1265
delay = client.approval_delay
1266
client.approvals_pending += 1
1267
approval_required = True
1270
if not client.enabled:
1271
logger.info("Client %s is disabled",
1273
if self.server.use_dbus:
1275
client.Rejected("Disabled")
1278
if client._approved or not client.approval_delay:
1279
#We are approved or approval is disabled
1281
elif client._approved is None:
1282
logger.info("Client %s needs approval",
1284
if self.server.use_dbus:
1286
client.NeedApproval(
1287
client.approval_delay_milliseconds(),
1288
client.approved_by_default)
1290
logger.warning("Client %s was not approved",
1292
if self.server.use_dbus:
1294
client.Rejected("Denied")
1297
#wait until timeout or approved
1298
#x = float(client._timedelta_to_milliseconds(delay))
1299
time = datetime.datetime.now()
1300
client.changedstate.acquire()
1301
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1302
client.changedstate.release()
1303
time2 = datetime.datetime.now()
1304
if (time2 - time) >= delay:
1305
if not client.approved_by_default:
1306
logger.warning("Client %s timed out while"
1307
" waiting for approval",
1309
if self.server.use_dbus:
1311
client.Rejected("Approval timed out")
1316
delay -= time2 - time
1319
while sent_size < len(client.secret):
1321
sent = session.send(client.secret[sent_size:])
1322
except gnutls.errors.GNUTLSError as error:
1323
logger.warning("gnutls send failed")
1325
logger.debug("Sent: %d, remaining: %d",
1326
sent, len(client.secret)
1327
- (sent_size + sent))
1330
logger.info("Sending secret to %s", client.name)
1331
# bump the timeout as if seen
1333
if self.server.use_dbus:
799
fpr = self.fingerprint(self.peer_certificate(session))
800
except (TypeError, gnutls.errors.GNUTLSError), error:
801
logger.warning(u"Bad certificate: %s", error)
804
logger.debug(u"Fingerprint: %s", fpr)
1338
if approval_required:
1339
client.approvals_pending -= 1
1342
except gnutls.errors.GNUTLSError as error:
1343
logger.warning("GnuTLS bye failed")
806
for c in self.server.clients:
807
if c.fingerprint == fpr:
811
ipc.write(u"NOTFOUND %s\n" % fpr)
814
# Have to check if client.still_valid(), since it is
815
# possible that the client timed out while establishing
816
# the GnuTLS session.
817
if not client.still_valid():
818
ipc.write(u"INVALID %s\n" % client.name)
821
ipc.write(u"SENDING %s\n" % client.name)
823
while sent_size < len(client.secret):
824
sent = session.send(client.secret[sent_size:])
825
logger.debug(u"Sent: %d, remaining: %d",
826
sent, len(client.secret)
827
- (sent_size + sent))
1346
832
def peer_certificate(session):
1402
888
# Convert the buffer to a Python bytestring
1403
889
fpr = ctypes.string_at(buf, buf_len.value)
1404
890
# Convert the bytestring to hexadecimal notation
1405
hex_fpr = ''.join("%02X" % ord(char) for char in fpr)
891
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1409
class MultiprocessingMixIn(object):
1410
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1411
def sub_process_main(self, request, address):
1413
self.finish_request(request, address)
1415
self.handle_error(request, address)
1416
self.close_request(request)
1418
def process_request(self, request, address):
1419
"""Start a new process to process the request."""
1420
multiprocessing.Process(target = self.sub_process_main,
1421
args = (request, address)).start()
1423
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1424
""" adds a pipe to the MixIn """
895
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
896
"""Like socketserver.ForkingMixIn, but also pass a pipe.
898
Assumes a gobject.MainLoop event loop.
1425
900
def process_request(self, request, client_address):
1426
901
"""Overrides and wraps the original process_request().
1428
This function creates a new pipe in self.pipe
903
This function creates a new pipe in self.pipe
1430
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1432
super(MultiprocessingMixInWithPipe,
905
self.pipe = os.pipe()
906
super(ForkingMixInWithPipe,
1433
907
self).process_request(request, client_address)
1434
self.child_pipe.close()
1435
self.add_pipe(parent_pipe)
1437
def add_pipe(self, parent_pipe):
908
os.close(self.pipe[1]) # close write end
909
# Call "handle_ipc" for both data and EOF events
910
gobject.io_add_watch(self.pipe[0],
911
gobject.IO_IN | gobject.IO_HUP,
913
def handle_ipc(self, source, condition):
1438
914
"""Dummy function; override as necessary"""
1439
raise NotImplementedError
1441
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
919
class IPv6_TCPServer(ForkingMixInWithPipe,
1442
920
socketserver.TCPServer, object):
1443
921
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1552
1022
for cond, name in
1553
1023
condition_names.iteritems()
1554
1024
if cond & condition)
1555
# error or the other end of multiprocessing.Pipe has closed
1556
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1559
# Read a request from the child
1560
request = parent_pipe.recv()
1561
command = request[0]
1563
if command == 'init':
1565
address = request[2]
1567
for c in self.clients:
1568
if c.fingerprint == fpr:
1572
logger.info("Client not found for fingerprint: %s, ad"
1573
"dress: %s", fpr, address)
1576
mandos_dbus_service.ClientNotFound(fpr, address[0])
1577
parent_pipe.send(False)
1580
gobject.io_add_watch(parent_pipe.fileno(),
1581
gobject.IO_IN | gobject.IO_HUP,
1582
functools.partial(self.handle_ipc,
1583
parent_pipe = parent_pipe,
1584
client_object = client))
1585
parent_pipe.send(True)
1586
# remove the old hook in favor of the new above hook on same fileno
1588
if command == 'funcall':
1589
funcname = request[1]
1593
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1595
if command == 'getattr':
1596
attrname = request[1]
1597
if callable(client_object.__getattribute__(attrname)):
1598
parent_pipe.send(('function',))
1600
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1602
if command == 'setattr':
1603
attrname = request[1]
1605
setattr(client_object, attrname, value)
1025
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1028
# Turn the pipe file descriptor into a Python file object
1029
if source not in file_objects:
1030
file_objects[source] = os.fdopen(source, u"r", 1)
1032
# Read a line from the file object
1033
cmdline = file_objects[source].readline()
1034
if not cmdline: # Empty line means end of file
1035
# close the IPC pipe
1036
file_objects[source].close()
1037
del file_objects[source]
1039
# Stop calling this function
1042
logger.debug(u"IPC command: %r", cmdline)
1044
# Parse and act on command
1045
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1047
if cmd == u"NOTFOUND":
1048
logger.warning(u"Client not found for fingerprint: %s",
1052
mandos_dbus_service.ClientNotFound(args)
1053
elif cmd == u"INVALID":
1054
for client in self.clients:
1055
if client.name == args:
1056
logger.warning(u"Client %s is invalid", args)
1062
logger.error(u"Unknown client %s is invalid", args)
1063
elif cmd == u"SENDING":
1064
for client in self.clients:
1065
if client.name == args:
1066
logger.info(u"Sending secret to %s", client.name)
1070
client.ReceivedSecret()
1073
logger.error(u"Sending secret to unknown client %s",
1076
logger.error(u"Unknown IPC command: %r", cmdline)
1078
# Keep calling this function
1610
1082
def string_to_delta(interval):
1611
1083
"""Parse a string and return a datetime.timedelta
1613
>>> string_to_delta('7d')
1085
>>> string_to_delta(u'7d')
1614
1086
datetime.timedelta(7)
1615
>>> string_to_delta('60s')
1087
>>> string_to_delta(u'60s')
1616
1088
datetime.timedelta(0, 60)
1617
>>> string_to_delta('60m')
1089
>>> string_to_delta(u'60m')
1618
1090
datetime.timedelta(0, 3600)
1619
>>> string_to_delta('24h')
1091
>>> string_to_delta(u'24h')
1620
1092
datetime.timedelta(1)
1621
>>> string_to_delta('1w')
1093
>>> string_to_delta(u'1w')
1622
1094
datetime.timedelta(7)
1623
>>> string_to_delta('5m 30s')
1095
>>> string_to_delta(u'5m 30s')
1624
1096
datetime.timedelta(0, 330)
1626
1098
timevalue = datetime.timedelta(0)
1700
##################################################################
1171
######################################################################
1701
1172
# Parsing of options, both command line and config file
1703
parser = argparse.ArgumentParser()
1704
parser.add_argument("-v", "--version", action="version",
1705
version = "%%(prog)s %s" % version,
1706
help="show version number and exit")
1707
parser.add_argument("-i", "--interface", metavar="IF",
1708
help="Bind to interface IF")
1709
parser.add_argument("-a", "--address",
1710
help="Address to listen for requests on")
1711
parser.add_argument("-p", "--port", type=int,
1712
help="Port number to receive requests on")
1713
parser.add_argument("--check", action="store_true",
1714
help="Run self-test")
1715
parser.add_argument("--debug", action="store_true",
1716
help="Debug mode; run in foreground and log"
1718
parser.add_argument("--debuglevel", metavar="LEVEL",
1719
help="Debug level for stdout output")
1720
parser.add_argument("--priority", help="GnuTLS"
1721
" priority string (see GnuTLS documentation)")
1722
parser.add_argument("--servicename",
1723
metavar="NAME", help="Zeroconf service name")
1724
parser.add_argument("--configdir",
1725
default="/etc/mandos", metavar="DIR",
1726
help="Directory to search for configuration"
1728
parser.add_argument("--no-dbus", action="store_false",
1729
dest="use_dbus", help="Do not provide D-Bus"
1730
" system bus interface")
1731
parser.add_argument("--no-ipv6", action="store_false",
1732
dest="use_ipv6", help="Do not use IPv6")
1733
options = parser.parse_args()
1174
parser = optparse.OptionParser(version = "%%prog %s" % version)
1175
parser.add_option("-i", u"--interface", type=u"string",
1176
metavar="IF", help=u"Bind to interface IF")
1177
parser.add_option("-a", u"--address", type=u"string",
1178
help=u"Address to listen for requests on")
1179
parser.add_option("-p", u"--port", type=u"int",
1180
help=u"Port number to receive requests on")
1181
parser.add_option("--check", action=u"store_true",
1182
help=u"Run self-test")
1183
parser.add_option("--debug", action=u"store_true",
1184
help=u"Debug mode; run in foreground and log to"
1186
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1187
u" priority string (see GnuTLS documentation)")
1188
parser.add_option("--servicename", type=u"string",
1189
metavar=u"NAME", help=u"Zeroconf service name")
1190
parser.add_option("--configdir", type=u"string",
1191
default=u"/etc/mandos", metavar=u"DIR",
1192
help=u"Directory to search for configuration"
1194
parser.add_option("--no-dbus", action=u"store_false",
1195
dest=u"use_dbus", help=u"Do not provide D-Bus"
1196
u" system bus interface")
1197
parser.add_option("--no-ipv6", action=u"store_false",
1198
dest=u"use_ipv6", help=u"Do not use IPv6")
1199
options = parser.parse_args()[0]
1735
1201
if options.check:
1740
1206
# Default values for config file for server-global settings
1741
server_defaults = { "interface": "",
1746
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1747
"servicename": "Mandos",
1207
server_defaults = { u"interface": u"",
1212
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1213
u"servicename": u"Mandos",
1214
u"use_dbus": u"True",
1215
u"use_ipv6": u"True",
1753
1218
# Parse config file for server-global settings
1754
1219
server_config = configparser.SafeConfigParser(server_defaults)
1755
1220
del server_defaults
1756
1221
server_config.read(os.path.join(options.configdir,
1758
1223
# Convert the SafeConfigParser object to a dict
1759
1224
server_settings = server_config.defaults()
1760
1225
# Use the appropriate methods on the non-string config options
1761
for option in ("debug", "use_dbus", "use_ipv6"):
1762
server_settings[option] = server_config.getboolean("DEFAULT",
1226
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1227
server_settings[option] = server_config.getboolean(u"DEFAULT",
1764
1229
if server_settings["port"]:
1765
server_settings["port"] = server_config.getint("DEFAULT",
1230
server_settings["port"] = server_config.getint(u"DEFAULT",
1767
1232
del server_config
1769
1234
# Override the settings from the config file with command line
1770
1235
# options, if set.
1771
for option in ("interface", "address", "port", "debug",
1772
"priority", "servicename", "configdir",
1773
"use_dbus", "use_ipv6", "debuglevel"):
1236
for option in (u"interface", u"address", u"port", u"debug",
1237
u"priority", u"servicename", u"configdir",
1238
u"use_dbus", u"use_ipv6"):
1774
1239
value = getattr(options, option)
1775
1240
if value is not None:
1776
1241
server_settings[option] = value
1784
1249
##################################################################
1786
1251
# For convenience
1787
debug = server_settings["debug"]
1788
debuglevel = server_settings["debuglevel"]
1789
use_dbus = server_settings["use_dbus"]
1790
use_ipv6 = server_settings["use_ipv6"]
1792
if server_settings["servicename"] != "Mandos":
1252
debug = server_settings[u"debug"]
1253
use_dbus = server_settings[u"use_dbus"]
1254
use_ipv6 = server_settings[u"use_ipv6"]
1257
syslogger.setLevel(logging.WARNING)
1258
console.setLevel(logging.WARNING)
1260
if server_settings[u"servicename"] != u"Mandos":
1793
1261
syslogger.setFormatter(logging.Formatter
1794
('Mandos (%s) [%%(process)d]:'
1795
' %%(levelname)s: %%(message)s'
1796
% server_settings["servicename"]))
1262
(u'Mandos (%s) [%%(process)d]:'
1263
u' %%(levelname)s: %%(message)s'
1264
% server_settings[u"servicename"]))
1798
1266
# Parse config file with clients
1799
client_defaults = { "timeout": "1h",
1801
"checker": "fping -q -- %%(host)s",
1803
"approval_delay": "0s",
1804
"approval_duration": "1s",
1267
client_defaults = { u"timeout": u"1h",
1269
u"checker": u"fping -q -- %%(host)s",
1806
1272
client_config = configparser.SafeConfigParser(client_defaults)
1807
client_config.read(os.path.join(server_settings["configdir"],
1273
client_config.read(os.path.join(server_settings[u"configdir"],
1810
1276
global mandos_dbus_service
1811
1277
mandos_dbus_service = None
1813
tcp_server = MandosServer((server_settings["address"],
1814
server_settings["port"]),
1280
tcp_server = MandosServer((server_settings[u"address"],
1281
server_settings[u"port"]),
1816
interface=(server_settings["interface"]
1283
interface=server_settings[u"interface"],
1818
1284
use_ipv6=use_ipv6,
1819
1286
gnutls_priority=
1820
server_settings["priority"],
1287
server_settings[u"priority"],
1821
1288
use_dbus=use_dbus)
1823
pidfilename = "/var/run/mandos.pid"
1825
pidfile = open(pidfilename, "w")
1827
logger.error("Could not open file %r", pidfilename)
1289
pidfilename = u"/var/run/mandos.pid"
1291
pidfile = open(pidfilename, u"w")
1293
logger.error(u"Could not open file %r", pidfilename)
1830
uid = pwd.getpwnam("_mandos").pw_uid
1831
gid = pwd.getpwnam("_mandos").pw_gid
1296
uid = pwd.getpwnam(u"_mandos").pw_uid
1297
gid = pwd.getpwnam(u"_mandos").pw_gid
1832
1298
except KeyError:
1834
uid = pwd.getpwnam("mandos").pw_uid
1835
gid = pwd.getpwnam("mandos").pw_gid
1300
uid = pwd.getpwnam(u"mandos").pw_uid
1301
gid = pwd.getpwnam(u"mandos").pw_gid
1836
1302
except KeyError:
1838
uid = pwd.getpwnam("nobody").pw_uid
1839
gid = pwd.getpwnam("nobody").pw_gid
1304
uid = pwd.getpwnam(u"nobody").pw_uid
1305
gid = pwd.getpwnam(u"nobody").pw_gid
1840
1306
except KeyError:
1846
except OSError as error:
1312
except OSError, error:
1847
1313
if error[0] != errno.EPERM:
1850
if not debug and not debuglevel:
1851
syslogger.setLevel(logging.WARNING)
1852
console.setLevel(logging.WARNING)
1854
level = getattr(logging, debuglevel.upper())
1855
syslogger.setLevel(level)
1856
console.setLevel(level)
1316
# Enable all possible GnuTLS debugging
1859
# Enable all possible GnuTLS debugging
1861
1318
# "Use a log level over 10 to enable all debugging options."
1862
1319
# - GnuTLS manual
1863
1320
gnutls.library.functions.gnutls_global_set_log_level(11)
1865
1322
@gnutls.library.types.gnutls_log_func
1866
1323
def debug_gnutls(level, string):
1867
logger.debug("GnuTLS: %s", string[:-1])
1324
logger.debug(u"GnuTLS: %s", string[:-1])
1869
1326
(gnutls.library.functions
1870
1327
.gnutls_global_set_log_function(debug_gnutls))
1872
# Redirect stdin so all checkers get /dev/null
1873
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1874
os.dup2(null, sys.stdin.fileno())
1878
# No console logging
1879
logger.removeHandler(console)
1881
# Need to fork before connecting to D-Bus
1883
# Close all input and output, do double fork, etc.
1886
1329
global main_loop
1887
1330
# From the Avahi example code
1890
1333
bus = dbus.SystemBus()
1891
1334
# End of Avahi example code
1894
bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1895
bus, do_not_queue=True)
1896
except dbus.exceptions.NameExistsException as e:
1897
logger.error(unicode(e) + ", disabling D-Bus")
1899
server_settings["use_dbus"] = False
1900
tcp_server.use_dbus = False
1336
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1901
1337
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1902
service = AvahiService(name = server_settings["servicename"],
1903
servicetype = "_mandos._tcp",
1338
service = AvahiService(name = server_settings[u"servicename"],
1339
servicetype = u"_mandos._tcp",
1904
1340
protocol = protocol, bus = bus)
1905
1341
if server_settings["interface"]:
1906
1342
service.interface = (if_nametoindex
1907
(str(server_settings["interface"])))
1909
global multiprocessing_manager
1910
multiprocessing_manager = multiprocessing.Manager()
1343
(str(server_settings[u"interface"])))
1912
1345
client_class = Client
1914
1347
client_class = functools.partial(ClientDBus, bus = bus)
1915
def client_config_items(config, section):
1916
special_settings = {
1917
"approved_by_default":
1918
lambda: config.getboolean(section,
1919
"approved_by_default"),
1921
for name, value in config.items(section):
1923
yield (name, special_settings[name]())
1927
tcp_server.clients.update(set(
1928
1349
client_class(name = section,
1929
config= dict(client_config_items(
1930
client_config, section)))
1350
config= dict(client_config.items(section)))
1931
1351
for section in client_config.sections()))
1932
if not tcp_server.clients:
1933
logger.warning("No clients defined")
1353
logger.warning(u"No clients defined")
1356
# Redirect stdin so all checkers get /dev/null
1357
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1358
os.dup2(null, sys.stdin.fileno())
1362
# No console logging
1363
logger.removeHandler(console)
1364
# Close all input and output, do double fork, etc.
1368
with closing(pidfile):
1370
pidfile.write(str(pid) + "\n")
1373
logger.error(u"Could not write to file %r with PID %d",
1376
# "pidfile" was never created
1381
"Cleanup function; run on exit"
1385
client = clients.pop()
1386
client.disable_hook = None
1389
atexit.register(cleanup)
1939
pidfile.write(str(pid) + "\n".encode("utf-8"))
1942
logger.error("Could not write to file %r with PID %d",
1945
# "pidfile" was never created
1949
1392
signal.signal(signal.SIGINT, signal.SIG_IGN)
1951
1393
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1952
1394
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1955
1397
class MandosDBusService(dbus.service.Object):
1956
1398
"""A D-Bus proxy object"""
1957
1399
def __init__(self):
1958
dbus.service.Object.__init__(self, bus, "/")
1959
_interface = "se.bsnet.fukt.Mandos"
1961
@dbus.service.signal(_interface, signature="o")
1962
def ClientAdded(self, objpath):
1966
@dbus.service.signal(_interface, signature="ss")
1967
def ClientNotFound(self, fingerprint, address):
1971
@dbus.service.signal(_interface, signature="os")
1400
dbus.service.Object.__init__(self, bus, u"/")
1401
_interface = u"se.bsnet.fukt.Mandos"
1403
@dbus.service.signal(_interface, signature=u"oa{sv}")
1404
def ClientAdded(self, objpath, properties):
1408
@dbus.service.signal(_interface, signature=u"s")
1409
def ClientNotFound(self, fingerprint):
1413
@dbus.service.signal(_interface, signature=u"os")
1972
1414
def ClientRemoved(self, objpath, name):
1976
@dbus.service.method(_interface, out_signature="ao")
1418
@dbus.service.method(_interface, out_signature=u"ao")
1977
1419
def GetAllClients(self):
1979
return dbus.Array(c.dbus_object_path
1980
for c in tcp_server.clients)
1421
return dbus.Array(c.dbus_object_path for c in clients)
1982
1423
@dbus.service.method(_interface,
1983
out_signature="a{oa{sv}}")
1424
out_signature=u"a{oa{sv}}")
1984
1425
def GetAllClientsWithProperties(self):
1986
1427
return dbus.Dictionary(
1987
((c.dbus_object_path, c.GetAll(""))
1988
for c in tcp_server.clients),
1428
((c.dbus_object_path, c.GetAllProperties())
1430
signature=u"oa{sv}")
1991
@dbus.service.method(_interface, in_signature="o")
1432
@dbus.service.method(_interface, in_signature=u"o")
1992
1433
def RemoveClient(self, object_path):
1994
for c in tcp_server.clients:
1995
1436
if c.dbus_object_path == object_path:
1996
tcp_server.clients.remove(c)
1997
1438
c.remove_from_connection()
1998
1439
# Don't signal anything except ClientRemoved
1999
c.disable(quiet=True)
1440
c.disable(signal=False)
2000
1441
# Emit D-Bus signal
2001
1442
self.ClientRemoved(object_path, c.name)
2003
raise KeyError(object_path)
2007
1448
mandos_dbus_service = MandosDBusService()
2010
"Cleanup function; run on exit"
2013
while tcp_server.clients:
2014
client = tcp_server.clients.pop()
2016
client.remove_from_connection()
2017
client.disable_hook = None
2018
# Don't signal anything except ClientRemoved
2019
client.disable(quiet=True)
2022
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
2025
atexit.register(cleanup)
2027
for client in tcp_server.clients:
1450
for client in clients:
2029
1452
# Emit D-Bus signal
2030
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1453
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1454
client.GetAllProperties())
2031
1455
client.enable()
2033
1457
tcp_server.enable()