144
158
u" after %i retries, exiting.",
145
159
self.rename_count)
146
160
raise AvahiServiceError(u"Too many renames")
147
self.name = server.GetAlternativeServiceName(self.name)
161
self.name = self.server.GetAlternativeServiceName(self.name)
148
162
logger.info(u"Changing Zeroconf service name to %r ...",
150
164
syslogger.setFormatter(logging.Formatter
151
('Mandos (%s) [%%(process)d]:'
152
' %%(levelname)s: %%(message)s'
165
(u'Mandos (%s) [%%(process)d]:'
166
u' %%(levelname)s: %%(message)s'
156
170
self.rename_count += 1
157
171
def remove(self):
158
172
"""Derived from the Avahi example code"""
159
if group is not None:
173
if self.group is not None:
162
176
"""Derived from the Avahi example code"""
165
group = dbus.Interface(bus.get_object
167
server.EntryGroupNew()),
168
avahi.DBUS_INTERFACE_ENTRY_GROUP)
169
group.connect_to_signal('StateChanged',
170
entry_group_state_changed)
177
if self.group is None:
178
self.group = dbus.Interface(
179
self.bus.get_object(avahi.DBUS_NAME,
180
self.server.EntryGroupNew()),
181
avahi.DBUS_INTERFACE_ENTRY_GROUP)
182
self.group.connect_to_signal('StateChanged',
184
.entry_group_state_changed)
171
185
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
172
service.name, service.type)
174
self.interface, # interface
175
self.protocol, # protocol
176
dbus.UInt32(0), # flags
177
self.name, self.type,
178
self.domain, self.host,
179
dbus.UInt16(self.port),
180
avahi.string_array_to_txt_array(self.TXT))
183
# From the Avahi example code:
184
group = None # our entry group
185
# End of Avahi example code
188
def _datetime_to_dbus(dt, variant_level=0):
189
"""Convert a UTC datetime.datetime() to a D-Bus type."""
190
return dbus.String(dt.isoformat(), variant_level=variant_level)
186
self.name, self.type)
187
self.group.AddService(
190
dbus.UInt32(0), # flags
191
self.name, self.type,
192
self.domain, self.host,
193
dbus.UInt16(self.port),
194
avahi.string_array_to_txt_array(self.TXT))
196
def entry_group_state_changed(self, state, error):
197
"""Derived from the Avahi example code"""
198
logger.debug(u"Avahi state change: %i", state)
200
if state == avahi.ENTRY_GROUP_ESTABLISHED:
201
logger.debug(u"Zeroconf service established.")
202
elif state == avahi.ENTRY_GROUP_COLLISION:
203
logger.warning(u"Zeroconf service name collision.")
205
elif state == avahi.ENTRY_GROUP_FAILURE:
206
logger.critical(u"Avahi: Error in group state changed %s",
208
raise AvahiGroupError(u"State changed: %s"
211
"""Derived from the Avahi example code"""
212
if self.group is not None:
215
def server_state_changed(self, state):
216
"""Derived from the Avahi example code"""
217
if state == avahi.SERVER_COLLISION:
218
logger.error(u"Zeroconf server name collision")
220
elif state == avahi.SERVER_RUNNING:
223
"""Derived from the Avahi example code"""
224
if self.server is None:
225
self.server = dbus.Interface(
226
self.bus.get_object(avahi.DBUS_NAME,
227
avahi.DBUS_PATH_SERVER),
228
avahi.DBUS_INTERFACE_SERVER)
229
self.server.connect_to_signal(u"StateChanged",
230
self.server_state_changed)
231
self.server_state_changed(self.server.GetState())
193
234
class Client(object):
206
247
last_checked_ok: datetime.datetime(); (UTC) or None
207
248
timeout: datetime.timedelta(); How long from last_checked_ok
208
until this client is invalid
249
until this client is disabled
209
250
interval: datetime.timedelta(); How often to start a new checker
210
251
disable_hook: If set, called by disable() as disable_hook(self)
211
252
checker: subprocess.Popen(); a running checker process used
212
253
to see if the client lives.
213
254
'None' if no process is running.
214
255
checker_initiator_tag: a gobject event source tag, or None
215
disable_initiator_tag: - '' -
256
disable_initiator_tag: - '' -
216
257
checker_callback_tag: - '' -
217
258
checker_command: string; External command which is run to check if
218
259
client lives. %() expansions are done at
219
260
runtime with vars(self) as dict, so that for
220
261
instance %(name)s can be used in the command.
221
262
current_checker_command: string; current running checker_command
263
approved_delay: datetime.timedelta(); Time to wait for approval
264
_approved: bool(); 'None' if not yet approved/disapproved
265
approved_duration: datetime.timedelta(); Duration of one approval
269
def _timedelta_to_milliseconds(td):
270
"Convert a datetime.timedelta() to milliseconds"
271
return ((td.days * 24 * 60 * 60 * 1000)
272
+ (td.seconds * 1000)
273
+ (td.microseconds // 1000))
223
275
def timeout_milliseconds(self):
224
276
"Return the 'timeout' attribute in milliseconds"
225
return ((self.timeout.days * 24 * 60 * 60 * 1000)
226
+ (self.timeout.seconds * 1000)
227
+ (self.timeout.microseconds // 1000))
277
return self._timedelta_to_milliseconds(self.timeout)
229
279
def interval_milliseconds(self):
230
280
"Return the 'interval' attribute in milliseconds"
231
return ((self.interval.days * 24 * 60 * 60 * 1000)
232
+ (self.interval.seconds * 1000)
233
+ (self.interval.microseconds // 1000))
281
return self._timedelta_to_milliseconds(self.interval)
283
def approved_delay_milliseconds(self):
284
return self._timedelta_to_milliseconds(self.approved_delay)
235
286
def __init__(self, name = None, disable_hook=None, config=None):
236
287
"""Note: the 'checker' key in 'config' sets the
243
294
# Uppercase and remove spaces from fingerprint for later
244
295
# comparison purposes with return value from the fingerprint()
246
self.fingerprint = (config["fingerprint"].upper()
297
self.fingerprint = (config[u"fingerprint"].upper()
247
298
.replace(u" ", u""))
248
299
logger.debug(u" Fingerprint: %s", self.fingerprint)
249
if "secret" in config:
250
self.secret = config["secret"].decode(u"base64")
251
elif "secfile" in config:
252
with closing(open(os.path.expanduser
254
(config["secfile"])))) as secfile:
300
if u"secret" in config:
301
self.secret = config[u"secret"].decode(u"base64")
302
elif u"secfile" in config:
303
with open(os.path.expanduser(os.path.expandvars
304
(config[u"secfile"])),
255
306
self.secret = secfile.read()
308
#XXX Need to allow secret on demand!
257
309
raise TypeError(u"No secret or secfile for client %s"
259
self.host = config.get("host", "")
311
self.host = config.get(u"host", u"")
260
312
self.created = datetime.datetime.utcnow()
261
313
self.enabled = False
262
314
self.last_enabled = None
263
315
self.last_checked_ok = None
264
self.timeout = string_to_delta(config["timeout"])
265
self.interval = string_to_delta(config["interval"])
316
self.timeout = string_to_delta(config[u"timeout"])
317
self.interval = string_to_delta(config[u"interval"])
266
318
self.disable_hook = disable_hook
267
319
self.checker = None
268
320
self.checker_initiator_tag = None
269
321
self.disable_initiator_tag = None
270
322
self.checker_callback_tag = None
271
self.checker_command = config["checker"]
323
self.checker_command = config[u"checker"]
272
324
self.current_checker_command = None
273
325
self.last_connect = None
326
self.approvals_pending = 0
327
self._approved = None
328
self.approved_by_default = config.get(u"approved_by_default",
330
self.approved_delay = string_to_delta(
331
config[u"approved_delay"])
332
self.approved_duration = string_to_delta(
333
config[u"approved_duration"])
334
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
336
def send_changedstate(self):
337
self.changedstate.acquire()
338
self.changedstate.notify_all()
339
self.changedstate.release()
275
341
def enable(self):
276
342
"""Start this client's checker and timeout hooks"""
343
if getattr(self, u"enabled", False):
346
self.send_changedstate()
277
347
self.last_enabled = datetime.datetime.utcnow()
278
348
# Schedule a new checker to be started an 'interval' from now,
279
349
# and every interval from then on.
280
350
self.checker_initiator_tag = (gobject.timeout_add
281
351
(self.interval_milliseconds(),
282
352
self.start_checker))
283
# Also start a new checker *right now*.
285
353
# Schedule a disable() when 'timeout' has passed
286
354
self.disable_initiator_tag = (gobject.timeout_add
287
355
(self.timeout_milliseconds(),
289
357
self.enabled = True
358
# Also start a new checker *right now*.
361
def disable(self, quiet=True):
292
362
"""Disable this client."""
293
363
if not getattr(self, "enabled", False):
295
logger.info(u"Disabling client %s", self.name)
296
if getattr(self, "disable_initiator_tag", False):
366
self.send_changedstate()
368
logger.info(u"Disabling client %s", self.name)
369
if getattr(self, u"disable_initiator_tag", False):
297
370
gobject.source_remove(self.disable_initiator_tag)
298
371
self.disable_initiator_tag = None
299
if getattr(self, "checker_initiator_tag", False):
372
if getattr(self, u"checker_initiator_tag", False):
300
373
gobject.source_remove(self.checker_initiator_tag)
301
374
self.checker_initiator_tag = None
302
375
self.stop_checker()
409
490
if self.checker_callback_tag:
410
491
gobject.source_remove(self.checker_callback_tag)
411
492
self.checker_callback_tag = None
412
if getattr(self, "checker", None) is None:
493
if getattr(self, u"checker", None) is None:
414
495
logger.debug(u"Stopping checker for %(name)s", vars(self))
416
497
os.kill(self.checker.pid, signal.SIGTERM)
418
499
#if self.checker.poll() is None:
419
500
# os.kill(self.checker.pid, signal.SIGKILL)
420
501
except OSError, error:
421
502
if error.errno != errno.ESRCH: # No such process
423
504
self.checker = None
425
def still_valid(self):
426
"""Has the timeout not yet passed for this client?"""
427
if not getattr(self, "enabled", False):
429
now = datetime.datetime.utcnow()
430
if self.last_checked_ok is None:
431
return now < (self.created + self.timeout)
433
return now < (self.last_checked_ok + self.timeout)
436
class ClientDBus(Client, dbus.service.Object):
506
def dbus_service_property(dbus_interface, signature=u"v",
507
access=u"readwrite", byte_arrays=False):
508
"""Decorators for marking methods of a DBusObjectWithProperties to
509
become properties on the D-Bus.
511
The decorated method will be called with no arguments by "Get"
512
and with one argument by "Set".
514
The parameters, where they are supported, are the same as
515
dbus.service.method, except there is only "signature", since the
516
type from Get() and the type sent to Set() is the same.
518
# Encoding deeply encoded byte arrays is not supported yet by the
519
# "Set" method, so we fail early here:
520
if byte_arrays and signature != u"ay":
521
raise ValueError(u"Byte arrays not supported for non-'ay'"
522
u" signature %r" % signature)
524
func._dbus_is_property = True
525
func._dbus_interface = dbus_interface
526
func._dbus_signature = signature
527
func._dbus_access = access
528
func._dbus_name = func.__name__
529
if func._dbus_name.endswith(u"_dbus_property"):
530
func._dbus_name = func._dbus_name[:-14]
531
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
536
class DBusPropertyException(dbus.exceptions.DBusException):
537
"""A base class for D-Bus property-related exceptions
539
def __unicode__(self):
540
return unicode(str(self))
543
class DBusPropertyAccessException(DBusPropertyException):
544
"""A property's access permissions disallows an operation.
549
class DBusPropertyNotFound(DBusPropertyException):
550
"""An attempt was made to access a non-existing property.
555
class DBusObjectWithProperties(dbus.service.Object):
556
"""A D-Bus object with properties.
558
Classes inheriting from this can use the dbus_service_property
559
decorator to expose methods as D-Bus properties. It exposes the
560
standard Get(), Set(), and GetAll() methods on the D-Bus.
564
def _is_dbus_property(obj):
565
return getattr(obj, u"_dbus_is_property", False)
567
def _get_all_dbus_properties(self):
568
"""Returns a generator of (name, attribute) pairs
570
return ((prop._dbus_name, prop)
572
inspect.getmembers(self, self._is_dbus_property))
574
def _get_dbus_property(self, interface_name, property_name):
575
"""Returns a bound method if one exists which is a D-Bus
576
property with the specified name and interface.
578
for name in (property_name,
579
property_name + u"_dbus_property"):
580
prop = getattr(self, name, None)
582
or not self._is_dbus_property(prop)
583
or prop._dbus_name != property_name
584
or (interface_name and prop._dbus_interface
585
and interface_name != prop._dbus_interface)):
589
raise DBusPropertyNotFound(self.dbus_object_path + u":"
590
+ interface_name + u"."
593
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
595
def Get(self, interface_name, property_name):
596
"""Standard D-Bus property Get() method, see D-Bus standard.
598
prop = self._get_dbus_property(interface_name, property_name)
599
if prop._dbus_access == u"write":
600
raise DBusPropertyAccessException(property_name)
602
if not hasattr(value, u"variant_level"):
604
return type(value)(value, variant_level=value.variant_level+1)
606
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
607
def Set(self, interface_name, property_name, value):
608
"""Standard D-Bus property Set() method, see D-Bus standard.
610
prop = self._get_dbus_property(interface_name, property_name)
611
if prop._dbus_access == u"read":
612
raise DBusPropertyAccessException(property_name)
613
if prop._dbus_get_args_options[u"byte_arrays"]:
614
# The byte_arrays option is not supported yet on
615
# signatures other than "ay".
616
if prop._dbus_signature != u"ay":
618
value = dbus.ByteArray(''.join(unichr(byte)
622
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
623
out_signature=u"a{sv}")
624
def GetAll(self, interface_name):
625
"""Standard D-Bus property GetAll() method, see D-Bus
628
Note: Will not include properties with access="write".
631
for name, prop in self._get_all_dbus_properties():
633
and interface_name != prop._dbus_interface):
634
# Interface non-empty but did not match
636
# Ignore write-only properties
637
if prop._dbus_access == u"write":
640
if not hasattr(value, u"variant_level"):
643
all[name] = type(value)(value, variant_level=
644
value.variant_level+1)
645
return dbus.Dictionary(all, signature=u"sv")
647
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
649
path_keyword='object_path',
650
connection_keyword='connection')
651
def Introspect(self, object_path, connection):
652
"""Standard D-Bus method, overloaded to insert property tags.
654
xmlstring = dbus.service.Object.Introspect(self, object_path,
657
document = xml.dom.minidom.parseString(xmlstring)
658
def make_tag(document, name, prop):
659
e = document.createElement(u"property")
660
e.setAttribute(u"name", name)
661
e.setAttribute(u"type", prop._dbus_signature)
662
e.setAttribute(u"access", prop._dbus_access)
664
for if_tag in document.getElementsByTagName(u"interface"):
665
for tag in (make_tag(document, name, prop)
667
in self._get_all_dbus_properties()
668
if prop._dbus_interface
669
== if_tag.getAttribute(u"name")):
670
if_tag.appendChild(tag)
671
# Add the names to the return values for the
672
# "org.freedesktop.DBus.Properties" methods
673
if (if_tag.getAttribute(u"name")
674
== u"org.freedesktop.DBus.Properties"):
675
for cn in if_tag.getElementsByTagName(u"method"):
676
if cn.getAttribute(u"name") == u"Get":
677
for arg in cn.getElementsByTagName(u"arg"):
678
if (arg.getAttribute(u"direction")
680
arg.setAttribute(u"name", u"value")
681
elif cn.getAttribute(u"name") == u"GetAll":
682
for arg in cn.getElementsByTagName(u"arg"):
683
if (arg.getAttribute(u"direction")
685
arg.setAttribute(u"name", u"props")
686
xmlstring = document.toxml(u"utf-8")
688
except (AttributeError, xml.dom.DOMException,
689
xml.parsers.expat.ExpatError), error:
690
logger.error(u"Failed to override Introspection method",
695
class ClientDBus(Client, DBusObjectWithProperties):
437
696
"""A Client class using D-Bus
440
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
699
dbus_object_path: dbus.ObjectPath
700
bus: dbus.SystemBus()
442
702
# dbus.service.Object doesn't use super(), so we can't either.
444
def __init__(self, *args, **kwargs):
704
def __init__(self, bus = None, *args, **kwargs):
445
706
Client.__init__(self, *args, **kwargs)
446
707
# Only now, when this client is initialized, can it show up on
448
709
self.dbus_object_path = (dbus.ObjectPath
450
+ self.name.replace(".", "_")))
451
dbus.service.Object.__init__(self, bus,
452
self.dbus_object_path)
711
+ self.name.replace(u".", u"_")))
712
DBusObjectWithProperties.__init__(self, self.bus,
713
self.dbus_object_path)
716
def _datetime_to_dbus(dt, variant_level=0):
717
"""Convert a UTC datetime.datetime() to a D-Bus type."""
718
return dbus.String(dt.isoformat(),
719
variant_level=variant_level)
453
721
def enable(self):
454
oldstate = getattr(self, "enabled", False)
722
oldstate = getattr(self, u"enabled", False)
455
723
r = Client.enable(self)
456
724
if oldstate != self.enabled:
457
725
# Emit D-Bus signals
458
726
self.PropertyChanged(dbus.String(u"enabled"),
459
727
dbus.Boolean(True, variant_level=1))
460
self.PropertyChanged(dbus.String(u"last_enabled"),
461
(_datetime_to_dbus(self.last_enabled,
728
self.PropertyChanged(
729
dbus.String(u"last_enabled"),
730
self._datetime_to_dbus(self.last_enabled,
465
def disable(self, signal = True):
466
oldstate = getattr(self, "enabled", False)
467
r = Client.disable(self)
468
if signal and oldstate != self.enabled:
734
def disable(self, quiet = False):
735
oldstate = getattr(self, u"enabled", False)
736
r = Client.disable(self, quiet=quiet)
737
if not quiet and oldstate != self.enabled:
469
738
# Emit D-Bus signal
470
739
self.PropertyChanged(dbus.String(u"enabled"),
471
740
dbus.Boolean(False, variant_level=1))
524
793
# Emit D-Bus signal
525
794
self.CheckerStarted(self.current_checker_command)
526
795
self.PropertyChanged(
527
dbus.String("checker_running"),
796
dbus.String(u"checker_running"),
528
797
dbus.Boolean(True, variant_level=1))
531
800
def stop_checker(self, *args, **kwargs):
532
old_checker = getattr(self, "checker", None)
801
old_checker = getattr(self, u"checker", None)
533
802
r = Client.stop_checker(self, *args, **kwargs)
534
803
if (old_checker is not None
535
and getattr(self, "checker", None) is None):
804
and getattr(self, u"checker", None) is None):
536
805
self.PropertyChanged(dbus.String(u"checker_running"),
537
806
dbus.Boolean(False, variant_level=1))
540
## D-Bus methods & signals
809
def _reset_approved(self):
810
self._approved = None
813
def approve(self, value=True):
814
self._approved = value
815
gobject.timeout_add(self._timedelta_to_milliseconds(self.approved_duration, self._reset_approved))
817
def approved_pending(self):
818
return self.approvals_pending > 0
821
## D-Bus methods, signals & properties
541
822
_interface = u"se.bsnet.fukt.Mandos.Client"
544
CheckedOK = dbus.service.method(_interface)(checked_ok)
545
CheckedOK.__name__ = "CheckedOK"
547
826
# CheckerCompleted - signal
548
@dbus.service.signal(_interface, signature="nxs")
827
@dbus.service.signal(_interface, signature=u"nxs")
549
828
def CheckerCompleted(self, exitcode, waitstatus, command):
553
832
# CheckerStarted - signal
554
@dbus.service.signal(_interface, signature="s")
833
@dbus.service.signal(_interface, signature=u"s")
555
834
def CheckerStarted(self, command):
559
# GetAllProperties - method
560
@dbus.service.method(_interface, out_signature="a{sv}")
561
def GetAllProperties(self):
563
return dbus.Dictionary({
565
dbus.String(self.name, variant_level=1),
566
dbus.String("fingerprint"):
567
dbus.String(self.fingerprint, variant_level=1),
569
dbus.String(self.host, variant_level=1),
570
dbus.String("created"):
571
_datetime_to_dbus(self.created, variant_level=1),
572
dbus.String("last_enabled"):
573
(_datetime_to_dbus(self.last_enabled,
575
if self.last_enabled is not None
576
else dbus.Boolean(False, variant_level=1)),
577
dbus.String("enabled"):
578
dbus.Boolean(self.enabled, variant_level=1),
579
dbus.String("last_checked_ok"):
580
(_datetime_to_dbus(self.last_checked_ok,
582
if self.last_checked_ok is not None
583
else dbus.Boolean (False, variant_level=1)),
584
dbus.String("timeout"):
585
dbus.UInt64(self.timeout_milliseconds(),
587
dbus.String("interval"):
588
dbus.UInt64(self.interval_milliseconds(),
590
dbus.String("checker"):
591
dbus.String(self.checker_command,
593
dbus.String("checker_running"):
594
dbus.Boolean(self.checker is not None,
596
dbus.String("object_path"):
597
dbus.ObjectPath(self.dbus_object_path,
601
# IsStillValid - method
602
@dbus.service.method(_interface, out_signature="b")
603
def IsStillValid(self):
604
return self.still_valid()
606
838
# PropertyChanged - signal
607
@dbus.service.signal(_interface, signature="sv")
839
@dbus.service.signal(_interface, signature=u"sv")
608
840
def PropertyChanged(self, property, value):
612
# ReceivedSecret - signal
613
845
@dbus.service.signal(_interface)
614
def ReceivedSecret(self):
848
if self.approved_pending():
849
self.PropertyChanged(dbus.String(u"checker_running"),
850
dbus.Boolean(False, variant_level=1))
618
852
# Rejected - signal
619
@dbus.service.signal(_interface)
624
# SetChecker - method
625
@dbus.service.method(_interface, in_signature="s")
626
def SetChecker(self, checker):
627
"D-Bus setter method"
628
self.checker_command = checker
630
self.PropertyChanged(dbus.String(u"checker"),
631
dbus.String(self.checker_command,
635
@dbus.service.method(_interface, in_signature="s")
636
def SetHost(self, host):
637
"D-Bus setter method"
640
self.PropertyChanged(dbus.String(u"host"),
641
dbus.String(self.host, variant_level=1))
643
# SetInterval - method
644
@dbus.service.method(_interface, in_signature="t")
645
def SetInterval(self, milliseconds):
646
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
648
self.PropertyChanged(dbus.String(u"interval"),
649
(dbus.UInt64(self.interval_milliseconds(),
653
@dbus.service.method(_interface, in_signature="ay",
655
def SetSecret(self, secret):
656
"D-Bus setter method"
657
self.secret = str(secret)
659
# SetTimeout - method
660
@dbus.service.method(_interface, in_signature="t")
661
def SetTimeout(self, milliseconds):
662
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
664
self.PropertyChanged(dbus.String(u"timeout"),
665
(dbus.UInt64(self.timeout_milliseconds(),
853
@dbus.service.signal(_interface, signature=u"s")
854
def Rejected(self, reason):
856
if self.approved_pending():
857
self.PropertyChanged(dbus.String(u"checker_running"),
858
dbus.Boolean(False, variant_level=1))
860
# NeedApproval - signal
861
@dbus.service.signal(_interface, signature=u"db")
862
def NeedApproval(self, timeout, default):
864
if not self.approved_pending():
865
self.PropertyChanged(dbus.String(u"approved_pending"),
866
dbus.Boolean(True, variant_level=1))
871
@dbus.service.method(_interface, in_signature=u"b")
872
def Approve(self, value):
876
@dbus.service.method(_interface)
878
return self.checked_ok()
668
880
# Enable - method
669
Enable = dbus.service.method(_interface)(enable)
670
Enable.__name__ = "Enable"
881
@dbus.service.method(_interface)
672
886
# StartChecker - method
673
887
@dbus.service.method(_interface)
684
898
# StopChecker - method
685
StopChecker = dbus.service.method(_interface)(stop_checker)
686
StopChecker.__name__ = "StopChecker"
899
@dbus.service.method(_interface)
900
def StopChecker(self):
905
# approved_pending - property
906
@dbus_service_property(_interface, signature=u"b", access=u"read")
907
def approved_pending_dbus_property(self):
908
return dbus.Boolean(self.approved_pending())
910
# approved_by_default - property
911
@dbus_service_property(_interface, signature=u"b",
913
def approved_by_default_dbus_property(self):
914
return dbus.Boolean(self.approved_by_default)
916
# approved_delay - property
917
@dbus_service_property(_interface, signature=u"t",
919
def approved_delay_dbus_property(self):
920
return dbus.UInt64(self.approved_delay_milliseconds())
922
# approved_duration - property
923
@dbus_service_property(_interface, signature=u"t",
925
def approved_duration_dbus_property(self):
926
return dbus.UInt64(self._timedelta_to_milliseconds(
927
self.approved_duration))
930
@dbus_service_property(_interface, signature=u"s", access=u"read")
931
def name_dbus_property(self):
932
return dbus.String(self.name)
934
# fingerprint - property
935
@dbus_service_property(_interface, signature=u"s", access=u"read")
936
def fingerprint_dbus_property(self):
937
return dbus.String(self.fingerprint)
940
@dbus_service_property(_interface, signature=u"s",
942
def host_dbus_property(self, value=None):
943
if value is None: # get
944
return dbus.String(self.host)
947
self.PropertyChanged(dbus.String(u"host"),
948
dbus.String(value, variant_level=1))
951
@dbus_service_property(_interface, signature=u"s", access=u"read")
952
def created_dbus_property(self):
953
return dbus.String(self._datetime_to_dbus(self.created))
955
# last_enabled - property
956
@dbus_service_property(_interface, signature=u"s", access=u"read")
957
def last_enabled_dbus_property(self):
958
if self.last_enabled is None:
959
return dbus.String(u"")
960
return dbus.String(self._datetime_to_dbus(self.last_enabled))
963
@dbus_service_property(_interface, signature=u"b",
965
def enabled_dbus_property(self, value=None):
966
if value is None: # get
967
return dbus.Boolean(self.enabled)
973
# last_checked_ok - property
974
@dbus_service_property(_interface, signature=u"s",
976
def last_checked_ok_dbus_property(self, value=None):
977
if value is not None:
980
if self.last_checked_ok is None:
981
return dbus.String(u"")
982
return dbus.String(self._datetime_to_dbus(self
986
@dbus_service_property(_interface, signature=u"t",
988
def timeout_dbus_property(self, value=None):
989
if value is None: # get
990
return dbus.UInt64(self.timeout_milliseconds())
991
self.timeout = datetime.timedelta(0, 0, 0, value)
993
self.PropertyChanged(dbus.String(u"timeout"),
994
dbus.UInt64(value, variant_level=1))
995
if getattr(self, u"disable_initiator_tag", None) is None:
998
gobject.source_remove(self.disable_initiator_tag)
999
self.disable_initiator_tag = None
1000
time_to_die = (self.
1001
_timedelta_to_milliseconds((self
1006
if time_to_die <= 0:
1007
# The timeout has passed
1010
self.disable_initiator_tag = (gobject.timeout_add
1011
(time_to_die, self.disable))
1013
# interval - property
1014
@dbus_service_property(_interface, signature=u"t",
1015
access=u"readwrite")
1016
def interval_dbus_property(self, value=None):
1017
if value is None: # get
1018
return dbus.UInt64(self.interval_milliseconds())
1019
self.interval = datetime.timedelta(0, 0, 0, value)
1021
self.PropertyChanged(dbus.String(u"interval"),
1022
dbus.UInt64(value, variant_level=1))
1023
if getattr(self, u"checker_initiator_tag", None) is None:
1025
# Reschedule checker run
1026
gobject.source_remove(self.checker_initiator_tag)
1027
self.checker_initiator_tag = (gobject.timeout_add
1028
(value, self.start_checker))
1029
self.start_checker() # Start one now, too
1031
# checker - property
1032
@dbus_service_property(_interface, signature=u"s",
1033
access=u"readwrite")
1034
def checker_dbus_property(self, value=None):
1035
if value is None: # get
1036
return dbus.String(self.checker_command)
1037
self.checker_command = value
1039
self.PropertyChanged(dbus.String(u"checker"),
1040
dbus.String(self.checker_command,
1043
# checker_running - property
1044
@dbus_service_property(_interface, signature=u"b",
1045
access=u"readwrite")
1046
def checker_running_dbus_property(self, value=None):
1047
if value is None: # get
1048
return dbus.Boolean(self.checker is not None)
1050
self.start_checker()
1054
# object_path - property
1055
@dbus_service_property(_interface, signature=u"o", access=u"read")
1056
def object_path_dbus_property(self):
1057
return self.dbus_object_path # is already a dbus.ObjectPath
1060
@dbus_service_property(_interface, signature=u"ay",
1061
access=u"write", byte_arrays=True)
1062
def secret_dbus_property(self, value):
1063
self.secret = str(value)
691
class ClientHandler(SocketServer.BaseRequestHandler, object):
1068
class ProxyClient(object):
1069
def __init__(self, child_pipe, fpr, address):
1070
self._pipe = child_pipe
1071
self._pipe.send(('init', fpr, address))
1072
if not self._pipe.recv():
1075
def __getattribute__(self, name):
1076
if(name == '_pipe'):
1077
return super(ProxyClient, self).__getattribute__(name)
1078
self._pipe.send(('getattr', name))
1079
data = self._pipe.recv()
1080
if data[0] == 'data':
1082
if data[0] == 'function':
1083
def func(*args, **kwargs):
1084
self._pipe.send(('funcall', name, args, kwargs))
1085
return self._pipe.recv()[1]
1088
def __setattr__(self, name, value):
1089
if(name == '_pipe'):
1090
return super(ProxyClient, self).__setattr__(name, value)
1091
self._pipe.send(('setattr', name, value))
1094
class ClientHandler(socketserver.BaseRequestHandler, object):
692
1095
"""A class to handle client connections.
694
1097
Instantiated once for each connection to handle it.
695
1098
Note: This will run in its own forked process."""
697
1100
def handle(self):
698
logger.info(u"TCP connection from: %s",
699
unicode(self.client_address))
700
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
701
# Open IPC pipe to parent process
702
with closing(os.fdopen(self.server.pipe[1], "w", 1)) as ipc:
1101
with contextlib.closing(self.server.child_pipe) as child_pipe:
1102
logger.info(u"TCP connection from: %s",
1103
unicode(self.client_address))
1104
logger.debug(u"Pipe FD: %d",
1105
self.server.child_pipe.fileno())
703
1107
session = (gnutls.connection
704
1108
.ClientSession(self.request,
705
1109
gnutls.connection
706
1110
.X509Credentials()))
708
line = self.request.makefile().readline()
709
logger.debug(u"Protocol version: %r", line)
711
if int(line.strip().split()[0]) > 1:
713
except (ValueError, IndexError, RuntimeError), error:
714
logger.error(u"Unknown protocol version: %s", error)
717
1112
# Note: gnutls.connection.X509Credentials is really a
718
1113
# generic GnuTLS certificate credentials object so long as
719
1114
# no X.509 keys are added to it. Therefore, we can use it
720
1115
# here despite using OpenPGP certificates.
722
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
723
# "+AES-256-CBC", "+SHA1",
724
# "+COMP-NULL", "+CTYPE-OPENPGP",
1117
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1118
# u"+AES-256-CBC", u"+SHA1",
1119
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
726
1121
# Use a fallback default, since this MUST be set.
727
1122
priority = self.server.gnutls_priority
728
1123
if priority is None:
1124
priority = u"NORMAL"
730
1125
(gnutls.library.functions
731
1126
.gnutls_priority_set_direct(session._c_object,
732
1127
priority, None))
1129
# Start communication using the Mandos protocol
1130
# Get protocol number
1131
line = self.request.makefile().readline()
1132
logger.debug(u"Protocol version: %r", line)
1134
if int(line.strip().split()[0]) > 1:
1136
except (ValueError, IndexError, RuntimeError), error:
1137
logger.error(u"Unknown protocol version: %s", error)
1140
# Start GnuTLS connection
735
1142
session.handshake()
736
1143
except gnutls.errors.GNUTLSError, error:
739
1146
# established. Just abandon the request.
741
1148
logger.debug(u"Handshake succeeded")
1150
approval_required = False
743
fpr = self.fingerprint(self.peer_certificate(session))
744
except (TypeError, gnutls.errors.GNUTLSError), error:
745
logger.warning(u"Bad certificate: %s", error)
748
logger.debug(u"Fingerprint: %s", fpr)
1153
fpr = self.fingerprint(self.peer_certificate
1155
except (TypeError, gnutls.errors.GNUTLSError), error:
1156
logger.warning(u"Bad certificate: %s", error)
1158
logger.debug(u"Fingerprint: %s", fpr)
1161
client = ProxyClient(child_pipe, fpr,
1162
self.client_address)
1166
if client.approved_delay:
1167
delay = client.approved_delay
1168
client.approvals_pending += 1
1169
approval_required = True
1172
if not client.enabled:
1173
logger.warning(u"Client %s is disabled",
1175
if self.server.use_dbus:
1177
client.Rejected("Disabled")
1180
if client._approved or not client.approved_delay:
1181
#We are approved or approval is disabled
1183
elif client._approved is None:
1184
logger.info(u"Client %s need approval",
1186
if self.server.use_dbus:
1188
client.NeedApproval(
1189
client.approved_delay_milliseconds(),
1190
client.approved_by_default)
1192
logger.warning(u"Client %s was not approved",
1194
if self.server.use_dbus:
1196
client.Rejected("Disapproved")
1199
#wait until timeout or approved
1200
#x = float(client._timedelta_to_milliseconds(delay))
1201
time = datetime.datetime.now()
1202
client.changedstate.acquire()
1203
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1204
client.changedstate.release()
1205
time2 = datetime.datetime.now()
1206
if (time2 - time) >= delay:
1207
if not client.approved_by_default:
1208
logger.warning("Client %s timed out while"
1209
" waiting for approval",
1211
if self.server.use_dbus:
1213
client.Rejected("Time out")
1218
delay -= time2 - time
1221
while sent_size < len(client.secret):
1222
# XXX handle session exception
1223
sent = session.send(client.secret[sent_size:])
1224
logger.debug(u"Sent: %d, remaining: %d",
1225
sent, len(client.secret)
1226
- (sent_size + sent))
1229
logger.info(u"Sending secret to %s", client.name)
1230
# bump the timeout as if seen
1232
if self.server.use_dbus:
750
for c in self.server.clients:
751
if c.fingerprint == fpr:
755
ipc.write("NOTFOUND %s\n" % fpr)
758
# Have to check if client.still_valid(), since it is
759
# possible that the client timed out while establishing
760
# the GnuTLS session.
761
if not client.still_valid():
762
ipc.write("INVALID %s\n" % client.name)
765
ipc.write("SENDING %s\n" % client.name)
767
while sent_size < len(client.secret):
768
sent = session.send(client.secret[sent_size:])
769
logger.debug(u"Sent: %d, remaining: %d",
770
sent, len(client.secret)
771
- (sent_size + sent))
1237
if approval_required:
1238
client.approvals_pending -= 1
776
1242
def peer_certificate(session):
839
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
840
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
842
Assumes a gobject.MainLoop event loop.
1305
class MultiprocessingMixIn(object):
1306
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1307
def sub_process_main(self, request, address):
1309
self.finish_request(request, address)
1311
self.handle_error(request, address)
1312
self.close_request(request)
1314
def process_request(self, request, address):
1315
"""Start a new process to process the request."""
1316
multiprocessing.Process(target = self.sub_process_main,
1317
args = (request, address)).start()
1319
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1320
""" adds a pipe to the MixIn """
844
1321
def process_request(self, request, client_address):
845
1322
"""Overrides and wraps the original process_request().
847
This function creates a new pipe in self.pipe
1324
This function creates a new pipe in self.pipe
849
self.pipe = os.pipe()
850
super(ForkingMixInWithPipe,
1326
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1328
super(MultiprocessingMixInWithPipe,
851
1329
self).process_request(request, client_address)
852
os.close(self.pipe[1]) # close write end
853
# Call "handle_ipc" for both data and EOF events
854
gobject.io_add_watch(self.pipe[0],
855
gobject.IO_IN | gobject.IO_HUP,
857
def handle_ipc(source, condition):
1330
self.add_pipe(parent_pipe)
1331
def add_pipe(self, parent_pipe):
858
1332
"""Dummy function; override as necessary"""
863
class IPv6_TCPServer(ForkingMixInWithPipe,
864
SocketServer.TCPServer, object):
1335
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1336
socketserver.TCPServer, object):
865
1337
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
868
1340
enabled: Boolean; whether this server is activated yet
869
1341
interface: None or a network interface name (string)
870
1342
use_ipv6: Boolean; to use IPv6 or not
872
clients: Set() of Client objects
873
gnutls_priority GnuTLS priority string
874
use_dbus: Boolean; to emit D-Bus signals or not
876
1344
def __init__(self, server_address, RequestHandlerClass,
877
interface=None, use_ipv6=True, clients=None,
878
gnutls_priority=None, use_dbus=True):
1345
interface=None, use_ipv6=True):
880
1346
self.interface = interface
882
1348
self.address_family = socket.AF_INET6
883
self.clients = clients
884
self.use_dbus = use_dbus
885
self.gnutls_priority = gnutls_priority
886
SocketServer.TCPServer.__init__(self, server_address,
1349
socketserver.TCPServer.__init__(self, server_address,
887
1350
RequestHandlerClass)
888
1351
def server_bind(self):
889
1352
"""This overrides the normal server_bind() function
890
1353
to bind to an interface if one was specified, and also NOT to
891
1354
bind to an address or port if they were not specified."""
892
1355
if self.interface is not None:
894
self.socket.setsockopt(socket.SOL_SOCKET,
896
self.interface + '\0')
897
except socket.error, error:
898
if error[0] == errno.EPERM:
899
logger.error(u"No permission to"
900
u" bind to interface %s",
1356
if SO_BINDTODEVICE is None:
1357
logger.error(u"SO_BINDTODEVICE does not exist;"
1358
u" cannot bind to interface %s",
1362
self.socket.setsockopt(socket.SOL_SOCKET,
1366
except socket.error, error:
1367
if error[0] == errno.EPERM:
1368
logger.error(u"No permission to"
1369
u" bind to interface %s",
1371
elif error[0] == errno.ENOPROTOOPT:
1372
logger.error(u"SO_BINDTODEVICE not available;"
1373
u" cannot bind to interface %s",
904
1377
# Only bind(2) the socket if we really need to.
905
1378
if self.server_address[0] or self.server_address[1]:
906
1379
if not self.server_address[0]:
907
1380
if self.address_family == socket.AF_INET6:
908
any_address = "::" # in6addr_any
1381
any_address = u"::" # in6addr_any
910
1383
any_address = socket.INADDR_ANY
911
1384
self.server_address = (any_address,
920
1393
# if_nametoindex
921
1394
# (self.interface))
922
return SocketServer.TCPServer.server_bind(self)
1395
return socketserver.TCPServer.server_bind(self)
1398
class MandosServer(IPv6_TCPServer):
1402
clients: set of Client objects
1403
gnutls_priority GnuTLS priority string
1404
use_dbus: Boolean; to emit D-Bus signals or not
1406
Assumes a gobject.MainLoop event loop.
1408
def __init__(self, server_address, RequestHandlerClass,
1409
interface=None, use_ipv6=True, clients=None,
1410
gnutls_priority=None, use_dbus=True):
1411
self.enabled = False
1412
self.clients = clients
1413
if self.clients is None:
1414
self.clients = set()
1415
self.use_dbus = use_dbus
1416
self.gnutls_priority = gnutls_priority
1417
IPv6_TCPServer.__init__(self, server_address,
1418
RequestHandlerClass,
1419
interface = interface,
1420
use_ipv6 = use_ipv6)
923
1421
def server_activate(self):
924
1422
if self.enabled:
925
return SocketServer.TCPServer.server_activate(self)
1423
return socketserver.TCPServer.server_activate(self)
926
1424
def enable(self):
927
1425
self.enabled = True
928
def handle_ipc(self, source, condition, file_objects={}):
1426
def add_pipe(self, parent_pipe):
1427
# Call "handle_ipc" for both data and EOF events
1428
gobject.io_add_watch(parent_pipe.fileno(),
1429
gobject.IO_IN | gobject.IO_HUP,
1430
functools.partial(self.handle_ipc,
1431
parent_pipe = parent_pipe))
1433
def handle_ipc(self, source, condition, parent_pipe=None,
1434
client_object=None):
929
1435
condition_names = {
930
gobject.IO_IN: "IN", # There is data to read.
931
gobject.IO_OUT: "OUT", # Data can be written (without
933
gobject.IO_PRI: "PRI", # There is urgent data to read.
934
gobject.IO_ERR: "ERR", # Error condition.
935
gobject.IO_HUP: "HUP" # Hung up (the connection has been
936
# broken, usually for pipes and
1436
gobject.IO_IN: u"IN", # There is data to read.
1437
gobject.IO_OUT: u"OUT", # Data can be written (without
1439
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1440
gobject.IO_ERR: u"ERR", # Error condition.
1441
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1442
# broken, usually for pipes and
939
1445
conditions_string = ' | '.join(name
940
1446
for cond, name in
941
1447
condition_names.iteritems()
942
1448
if cond & condition)
943
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1449
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
944
1450
conditions_string)
946
# Turn the pipe file descriptor into a Python file object
947
if source not in file_objects:
948
file_objects[source] = os.fdopen(source, "r", 1)
1452
# Read a request from the child
1453
request = parent_pipe.recv()
1454
command = request[0]
950
# Read a line from the file object
951
cmdline = file_objects[source].readline()
952
if not cmdline: # Empty line means end of file
954
file_objects[source].close()
955
del file_objects[source]
957
# Stop calling this function
1456
if command == 'init':
1458
address = request[2]
1460
for c in self.clients:
1461
if c.fingerprint == fpr:
1465
logger.warning(u"Client not found for fingerprint: %s, ad"
1466
u"dress: %s", fpr, address)
1469
mandos_dbus_service.ClientNotFound(fpr, address)
1470
parent_pipe.send(False)
1473
gobject.io_add_watch(parent_pipe.fileno(),
1474
gobject.IO_IN | gobject.IO_HUP,
1475
functools.partial(self.handle_ipc,
1476
parent_pipe = parent_pipe,
1477
client_object = client))
1478
parent_pipe.send(True)
1479
# remove the old hook in favor of the new above hook on same fileno
960
logger.debug("IPC command: %r", cmdline)
962
# Parse and act on command
963
cmd, args = cmdline.rstrip("\r\n").split(None, 1)
965
if cmd == "NOTFOUND":
966
logger.warning(u"Client not found for fingerprint: %s",
970
mandos_dbus_service.ClientNotFound(args)
971
elif cmd == "INVALID":
972
for client in self.clients:
973
if client.name == args:
974
logger.warning(u"Client %s is invalid", args)
980
logger.error(u"Unknown client %s is invalid", args)
981
elif cmd == "SENDING":
982
for client in self.clients:
983
if client.name == args:
984
logger.info(u"Sending secret to %s", client.name)
988
client.ReceivedSecret()
991
logger.error(u"Sending secret to unknown client %s",
994
logger.error("Unknown IPC command: %r", cmdline)
996
# Keep calling this function
1481
if command == 'funcall':
1482
funcname = request[1]
1486
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1488
if command == 'getattr':
1489
attrname = request[1]
1490
if callable(client_object.__getattribute__(attrname)):
1491
parent_pipe.send(('function',))
1493
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1495
if command == 'setattr':
1496
attrname = request[1]
1498
setattr(client_object, attrname, value)
1000
1503
def string_to_delta(interval):
1001
1504
"""Parse a string and return a datetime.timedelta
1003
>>> string_to_delta('7d')
1506
>>> string_to_delta(u'7d')
1004
1507
datetime.timedelta(7)
1005
>>> string_to_delta('60s')
1508
>>> string_to_delta(u'60s')
1006
1509
datetime.timedelta(0, 60)
1007
>>> string_to_delta('60m')
1510
>>> string_to_delta(u'60m')
1008
1511
datetime.timedelta(0, 3600)
1009
>>> string_to_delta('24h')
1512
>>> string_to_delta(u'24h')
1010
1513
datetime.timedelta(1)
1011
1514
>>> string_to_delta(u'1w')
1012
1515
datetime.timedelta(7)
1013
>>> string_to_delta('5m 30s')
1516
>>> string_to_delta(u'5m 30s')
1014
1517
datetime.timedelta(0, 330)
1016
1519
timevalue = datetime.timedelta(0)
1029
1532
elif suffix == u"w":
1030
1533
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1033
except (ValueError, IndexError):
1535
raise ValueError(u"Unknown suffix %r" % suffix)
1536
except (ValueError, IndexError), e:
1537
raise ValueError(e.message)
1035
1538
timevalue += delta
1036
1539
return timevalue
1039
def server_state_changed(state):
1040
"""Derived from the Avahi example code"""
1041
if state == avahi.SERVER_COLLISION:
1042
logger.error(u"Zeroconf server name collision")
1044
elif state == avahi.SERVER_RUNNING:
1048
def entry_group_state_changed(state, error):
1049
"""Derived from the Avahi example code"""
1050
logger.debug(u"Avahi state change: %i", state)
1052
if state == avahi.ENTRY_GROUP_ESTABLISHED:
1053
logger.debug(u"Zeroconf service established.")
1054
elif state == avahi.ENTRY_GROUP_COLLISION:
1055
logger.warning(u"Zeroconf service name collision.")
1057
elif state == avahi.ENTRY_GROUP_FAILURE:
1058
logger.critical(u"Avahi: Error in group state changed %s",
1060
raise AvahiGroupError(u"State changed: %s" % unicode(error))
1062
1542
def if_nametoindex(interface):
1063
"""Call the C function if_nametoindex(), or equivalent"""
1543
"""Call the C function if_nametoindex(), or equivalent
1545
Note: This function cannot accept a unicode string."""
1064
1546
global if_nametoindex
1066
1548
if_nametoindex = (ctypes.cdll.LoadLibrary
1067
(ctypes.util.find_library("c"))
1549
(ctypes.util.find_library(u"c"))
1068
1550
.if_nametoindex)
1069
1551
except (OSError, AttributeError):
1070
if "struct" not in sys.modules:
1072
if "fcntl" not in sys.modules:
1552
logger.warning(u"Doing if_nametoindex the hard way")
1074
1553
def if_nametoindex(interface):
1075
1554
"Get an interface index the hard way, i.e. using fcntl()"
1076
1555
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1077
with closing(socket.socket()) as s:
1556
with contextlib.closing(socket.socket()) as s:
1078
1557
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1079
struct.pack("16s16x", interface))
1080
interface_index = struct.unpack("I", ifreq[16:20])[0]
1558
struct.pack(str(u"16s16x"),
1560
interface_index = struct.unpack(str(u"I"),
1081
1562
return interface_index
1082
1563
return if_nametoindex(interface)
1111
######################################################################
1593
##################################################################
1112
1594
# Parsing of options, both command line and config file
1114
1596
parser = optparse.OptionParser(version = "%%prog %s" % version)
1115
parser.add_option("-i", "--interface", type="string",
1116
metavar="IF", help="Bind to interface IF")
1117
parser.add_option("-a", "--address", type="string",
1118
help="Address to listen for requests on")
1119
parser.add_option("-p", "--port", type="int",
1120
help="Port number to receive requests on")
1121
parser.add_option("--check", action="store_true",
1122
help="Run self-test")
1123
parser.add_option("--debug", action="store_true",
1124
help="Debug mode; run in foreground and log to"
1126
parser.add_option("--priority", type="string", help="GnuTLS"
1127
" priority string (see GnuTLS documentation)")
1128
parser.add_option("--servicename", type="string", metavar="NAME",
1129
help="Zeroconf service name")
1130
parser.add_option("--configdir", type="string",
1131
default="/etc/mandos", metavar="DIR",
1132
help="Directory to search for configuration"
1134
parser.add_option("--no-dbus", action="store_false",
1136
help="Do not provide D-Bus system bus"
1138
parser.add_option("--no-ipv6", action="store_false",
1139
dest="use_ipv6", help="Do not use IPv6")
1597
parser.add_option("-i", u"--interface", type=u"string",
1598
metavar="IF", help=u"Bind to interface IF")
1599
parser.add_option("-a", u"--address", type=u"string",
1600
help=u"Address to listen for requests on")
1601
parser.add_option("-p", u"--port", type=u"int",
1602
help=u"Port number to receive requests on")
1603
parser.add_option("--check", action=u"store_true",
1604
help=u"Run self-test")
1605
parser.add_option("--debug", action=u"store_true",
1606
help=u"Debug mode; run in foreground and log to"
1608
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1609
u" priority string (see GnuTLS documentation)")
1610
parser.add_option("--servicename", type=u"string",
1611
metavar=u"NAME", help=u"Zeroconf service name")
1612
parser.add_option("--configdir", type=u"string",
1613
default=u"/etc/mandos", metavar=u"DIR",
1614
help=u"Directory to search for configuration"
1616
parser.add_option("--no-dbus", action=u"store_false",
1617
dest=u"use_dbus", help=u"Do not provide D-Bus"
1618
u" system bus interface")
1619
parser.add_option("--no-ipv6", action=u"store_false",
1620
dest=u"use_ipv6", help=u"Do not use IPv6")
1140
1621
options = parser.parse_args()[0]
1142
1623
if options.check:
1147
1628
# Default values for config file for server-global settings
1148
server_defaults = { "interface": "",
1153
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1154
"servicename": "Mandos",
1629
server_defaults = { u"interface": u"",
1634
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1635
u"servicename": u"Mandos",
1636
u"use_dbus": u"True",
1637
u"use_ipv6": u"True",
1159
1640
# Parse config file for server-global settings
1160
server_config = ConfigParser.SafeConfigParser(server_defaults)
1641
server_config = configparser.SafeConfigParser(server_defaults)
1161
1642
del server_defaults
1162
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1643
server_config.read(os.path.join(options.configdir,
1163
1645
# Convert the SafeConfigParser object to a dict
1164
1646
server_settings = server_config.defaults()
1165
1647
# Use the appropriate methods on the non-string config options
1166
server_settings["debug"] = server_config.getboolean("DEFAULT",
1168
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1170
server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1648
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1649
server_settings[option] = server_config.getboolean(u"DEFAULT",
1172
1651
if server_settings["port"]:
1173
server_settings["port"] = server_config.getint("DEFAULT",
1652
server_settings["port"] = server_config.getint(u"DEFAULT",
1175
1654
del server_config
1177
1656
# Override the settings from the config file with command line
1178
1657
# options, if set.
1179
for option in ("interface", "address", "port", "debug",
1180
"priority", "servicename", "configdir",
1181
"use_dbus", "use_ipv6"):
1658
for option in (u"interface", u"address", u"port", u"debug",
1659
u"priority", u"servicename", u"configdir",
1660
u"use_dbus", u"use_ipv6"):
1182
1661
value = getattr(options, option)
1183
1662
if value is not None:
1184
1663
server_settings[option] = value
1665
# Force all strings to be unicode
1666
for option in server_settings.keys():
1667
if type(server_settings[option]) is str:
1668
server_settings[option] = unicode(server_settings[option])
1186
1669
# Now we have our good server settings in "server_settings"
1188
1671
##################################################################
1190
1673
# For convenience
1191
debug = server_settings["debug"]
1192
use_dbus = server_settings["use_dbus"]
1193
use_ipv6 = server_settings["use_ipv6"]
1674
debug = server_settings[u"debug"]
1675
use_dbus = server_settings[u"use_dbus"]
1676
use_ipv6 = server_settings[u"use_ipv6"]
1196
1679
syslogger.setLevel(logging.WARNING)
1197
1680
console.setLevel(logging.WARNING)
1199
if server_settings["servicename"] != "Mandos":
1682
if server_settings[u"servicename"] != u"Mandos":
1200
1683
syslogger.setFormatter(logging.Formatter
1201
('Mandos (%s) [%%(process)d]:'
1202
' %%(levelname)s: %%(message)s'
1203
% server_settings["servicename"]))
1684
(u'Mandos (%s) [%%(process)d]:'
1685
u' %%(levelname)s: %%(message)s'
1686
% server_settings[u"servicename"]))
1205
1688
# Parse config file with clients
1206
client_defaults = { "timeout": "1h",
1208
"checker": "fping -q -- %%(host)s",
1689
client_defaults = { u"timeout": u"1h",
1691
u"checker": u"fping -q -- %%(host)s",
1693
u"approved_delay": u"0s",
1694
u"approved_duration": u"1s",
1211
client_config = ConfigParser.SafeConfigParser(client_defaults)
1212
client_config.read(os.path.join(server_settings["configdir"],
1696
client_config = configparser.SafeConfigParser(client_defaults)
1697
client_config.read(os.path.join(server_settings[u"configdir"],
1215
1700
global mandos_dbus_service
1216
1701
mandos_dbus_service = None
1219
tcp_server = IPv6_TCPServer((server_settings["address"],
1220
server_settings["port"]),
1223
server_settings["interface"],
1227
server_settings["priority"],
1229
pidfilename = "/var/run/mandos.pid"
1703
tcp_server = MandosServer((server_settings[u"address"],
1704
server_settings[u"port"]),
1706
interface=server_settings[u"interface"],
1709
server_settings[u"priority"],
1711
pidfilename = u"/var/run/mandos.pid"
1231
pidfile = open(pidfilename, "w")
1713
pidfile = open(pidfilename, u"w")
1232
1714
except IOError:
1233
logger.error("Could not open file %r", pidfilename)
1715
logger.error(u"Could not open file %r", pidfilename)
1236
uid = pwd.getpwnam("_mandos").pw_uid
1237
gid = pwd.getpwnam("_mandos").pw_gid
1718
uid = pwd.getpwnam(u"_mandos").pw_uid
1719
gid = pwd.getpwnam(u"_mandos").pw_gid
1238
1720
except KeyError:
1240
uid = pwd.getpwnam("mandos").pw_uid
1241
gid = pwd.getpwnam("mandos").pw_gid
1722
uid = pwd.getpwnam(u"mandos").pw_uid
1723
gid = pwd.getpwnam(u"mandos").pw_gid
1242
1724
except KeyError:
1244
uid = pwd.getpwnam("nobody").pw_uid
1245
gid = pwd.getpwnam("nogroup").pw_gid
1726
uid = pwd.getpwnam(u"nobody").pw_uid
1727
gid = pwd.getpwnam(u"nobody").pw_gid
1246
1728
except KeyError:
1262
1744
@gnutls.library.types.gnutls_log_func
1263
1745
def debug_gnutls(level, string):
1264
logger.debug("GnuTLS: %s", string[:-1])
1746
logger.debug(u"GnuTLS: %s", string[:-1])
1266
1748
(gnutls.library.functions
1267
1749
.gnutls_global_set_log_function(debug_gnutls))
1270
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1271
service = AvahiService(name = server_settings["servicename"],
1272
servicetype = "_mandos._tcp",
1273
protocol = protocol)
1274
if server_settings["interface"]:
1275
service.interface = (if_nametoindex
1276
(server_settings["interface"]))
1278
1751
global main_loop
1281
1752
# From the Avahi example code
1282
1753
DBusGMainLoop(set_as_default=True )
1283
1754
main_loop = gobject.MainLoop()
1284
1755
bus = dbus.SystemBus()
1285
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1286
avahi.DBUS_PATH_SERVER),
1287
avahi.DBUS_INTERFACE_SERVER)
1288
1756
# End of Avahi example code
1290
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1759
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1760
bus, do_not_queue=True)
1761
except dbus.exceptions.NameExistsException, e:
1762
logger.error(unicode(e) + u", disabling D-Bus")
1764
server_settings[u"use_dbus"] = False
1765
tcp_server.use_dbus = False
1766
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1767
service = AvahiService(name = server_settings[u"servicename"],
1768
servicetype = u"_mandos._tcp",
1769
protocol = protocol, bus = bus)
1770
if server_settings["interface"]:
1771
service.interface = (if_nametoindex
1772
(str(server_settings[u"interface"])))
1292
1774
client_class = Client
1294
client_class = ClientDBus
1776
client_class = functools.partial(ClientDBus, bus = bus)
1777
def client_config_items(config, section):
1778
special_settings = {
1779
"approved_by_default":
1780
lambda: config.getboolean(section,
1781
"approved_by_default"),
1783
for name, value in config.items(section):
1785
yield (name, special_settings[name]())
1789
tcp_server.clients.update(set(
1296
1790
client_class(name = section,
1297
config= dict(client_config.items(section)))
1791
config= dict(client_config_items(
1792
client_config, section)))
1298
1793
for section in client_config.sections()))
1794
if not tcp_server.clients:
1300
1795
logger.warning(u"No clients defined")
1349
1828
class MandosDBusService(dbus.service.Object):
1350
1829
"""A D-Bus proxy object"""
1351
1830
def __init__(self):
1352
dbus.service.Object.__init__(self, bus, "/")
1831
dbus.service.Object.__init__(self, bus, u"/")
1353
1832
_interface = u"se.bsnet.fukt.Mandos"
1355
@dbus.service.signal(_interface, signature="oa{sv}")
1356
def ClientAdded(self, objpath, properties):
1360
@dbus.service.signal(_interface, signature="s")
1361
def ClientNotFound(self, fingerprint):
1365
@dbus.service.signal(_interface, signature="os")
1834
@dbus.service.signal(_interface, signature=u"o")
1835
def ClientAdded(self, objpath):
1839
@dbus.service.signal(_interface, signature=u"ss")
1840
def ClientNotFound(self, fingerprint, address):
1844
@dbus.service.signal(_interface, signature=u"os")
1366
1845
def ClientRemoved(self, objpath, name):
1370
@dbus.service.method(_interface, out_signature="ao")
1849
@dbus.service.method(_interface, out_signature=u"ao")
1371
1850
def GetAllClients(self):
1373
return dbus.Array(c.dbus_object_path for c in clients)
1852
return dbus.Array(c.dbus_object_path
1853
for c in tcp_server.clients)
1375
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1855
@dbus.service.method(_interface,
1856
out_signature=u"a{oa{sv}}")
1376
1857
def GetAllClientsWithProperties(self):
1378
1859
return dbus.Dictionary(
1379
((c.dbus_object_path, c.GetAllProperties())
1860
((c.dbus_object_path, c.GetAll(u""))
1861
for c in tcp_server.clients),
1862
signature=u"oa{sv}")
1383
@dbus.service.method(_interface, in_signature="o")
1864
@dbus.service.method(_interface, in_signature=u"o")
1384
1865
def RemoveClient(self, object_path):
1867
for c in tcp_server.clients:
1387
1868
if c.dbus_object_path == object_path:
1869
tcp_server.clients.remove(c)
1389
1870
c.remove_from_connection()
1390
1871
# Don't signal anything except ClientRemoved
1391
c.disable(signal=False)
1872
c.disable(quiet=True)
1392
1873
# Emit D-Bus signal
1393
1874
self.ClientRemoved(object_path, c.name)
1876
raise KeyError(object_path)
1399
1880
mandos_dbus_service = MandosDBusService()
1401
for client in clients:
1883
"Cleanup function; run on exit"
1886
while tcp_server.clients:
1887
client = tcp_server.clients.pop()
1889
client.remove_from_connection()
1890
client.disable_hook = None
1891
# Don't signal anything except ClientRemoved
1892
client.disable(quiet=True)
1895
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1898
atexit.register(cleanup)
1900
for client in tcp_server.clients:
1403
1902
# Emit D-Bus signal
1404
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1405
client.GetAllProperties())
1903
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1406
1904
client.enable()
1408
1906
tcp_server.enable()