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())
235
# approved_by_default (Config option for each client)
236
# approved_delay (config option for each client)
237
# approved_duration (config option for each client)
193
238
class Client(object):
194
239
"""A representation of a client host served by this server.
206
251
last_checked_ok: datetime.datetime(); (UTC) or None
207
252
timeout: datetime.timedelta(); How long from last_checked_ok
208
until this client is invalid
253
until this client is disabled
209
254
interval: datetime.timedelta(); How often to start a new checker
210
255
disable_hook: If set, called by disable() as disable_hook(self)
211
256
checker: subprocess.Popen(); a running checker process used
212
257
to see if the client lives.
213
258
'None' if no process is running.
214
259
checker_initiator_tag: a gobject event source tag, or None
215
disable_initiator_tag: - '' -
260
disable_initiator_tag: - '' -
216
261
checker_callback_tag: - '' -
217
262
checker_command: string; External command which is run to check if
218
263
client lives. %() expansions are done at
219
264
runtime with vars(self) as dict, so that for
220
265
instance %(name)s can be used in the command.
221
266
current_checker_command: string; current running checker_command
267
approved_delay: datetime.timedelta(); Time to wait for approval
268
_approved: bool(); 'None' if not yet approved/disapproved
269
approved_duration: datetime.timedelta(); Duration of one approval
273
def _timedelta_to_milliseconds(td):
274
"Convert a datetime.timedelta() to milliseconds"
275
return ((td.days * 24 * 60 * 60 * 1000)
276
+ (td.seconds * 1000)
277
+ (td.microseconds // 1000))
223
279
def timeout_milliseconds(self):
224
280
"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))
281
return self._timedelta_to_milliseconds(self.timeout)
229
283
def interval_milliseconds(self):
230
284
"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))
285
return self._timedelta_to_milliseconds(self.interval)
287
def approved_delay_milliseconds(self):
288
return self._timedelta_to_milliseconds(self.approved_delay)
235
290
def __init__(self, name = None, disable_hook=None, config=None):
236
291
"""Note: the 'checker' key in 'config' sets the
243
298
# Uppercase and remove spaces from fingerprint for later
244
299
# comparison purposes with return value from the fingerprint()
246
self.fingerprint = (config["fingerprint"].upper()
301
self.fingerprint = (config[u"fingerprint"].upper()
247
302
.replace(u" ", u""))
248
303
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:
304
if u"secret" in config:
305
self.secret = config[u"secret"].decode(u"base64")
306
elif u"secfile" in config:
307
with open(os.path.expanduser(os.path.expandvars
308
(config[u"secfile"])),
255
310
self.secret = secfile.read()
312
#XXX Need to allow secret on demand!
257
313
raise TypeError(u"No secret or secfile for client %s"
259
self.host = config.get("host", "")
315
self.host = config.get(u"host", u"")
260
316
self.created = datetime.datetime.utcnow()
261
317
self.enabled = False
262
318
self.last_enabled = None
263
319
self.last_checked_ok = None
264
self.timeout = string_to_delta(config["timeout"])
265
self.interval = string_to_delta(config["interval"])
320
self.timeout = string_to_delta(config[u"timeout"])
321
self.interval = string_to_delta(config[u"interval"])
266
322
self.disable_hook = disable_hook
267
323
self.checker = None
268
324
self.checker_initiator_tag = None
269
325
self.disable_initiator_tag = None
270
326
self.checker_callback_tag = None
271
self.checker_command = config["checker"]
327
self.checker_command = config[u"checker"]
272
328
self.current_checker_command = None
273
329
self.last_connect = None
330
self._approved = None
331
self.approved_by_default = config.get(u"approved_by_default",
333
self.approved_delay = string_to_delta(
334
config[u"approved_delay"])
335
self.approved_duration = string_to_delta(
336
config[u"approved_duration"])
337
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
339
def send_changedstate(self):
340
self.changedstate.acquire()
341
self.changedstate.notify_all()
342
self.changedstate.release()
275
344
def enable(self):
276
345
"""Start this client's checker and timeout hooks"""
346
if getattr(self, u"enabled", False):
349
self.send_changedstate()
277
350
self.last_enabled = datetime.datetime.utcnow()
278
351
# Schedule a new checker to be started an 'interval' from now,
279
352
# and every interval from then on.
280
353
self.checker_initiator_tag = (gobject.timeout_add
281
354
(self.interval_milliseconds(),
282
355
self.start_checker))
283
# Also start a new checker *right now*.
285
356
# Schedule a disable() when 'timeout' has passed
286
357
self.disable_initiator_tag = (gobject.timeout_add
287
358
(self.timeout_milliseconds(),
289
360
self.enabled = True
361
# Also start a new checker *right now*.
364
def disable(self, quiet=True):
292
365
"""Disable this client."""
293
366
if not getattr(self, "enabled", False):
295
logger.info(u"Disabling client %s", self.name)
296
if getattr(self, "disable_initiator_tag", False):
369
self.send_changedstate()
371
logger.info(u"Disabling client %s", self.name)
372
if getattr(self, u"disable_initiator_tag", False):
297
373
gobject.source_remove(self.disable_initiator_tag)
298
374
self.disable_initiator_tag = None
299
if getattr(self, "checker_initiator_tag", False):
375
if getattr(self, u"checker_initiator_tag", False):
300
376
gobject.source_remove(self.checker_initiator_tag)
301
377
self.checker_initiator_tag = None
302
378
self.stop_checker()
409
493
if self.checker_callback_tag:
410
494
gobject.source_remove(self.checker_callback_tag)
411
495
self.checker_callback_tag = None
412
if getattr(self, "checker", None) is None:
496
if getattr(self, u"checker", None) is None:
414
498
logger.debug(u"Stopping checker for %(name)s", vars(self))
416
500
os.kill(self.checker.pid, signal.SIGTERM)
418
502
#if self.checker.poll() is None:
419
503
# os.kill(self.checker.pid, signal.SIGKILL)
420
504
except OSError, error:
421
505
if error.errno != errno.ESRCH: # No such process
423
507
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):
509
def dbus_service_property(dbus_interface, signature=u"v",
510
access=u"readwrite", byte_arrays=False):
511
"""Decorators for marking methods of a DBusObjectWithProperties to
512
become properties on the D-Bus.
514
The decorated method will be called with no arguments by "Get"
515
and with one argument by "Set".
517
The parameters, where they are supported, are the same as
518
dbus.service.method, except there is only "signature", since the
519
type from Get() and the type sent to Set() is the same.
521
# Encoding deeply encoded byte arrays is not supported yet by the
522
# "Set" method, so we fail early here:
523
if byte_arrays and signature != u"ay":
524
raise ValueError(u"Byte arrays not supported for non-'ay'"
525
u" signature %r" % signature)
527
func._dbus_is_property = True
528
func._dbus_interface = dbus_interface
529
func._dbus_signature = signature
530
func._dbus_access = access
531
func._dbus_name = func.__name__
532
if func._dbus_name.endswith(u"_dbus_property"):
533
func._dbus_name = func._dbus_name[:-14]
534
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
539
class DBusPropertyException(dbus.exceptions.DBusException):
540
"""A base class for D-Bus property-related exceptions
542
def __unicode__(self):
543
return unicode(str(self))
546
class DBusPropertyAccessException(DBusPropertyException):
547
"""A property's access permissions disallows an operation.
552
class DBusPropertyNotFound(DBusPropertyException):
553
"""An attempt was made to access a non-existing property.
558
class DBusObjectWithProperties(dbus.service.Object):
559
"""A D-Bus object with properties.
561
Classes inheriting from this can use the dbus_service_property
562
decorator to expose methods as D-Bus properties. It exposes the
563
standard Get(), Set(), and GetAll() methods on the D-Bus.
567
def _is_dbus_property(obj):
568
return getattr(obj, u"_dbus_is_property", False)
570
def _get_all_dbus_properties(self):
571
"""Returns a generator of (name, attribute) pairs
573
return ((prop._dbus_name, prop)
575
inspect.getmembers(self, self._is_dbus_property))
577
def _get_dbus_property(self, interface_name, property_name):
578
"""Returns a bound method if one exists which is a D-Bus
579
property with the specified name and interface.
581
for name in (property_name,
582
property_name + u"_dbus_property"):
583
prop = getattr(self, name, None)
585
or not self._is_dbus_property(prop)
586
or prop._dbus_name != property_name
587
or (interface_name and prop._dbus_interface
588
and interface_name != prop._dbus_interface)):
592
raise DBusPropertyNotFound(self.dbus_object_path + u":"
593
+ interface_name + u"."
596
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
598
def Get(self, interface_name, property_name):
599
"""Standard D-Bus property Get() method, see D-Bus standard.
601
prop = self._get_dbus_property(interface_name, property_name)
602
if prop._dbus_access == u"write":
603
raise DBusPropertyAccessException(property_name)
605
if not hasattr(value, u"variant_level"):
607
return type(value)(value, variant_level=value.variant_level+1)
609
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
610
def Set(self, interface_name, property_name, value):
611
"""Standard D-Bus property Set() method, see D-Bus standard.
613
prop = self._get_dbus_property(interface_name, property_name)
614
if prop._dbus_access == u"read":
615
raise DBusPropertyAccessException(property_name)
616
if prop._dbus_get_args_options[u"byte_arrays"]:
617
# The byte_arrays option is not supported yet on
618
# signatures other than "ay".
619
if prop._dbus_signature != u"ay":
621
value = dbus.ByteArray(''.join(unichr(byte)
625
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
626
out_signature=u"a{sv}")
627
def GetAll(self, interface_name):
628
"""Standard D-Bus property GetAll() method, see D-Bus
631
Note: Will not include properties with access="write".
634
for name, prop in self._get_all_dbus_properties():
636
and interface_name != prop._dbus_interface):
637
# Interface non-empty but did not match
639
# Ignore write-only properties
640
if prop._dbus_access == u"write":
643
if not hasattr(value, u"variant_level"):
646
all[name] = type(value)(value, variant_level=
647
value.variant_level+1)
648
return dbus.Dictionary(all, signature=u"sv")
650
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
652
path_keyword='object_path',
653
connection_keyword='connection')
654
def Introspect(self, object_path, connection):
655
"""Standard D-Bus method, overloaded to insert property tags.
657
xmlstring = dbus.service.Object.Introspect(self, object_path,
660
document = xml.dom.minidom.parseString(xmlstring)
661
def make_tag(document, name, prop):
662
e = document.createElement(u"property")
663
e.setAttribute(u"name", name)
664
e.setAttribute(u"type", prop._dbus_signature)
665
e.setAttribute(u"access", prop._dbus_access)
667
for if_tag in document.getElementsByTagName(u"interface"):
668
for tag in (make_tag(document, name, prop)
670
in self._get_all_dbus_properties()
671
if prop._dbus_interface
672
== if_tag.getAttribute(u"name")):
673
if_tag.appendChild(tag)
674
# Add the names to the return values for the
675
# "org.freedesktop.DBus.Properties" methods
676
if (if_tag.getAttribute(u"name")
677
== u"org.freedesktop.DBus.Properties"):
678
for cn in if_tag.getElementsByTagName(u"method"):
679
if cn.getAttribute(u"name") == u"Get":
680
for arg in cn.getElementsByTagName(u"arg"):
681
if (arg.getAttribute(u"direction")
683
arg.setAttribute(u"name", u"value")
684
elif cn.getAttribute(u"name") == u"GetAll":
685
for arg in cn.getElementsByTagName(u"arg"):
686
if (arg.getAttribute(u"direction")
688
arg.setAttribute(u"name", u"props")
689
xmlstring = document.toxml(u"utf-8")
691
except (AttributeError, xml.dom.DOMException,
692
xml.parsers.expat.ExpatError), error:
693
logger.error(u"Failed to override Introspection method",
698
class ClientDBus(Client, DBusObjectWithProperties):
437
699
"""A Client class using D-Bus
440
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
702
dbus_object_path: dbus.ObjectPath
703
bus: dbus.SystemBus()
442
705
# dbus.service.Object doesn't use super(), so we can't either.
444
def __init__(self, *args, **kwargs):
707
def __init__(self, bus = None, *args, **kwargs):
445
709
Client.__init__(self, *args, **kwargs)
446
710
# Only now, when this client is initialized, can it show up on
448
712
self.dbus_object_path = (dbus.ObjectPath
450
+ self.name.replace(".", "_")))
451
dbus.service.Object.__init__(self, bus,
452
self.dbus_object_path)
714
+ self.name.replace(u".", u"_")))
715
DBusObjectWithProperties.__init__(self, self.bus,
716
self.dbus_object_path)
719
def _datetime_to_dbus(dt, variant_level=0):
720
"""Convert a UTC datetime.datetime() to a D-Bus type."""
721
return dbus.String(dt.isoformat(),
722
variant_level=variant_level)
453
724
def enable(self):
454
oldstate = getattr(self, "enabled", False)
725
oldstate = getattr(self, u"enabled", False)
455
726
r = Client.enable(self)
456
727
if oldstate != self.enabled:
457
728
# Emit D-Bus signals
458
729
self.PropertyChanged(dbus.String(u"enabled"),
459
730
dbus.Boolean(True, variant_level=1))
460
self.PropertyChanged(dbus.String(u"last_enabled"),
461
(_datetime_to_dbus(self.last_enabled,
731
self.PropertyChanged(
732
dbus.String(u"last_enabled"),
733
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:
737
def disable(self, quiet = False):
738
oldstate = getattr(self, u"enabled", False)
739
r = Client.disable(self, quiet=quiet)
740
if not quiet and oldstate != self.enabled:
469
741
# Emit D-Bus signal
470
742
self.PropertyChanged(dbus.String(u"enabled"),
471
743
dbus.Boolean(False, variant_level=1))
524
796
# Emit D-Bus signal
525
797
self.CheckerStarted(self.current_checker_command)
526
798
self.PropertyChanged(
527
dbus.String("checker_running"),
799
dbus.String(u"checker_running"),
528
800
dbus.Boolean(True, variant_level=1))
531
803
def stop_checker(self, *args, **kwargs):
532
old_checker = getattr(self, "checker", None)
804
old_checker = getattr(self, u"checker", None)
533
805
r = Client.stop_checker(self, *args, **kwargs)
534
806
if (old_checker is not None
535
and getattr(self, "checker", None) is None):
807
and getattr(self, u"checker", None) is None):
536
808
self.PropertyChanged(dbus.String(u"checker_running"),
537
809
dbus.Boolean(False, variant_level=1))
540
## D-Bus methods & signals
812
def _reset_approved(self):
813
self._approved = None
816
def approve(self, value=True):
817
self._approved = value
818
gobject.timeout_add(self._timedelta_to_milliseconds(self.approved_duration, self._reset_approved))
820
## D-Bus methods, signals & properties
541
821
_interface = u"se.bsnet.fukt.Mandos.Client"
544
CheckedOK = dbus.service.method(_interface)(checked_ok)
545
CheckedOK.__name__ = "CheckedOK"
547
825
# CheckerCompleted - signal
548
@dbus.service.signal(_interface, signature="nxs")
826
@dbus.service.signal(_interface, signature=u"nxs")
549
827
def CheckerCompleted(self, exitcode, waitstatus, command):
553
831
# CheckerStarted - signal
554
@dbus.service.signal(_interface, signature="s")
832
@dbus.service.signal(_interface, signature=u"s")
555
833
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
837
# PropertyChanged - signal
607
@dbus.service.signal(_interface, signature="sv")
838
@dbus.service.signal(_interface, signature=u"sv")
608
839
def PropertyChanged(self, property, value):
612
# ReceivedSecret - signal
613
844
@dbus.service.signal(_interface)
614
def ReceivedSecret(self):
618
849
# 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(),
850
@dbus.service.signal(_interface, signature=u"s")
851
def Rejected(self, reason):
855
# NeedApproval - signal
856
@dbus.service.signal(_interface, signature=u"db")
857
def NeedApproval(self, timeout, default):
864
@dbus.service.method(_interface, in_signature=u"b")
865
def Approve(self, value):
869
@dbus.service.method(_interface)
871
return self.checked_ok()
668
873
# Enable - method
669
Enable = dbus.service.method(_interface)(enable)
670
Enable.__name__ = "Enable"
874
@dbus.service.method(_interface)
672
879
# StartChecker - method
673
880
@dbus.service.method(_interface)
684
891
# StopChecker - method
685
StopChecker = dbus.service.method(_interface)(stop_checker)
686
StopChecker.__name__ = "StopChecker"
892
@dbus.service.method(_interface)
893
def StopChecker(self):
898
# xxx 3 new properties
901
@dbus_service_property(_interface, signature=u"s", access=u"read")
902
def name_dbus_property(self):
903
return dbus.String(self.name)
905
# fingerprint - property
906
@dbus_service_property(_interface, signature=u"s", access=u"read")
907
def fingerprint_dbus_property(self):
908
return dbus.String(self.fingerprint)
911
@dbus_service_property(_interface, signature=u"s",
913
def host_dbus_property(self, value=None):
914
if value is None: # get
915
return dbus.String(self.host)
918
self.PropertyChanged(dbus.String(u"host"),
919
dbus.String(value, variant_level=1))
922
@dbus_service_property(_interface, signature=u"s", access=u"read")
923
def created_dbus_property(self):
924
return dbus.String(self._datetime_to_dbus(self.created))
926
# last_enabled - property
927
@dbus_service_property(_interface, signature=u"s", access=u"read")
928
def last_enabled_dbus_property(self):
929
if self.last_enabled is None:
930
return dbus.String(u"")
931
return dbus.String(self._datetime_to_dbus(self.last_enabled))
934
@dbus_service_property(_interface, signature=u"b",
936
def enabled_dbus_property(self, value=None):
937
if value is None: # get
938
return dbus.Boolean(self.enabled)
944
# last_checked_ok - property
945
@dbus_service_property(_interface, signature=u"s",
947
def last_checked_ok_dbus_property(self, value=None):
948
if value is not None:
951
if self.last_checked_ok is None:
952
return dbus.String(u"")
953
return dbus.String(self._datetime_to_dbus(self
957
@dbus_service_property(_interface, signature=u"t",
959
def timeout_dbus_property(self, value=None):
960
if value is None: # get
961
return dbus.UInt64(self.timeout_milliseconds())
962
self.timeout = datetime.timedelta(0, 0, 0, value)
964
self.PropertyChanged(dbus.String(u"timeout"),
965
dbus.UInt64(value, variant_level=1))
966
if getattr(self, u"disable_initiator_tag", None) is None:
969
gobject.source_remove(self.disable_initiator_tag)
970
self.disable_initiator_tag = None
972
_timedelta_to_milliseconds((self
978
# The timeout has passed
981
self.disable_initiator_tag = (gobject.timeout_add
982
(time_to_die, self.disable))
984
# interval - property
985
@dbus_service_property(_interface, signature=u"t",
987
def interval_dbus_property(self, value=None):
988
if value is None: # get
989
return dbus.UInt64(self.interval_milliseconds())
990
self.interval = datetime.timedelta(0, 0, 0, value)
992
self.PropertyChanged(dbus.String(u"interval"),
993
dbus.UInt64(value, variant_level=1))
994
if getattr(self, u"checker_initiator_tag", None) is None:
996
# Reschedule checker run
997
gobject.source_remove(self.checker_initiator_tag)
998
self.checker_initiator_tag = (gobject.timeout_add
999
(value, self.start_checker))
1000
self.start_checker() # Start one now, too
1002
# checker - property
1003
@dbus_service_property(_interface, signature=u"s",
1004
access=u"readwrite")
1005
def checker_dbus_property(self, value=None):
1006
if value is None: # get
1007
return dbus.String(self.checker_command)
1008
self.checker_command = value
1010
self.PropertyChanged(dbus.String(u"checker"),
1011
dbus.String(self.checker_command,
1014
# checker_running - property
1015
@dbus_service_property(_interface, signature=u"b",
1016
access=u"readwrite")
1017
def checker_running_dbus_property(self, value=None):
1018
if value is None: # get
1019
return dbus.Boolean(self.checker is not None)
1021
self.start_checker()
1025
# object_path - property
1026
@dbus_service_property(_interface, signature=u"o", access=u"read")
1027
def object_path_dbus_property(self):
1028
return self.dbus_object_path # is already a dbus.ObjectPath
1031
@dbus_service_property(_interface, signature=u"ay",
1032
access=u"write", byte_arrays=True)
1033
def secret_dbus_property(self, value):
1034
self.secret = str(value)
691
class ClientHandler(SocketServer.BaseRequestHandler, object):
1039
class ProxyClient(object):
1040
def __init__(self, child_pipe, fpr, address):
1041
self._pipe = child_pipe
1042
self._pipe.send(('init', fpr, address))
1043
if not self._pipe.recv():
1046
def __getattribute__(self, name):
1047
if(name == '_pipe'):
1048
return super(ProxyClient, self).__getattribute__(name)
1049
self._pipe.send(('getattr', name))
1050
data = self._pipe.recv()
1051
if data[0] == 'data':
1053
if data[0] == 'function':
1054
def func(*args, **kwargs):
1055
self._pipe.send(('funcall', name, args, kwargs))
1056
return self._pipe.recv()[1]
1059
def __setattr__(self, name, value):
1060
if(name == '_pipe'):
1061
return super(ProxyClient, self).__setattr__(name, value)
1062
self._pipe.send(('setattr', name, value))
1065
class ClientHandler(socketserver.BaseRequestHandler, object):
692
1066
"""A class to handle client connections.
694
1068
Instantiated once for each connection to handle it.
695
1069
Note: This will run in its own forked process."""
697
1071
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:
1072
with contextlib.closing(self.server.child_pipe) as child_pipe:
1073
logger.info(u"TCP connection from: %s",
1074
unicode(self.client_address))
1075
logger.debug(u"Pipe FD: %d",
1076
self.server.child_pipe.fileno())
703
1078
session = (gnutls.connection
704
1079
.ClientSession(self.request,
705
1080
gnutls.connection
706
1081
.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
1083
# Note: gnutls.connection.X509Credentials is really a
718
1084
# generic GnuTLS certificate credentials object so long as
719
1085
# no X.509 keys are added to it. Therefore, we can use it
720
1086
# here despite using OpenPGP certificates.
722
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
723
# "+AES-256-CBC", "+SHA1",
724
# "+COMP-NULL", "+CTYPE-OPENPGP",
1088
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1089
# u"+AES-256-CBC", u"+SHA1",
1090
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
726
1092
# Use a fallback default, since this MUST be set.
727
1093
priority = self.server.gnutls_priority
728
1094
if priority is None:
1095
priority = u"NORMAL"
730
1096
(gnutls.library.functions
731
1097
.gnutls_priority_set_direct(session._c_object,
732
1098
priority, None))
1100
# Start communication using the Mandos protocol
1101
# Get protocol number
1102
line = self.request.makefile().readline()
1103
logger.debug(u"Protocol version: %r", line)
1105
if int(line.strip().split()[0]) > 1:
1107
except (ValueError, IndexError, RuntimeError), error:
1108
logger.error(u"Unknown protocol version: %s", error)
1111
# Start GnuTLS connection
735
1113
session.handshake()
736
1114
except gnutls.errors.GNUTLSError, error:
741
1119
logger.debug(u"Handshake succeeded")
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)
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))
1122
fpr = self.fingerprint(self.peer_certificate
1124
except (TypeError, gnutls.errors.GNUTLSError), error:
1125
logger.warning(u"Bad certificate: %s", error)
1127
logger.debug(u"Fingerprint: %s", fpr)
1130
client = ProxyClient(child_pipe, fpr,
1131
self.client_address)
1135
delay = client.approved_delay
1137
if not client.enabled:
1138
logger.warning(u"Client %s is disabled",
1140
if self.server.use_dbus:
1142
client.Rejected("Disabled")
1144
if client._approved is None:
1145
logger.info(u"Client %s need approval",
1147
if self.server.use_dbus:
1149
client.NeedApproval(
1150
client.approved_delay_milliseconds(),
1151
client.approved_by_default)
1152
elif client._approved:
1153
#We have a password and are approved
1156
logger.warning(u"Client %s was not approved",
1158
if self.server.use_dbus:
1160
client.Rejected("Disapproved")
1163
#wait until timeout or approved
1164
#x = float(client._timedelta_to_milliseconds(delay))
1165
time = datetime.datetime.now()
1166
client.changedstate.acquire()
1167
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1168
client.changedstate.release()
1169
time2 = datetime.datetime.now()
1170
if (time2 - time) >= delay:
1171
if not client.approved_by_default:
1172
logger.warning("Client %s timed out while"
1173
" waiting for approval",
1175
if self.server.use_dbus:
1177
client.Rejected("Time out")
1182
delay -= time2 - time
1185
while sent_size < len(client.secret):
1186
# XXX handle session exception
1187
sent = session.send(client.secret[sent_size:])
1188
logger.debug(u"Sent: %d, remaining: %d",
1189
sent, len(client.secret)
1190
- (sent_size + sent))
1193
logger.info(u"Sending secret to %s", client.name)
1194
# bump the timeout as if seen
1196
if self.server.use_dbus:
776
1204
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.
1267
class MultiprocessingMixIn(object):
1268
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1269
def sub_process_main(self, request, address):
1271
self.finish_request(request, address)
1273
self.handle_error(request, address)
1274
self.close_request(request)
1276
def process_request(self, request, address):
1277
"""Start a new process to process the request."""
1278
multiprocessing.Process(target = self.sub_process_main,
1279
args = (request, address)).start()
1281
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1282
""" adds a pipe to the MixIn """
844
1283
def process_request(self, request, client_address):
845
1284
"""Overrides and wraps the original process_request().
847
This function creates a new pipe in self.pipe
1286
This function creates a new pipe in self.pipe
849
self.pipe = os.pipe()
850
super(ForkingMixInWithPipe,
1288
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1290
super(MultiprocessingMixInWithPipe,
851
1291
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):
1292
self.add_pipe(parent_pipe)
1293
def add_pipe(self, parent_pipe):
858
1294
"""Dummy function; override as necessary"""
863
class IPv6_TCPServer(ForkingMixInWithPipe,
864
SocketServer.TCPServer, object):
1297
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1298
socketserver.TCPServer, object):
865
1299
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
868
1302
enabled: Boolean; whether this server is activated yet
869
1303
interface: None or a network interface name (string)
870
1304
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
1306
def __init__(self, server_address, RequestHandlerClass,
877
interface=None, use_ipv6=True, clients=None,
878
gnutls_priority=None, use_dbus=True):
1307
interface=None, use_ipv6=True):
880
1308
self.interface = interface
882
1310
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,
1311
socketserver.TCPServer.__init__(self, server_address,
887
1312
RequestHandlerClass)
888
1313
def server_bind(self):
889
1314
"""This overrides the normal server_bind() function
890
1315
to bind to an interface if one was specified, and also NOT to
891
1316
bind to an address or port if they were not specified."""
892
1317
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",
1318
if SO_BINDTODEVICE is None:
1319
logger.error(u"SO_BINDTODEVICE does not exist;"
1320
u" cannot bind to interface %s",
1324
self.socket.setsockopt(socket.SOL_SOCKET,
1328
except socket.error, error:
1329
if error[0] == errno.EPERM:
1330
logger.error(u"No permission to"
1331
u" bind to interface %s",
1333
elif error[0] == errno.ENOPROTOOPT:
1334
logger.error(u"SO_BINDTODEVICE not available;"
1335
u" cannot bind to interface %s",
904
1339
# Only bind(2) the socket if we really need to.
905
1340
if self.server_address[0] or self.server_address[1]:
906
1341
if not self.server_address[0]:
907
1342
if self.address_family == socket.AF_INET6:
908
any_address = "::" # in6addr_any
1343
any_address = u"::" # in6addr_any
910
1345
any_address = socket.INADDR_ANY
911
1346
self.server_address = (any_address,
920
1355
# if_nametoindex
921
1356
# (self.interface))
922
return SocketServer.TCPServer.server_bind(self)
1357
return socketserver.TCPServer.server_bind(self)
1360
class MandosServer(IPv6_TCPServer):
1364
clients: set of Client objects
1365
gnutls_priority GnuTLS priority string
1366
use_dbus: Boolean; to emit D-Bus signals or not
1368
Assumes a gobject.MainLoop event loop.
1370
def __init__(self, server_address, RequestHandlerClass,
1371
interface=None, use_ipv6=True, clients=None,
1372
gnutls_priority=None, use_dbus=True):
1373
self.enabled = False
1374
self.clients = clients
1375
if self.clients is None:
1376
self.clients = set()
1377
self.use_dbus = use_dbus
1378
self.gnutls_priority = gnutls_priority
1379
IPv6_TCPServer.__init__(self, server_address,
1380
RequestHandlerClass,
1381
interface = interface,
1382
use_ipv6 = use_ipv6)
923
1383
def server_activate(self):
924
1384
if self.enabled:
925
return SocketServer.TCPServer.server_activate(self)
1385
return socketserver.TCPServer.server_activate(self)
926
1386
def enable(self):
927
1387
self.enabled = True
928
def handle_ipc(self, source, condition, file_objects={}):
1388
def add_pipe(self, parent_pipe):
1389
# Call "handle_ipc" for both data and EOF events
1390
gobject.io_add_watch(parent_pipe.fileno(),
1391
gobject.IO_IN | gobject.IO_HUP,
1392
functools.partial(self.handle_ipc,
1393
parent_pipe = parent_pipe))
1395
def handle_ipc(self, source, condition, parent_pipe=None,
1396
client_object=None):
929
1397
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
1398
gobject.IO_IN: u"IN", # There is data to read.
1399
gobject.IO_OUT: u"OUT", # Data can be written (without
1401
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1402
gobject.IO_ERR: u"ERR", # Error condition.
1403
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1404
# broken, usually for pipes and
939
1407
conditions_string = ' | '.join(name
940
1408
for cond, name in
941
1409
condition_names.iteritems()
942
1410
if cond & condition)
943
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1411
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
944
1412
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)
1414
# Read a request from the child
1415
request = parent_pipe.recv()
1416
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
1418
if command == 'init':
1420
address = request[2]
1422
for c in self.clients:
1423
if c.fingerprint == fpr:
1427
logger.warning(u"Client not found for fingerprint: %s, ad"
1428
u"dress: %s", fpr, address)
1431
mandos_dbus_service.ClientNotFound(fpr, address)
1432
parent_pipe.send(False)
1435
gobject.io_add_watch(parent_pipe.fileno(),
1436
gobject.IO_IN | gobject.IO_HUP,
1437
functools.partial(self.handle_ipc,
1438
parent_pipe = parent_pipe,
1439
client_object = client))
1440
parent_pipe.send(True)
1441
# 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
1443
if command == 'funcall':
1444
funcname = request[1]
1448
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1450
if command == 'getattr':
1451
attrname = request[1]
1452
if callable(client_object.__getattribute__(attrname)):
1453
parent_pipe.send(('function',))
1455
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1457
if command == 'setattr':
1458
attrname = request[1]
1460
setattr(client_object, attrname, value)
1000
1465
def string_to_delta(interval):
1001
1466
"""Parse a string and return a datetime.timedelta
1003
>>> string_to_delta('7d')
1468
>>> string_to_delta(u'7d')
1004
1469
datetime.timedelta(7)
1005
>>> string_to_delta('60s')
1470
>>> string_to_delta(u'60s')
1006
1471
datetime.timedelta(0, 60)
1007
>>> string_to_delta('60m')
1472
>>> string_to_delta(u'60m')
1008
1473
datetime.timedelta(0, 3600)
1009
>>> string_to_delta('24h')
1474
>>> string_to_delta(u'24h')
1010
1475
datetime.timedelta(1)
1011
1476
>>> string_to_delta(u'1w')
1012
1477
datetime.timedelta(7)
1013
>>> string_to_delta('5m 30s')
1478
>>> string_to_delta(u'5m 30s')
1014
1479
datetime.timedelta(0, 330)
1016
1481
timevalue = datetime.timedelta(0)
1029
1494
elif suffix == u"w":
1030
1495
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1033
except (ValueError, IndexError):
1497
raise ValueError(u"Unknown suffix %r" % suffix)
1498
except (ValueError, IndexError), e:
1499
raise ValueError(e.message)
1035
1500
timevalue += delta
1036
1501
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
1504
def if_nametoindex(interface):
1063
"""Call the C function if_nametoindex(), or equivalent"""
1505
"""Call the C function if_nametoindex(), or equivalent
1507
Note: This function cannot accept a unicode string."""
1064
1508
global if_nametoindex
1066
1510
if_nametoindex = (ctypes.cdll.LoadLibrary
1067
(ctypes.util.find_library("c"))
1511
(ctypes.util.find_library(u"c"))
1068
1512
.if_nametoindex)
1069
1513
except (OSError, AttributeError):
1070
if "struct" not in sys.modules:
1072
if "fcntl" not in sys.modules:
1514
logger.warning(u"Doing if_nametoindex the hard way")
1074
1515
def if_nametoindex(interface):
1075
1516
"Get an interface index the hard way, i.e. using fcntl()"
1076
1517
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1077
with closing(socket.socket()) as s:
1518
with contextlib.closing(socket.socket()) as s:
1078
1519
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1079
struct.pack("16s16x", interface))
1080
interface_index = struct.unpack("I", ifreq[16:20])[0]
1520
struct.pack(str(u"16s16x"),
1522
interface_index = struct.unpack(str(u"I"),
1081
1524
return interface_index
1082
1525
return if_nametoindex(interface)
1111
######################################################################
1555
##################################################################
1112
1556
# Parsing of options, both command line and config file
1114
1558
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")
1559
parser.add_option("-i", u"--interface", type=u"string",
1560
metavar="IF", help=u"Bind to interface IF")
1561
parser.add_option("-a", u"--address", type=u"string",
1562
help=u"Address to listen for requests on")
1563
parser.add_option("-p", u"--port", type=u"int",
1564
help=u"Port number to receive requests on")
1565
parser.add_option("--check", action=u"store_true",
1566
help=u"Run self-test")
1567
parser.add_option("--debug", action=u"store_true",
1568
help=u"Debug mode; run in foreground and log to"
1570
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1571
u" priority string (see GnuTLS documentation)")
1572
parser.add_option("--servicename", type=u"string",
1573
metavar=u"NAME", help=u"Zeroconf service name")
1574
parser.add_option("--configdir", type=u"string",
1575
default=u"/etc/mandos", metavar=u"DIR",
1576
help=u"Directory to search for configuration"
1578
parser.add_option("--no-dbus", action=u"store_false",
1579
dest=u"use_dbus", help=u"Do not provide D-Bus"
1580
u" system bus interface")
1581
parser.add_option("--no-ipv6", action=u"store_false",
1582
dest=u"use_ipv6", help=u"Do not use IPv6")
1140
1583
options = parser.parse_args()[0]
1142
1585
if options.check:
1147
1590
# Default values for config file for server-global settings
1148
server_defaults = { "interface": "",
1153
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1154
"servicename": "Mandos",
1591
server_defaults = { u"interface": u"",
1596
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1597
u"servicename": u"Mandos",
1598
u"use_dbus": u"True",
1599
u"use_ipv6": u"True",
1159
1602
# Parse config file for server-global settings
1160
server_config = ConfigParser.SafeConfigParser(server_defaults)
1603
server_config = configparser.SafeConfigParser(server_defaults)
1161
1604
del server_defaults
1162
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1605
server_config.read(os.path.join(options.configdir,
1163
1607
# Convert the SafeConfigParser object to a dict
1164
1608
server_settings = server_config.defaults()
1165
1609
# 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",
1610
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1611
server_settings[option] = server_config.getboolean(u"DEFAULT",
1172
1613
if server_settings["port"]:
1173
server_settings["port"] = server_config.getint("DEFAULT",
1614
server_settings["port"] = server_config.getint(u"DEFAULT",
1175
1616
del server_config
1177
1618
# Override the settings from the config file with command line
1178
1619
# options, if set.
1179
for option in ("interface", "address", "port", "debug",
1180
"priority", "servicename", "configdir",
1181
"use_dbus", "use_ipv6"):
1620
for option in (u"interface", u"address", u"port", u"debug",
1621
u"priority", u"servicename", u"configdir",
1622
u"use_dbus", u"use_ipv6"):
1182
1623
value = getattr(options, option)
1183
1624
if value is not None:
1184
1625
server_settings[option] = value
1627
# Force all strings to be unicode
1628
for option in server_settings.keys():
1629
if type(server_settings[option]) is str:
1630
server_settings[option] = unicode(server_settings[option])
1186
1631
# Now we have our good server settings in "server_settings"
1188
1633
##################################################################
1190
1635
# For convenience
1191
debug = server_settings["debug"]
1192
use_dbus = server_settings["use_dbus"]
1193
use_ipv6 = server_settings["use_ipv6"]
1636
debug = server_settings[u"debug"]
1637
use_dbus = server_settings[u"use_dbus"]
1638
use_ipv6 = server_settings[u"use_ipv6"]
1196
1641
syslogger.setLevel(logging.WARNING)
1197
1642
console.setLevel(logging.WARNING)
1199
if server_settings["servicename"] != "Mandos":
1644
if server_settings[u"servicename"] != u"Mandos":
1200
1645
syslogger.setFormatter(logging.Formatter
1201
('Mandos (%s) [%%(process)d]:'
1202
' %%(levelname)s: %%(message)s'
1203
% server_settings["servicename"]))
1646
(u'Mandos (%s) [%%(process)d]:'
1647
u' %%(levelname)s: %%(message)s'
1648
% server_settings[u"servicename"]))
1205
1650
# Parse config file with clients
1206
client_defaults = { "timeout": "1h",
1208
"checker": "fping -q -- %%(host)s",
1651
client_defaults = { u"timeout": u"1h",
1653
u"checker": u"fping -q -- %%(host)s",
1655
u"approved_delay": u"5m",
1656
u"approved_duration": u"1s",
1211
client_config = ConfigParser.SafeConfigParser(client_defaults)
1212
client_config.read(os.path.join(server_settings["configdir"],
1658
client_config = configparser.SafeConfigParser(client_defaults)
1659
client_config.read(os.path.join(server_settings[u"configdir"],
1215
1662
global mandos_dbus_service
1216
1663
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"
1665
tcp_server = MandosServer((server_settings[u"address"],
1666
server_settings[u"port"]),
1668
interface=server_settings[u"interface"],
1671
server_settings[u"priority"],
1673
pidfilename = u"/var/run/mandos.pid"
1231
pidfile = open(pidfilename, "w")
1675
pidfile = open(pidfilename, u"w")
1232
1676
except IOError:
1233
logger.error("Could not open file %r", pidfilename)
1677
logger.error(u"Could not open file %r", pidfilename)
1236
uid = pwd.getpwnam("_mandos").pw_uid
1237
gid = pwd.getpwnam("_mandos").pw_gid
1680
uid = pwd.getpwnam(u"_mandos").pw_uid
1681
gid = pwd.getpwnam(u"_mandos").pw_gid
1238
1682
except KeyError:
1240
uid = pwd.getpwnam("mandos").pw_uid
1241
gid = pwd.getpwnam("mandos").pw_gid
1684
uid = pwd.getpwnam(u"mandos").pw_uid
1685
gid = pwd.getpwnam(u"mandos").pw_gid
1242
1686
except KeyError:
1244
uid = pwd.getpwnam("nobody").pw_uid
1245
gid = pwd.getpwnam("nogroup").pw_gid
1688
uid = pwd.getpwnam(u"nobody").pw_uid
1689
gid = pwd.getpwnam(u"nobody").pw_gid
1246
1690
except KeyError:
1262
1706
@gnutls.library.types.gnutls_log_func
1263
1707
def debug_gnutls(level, string):
1264
logger.debug("GnuTLS: %s", string[:-1])
1708
logger.debug(u"GnuTLS: %s", string[:-1])
1266
1710
(gnutls.library.functions
1267
1711
.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
1713
global main_loop
1281
1714
# From the Avahi example code
1282
1715
DBusGMainLoop(set_as_default=True )
1283
1716
main_loop = gobject.MainLoop()
1284
1717
bus = dbus.SystemBus()
1285
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1286
avahi.DBUS_PATH_SERVER),
1287
avahi.DBUS_INTERFACE_SERVER)
1288
1718
# End of Avahi example code
1290
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1721
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1722
bus, do_not_queue=True)
1723
except dbus.exceptions.NameExistsException, e:
1724
logger.error(unicode(e) + u", disabling D-Bus")
1726
server_settings[u"use_dbus"] = False
1727
tcp_server.use_dbus = False
1728
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1729
service = AvahiService(name = server_settings[u"servicename"],
1730
servicetype = u"_mandos._tcp",
1731
protocol = protocol, bus = bus)
1732
if server_settings["interface"]:
1733
service.interface = (if_nametoindex
1734
(str(server_settings[u"interface"])))
1292
1736
client_class = Client
1294
client_class = ClientDBus
1738
client_class = functools.partial(ClientDBus, bus = bus)
1739
def client_config_items(config, section):
1740
special_settings = {
1741
"approve_by_default":
1742
lambda: config.getboolean(section,
1743
"approve_by_default"),
1745
for name, value in config.items(section):
1747
yield special_settings[name]()
1751
tcp_server.clients.update(set(
1296
1752
client_class(name = section,
1297
config= dict(client_config.items(section)))
1753
config= dict(client_config_items(
1754
client_config, section)))
1298
1755
for section in client_config.sections()))
1756
if not tcp_server.clients:
1300
1757
logger.warning(u"No clients defined")
1349
1790
class MandosDBusService(dbus.service.Object):
1350
1791
"""A D-Bus proxy object"""
1351
1792
def __init__(self):
1352
dbus.service.Object.__init__(self, bus, "/")
1793
dbus.service.Object.__init__(self, bus, u"/")
1353
1794
_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")
1796
@dbus.service.signal(_interface, signature=u"o")
1797
def ClientAdded(self, objpath):
1801
@dbus.service.signal(_interface, signature=u"ss")
1802
def ClientNotFound(self, fingerprint, address):
1806
@dbus.service.signal(_interface, signature=u"os")
1366
1807
def ClientRemoved(self, objpath, name):
1370
@dbus.service.method(_interface, out_signature="ao")
1811
@dbus.service.method(_interface, out_signature=u"ao")
1371
1812
def GetAllClients(self):
1373
return dbus.Array(c.dbus_object_path for c in clients)
1814
return dbus.Array(c.dbus_object_path
1815
for c in tcp_server.clients)
1375
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1817
@dbus.service.method(_interface,
1818
out_signature=u"a{oa{sv}}")
1376
1819
def GetAllClientsWithProperties(self):
1378
1821
return dbus.Dictionary(
1379
((c.dbus_object_path, c.GetAllProperties())
1822
((c.dbus_object_path, c.GetAll(u""))
1823
for c in tcp_server.clients),
1824
signature=u"oa{sv}")
1383
@dbus.service.method(_interface, in_signature="o")
1826
@dbus.service.method(_interface, in_signature=u"o")
1384
1827
def RemoveClient(self, object_path):
1829
for c in tcp_server.clients:
1387
1830
if c.dbus_object_path == object_path:
1831
tcp_server.clients.remove(c)
1389
1832
c.remove_from_connection()
1390
1833
# Don't signal anything except ClientRemoved
1391
c.disable(signal=False)
1834
c.disable(quiet=True)
1392
1835
# Emit D-Bus signal
1393
1836
self.ClientRemoved(object_path, c.name)
1838
raise KeyError(object_path)
1399
1842
mandos_dbus_service = MandosDBusService()
1401
for client in clients:
1845
"Cleanup function; run on exit"
1848
while tcp_server.clients:
1849
client = tcp_server.clients.pop()
1851
client.remove_from_connection()
1852
client.disable_hook = None
1853
# Don't signal anything except ClientRemoved
1854
client.disable(quiet=True)
1857
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1860
atexit.register(cleanup)
1862
for client in tcp_server.clients:
1403
1864
# Emit D-Bus signal
1404
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1405
client.GetAllProperties())
1865
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1406
1866
client.enable()
1408
1868
tcp_server.enable()