156
133
u" after %i retries, exiting.",
157
134
self.rename_count)
158
135
raise AvahiServiceError(u"Too many renames")
159
self.name = self.server.GetAlternativeServiceName(self.name)
136
self.name = server.GetAlternativeServiceName(self.name)
160
137
logger.info(u"Changing Zeroconf service name to %r ...",
162
139
syslogger.setFormatter(logging.Formatter
163
(u'Mandos (%s) [%%(process)d]:'
164
u' %%(levelname)s: %%(message)s'
140
('Mandos (%s) [%%(process)d]:'
141
' %%(levelname)s: %%(message)s'
168
145
self.rename_count += 1
169
146
def remove(self):
170
147
"""Derived from the Avahi example code"""
171
if self.group is not None:
148
if group is not None:
174
151
"""Derived from the Avahi example code"""
175
if self.group is None:
176
self.group = dbus.Interface(
177
self.bus.get_object(avahi.DBUS_NAME,
178
self.server.EntryGroupNew()),
179
avahi.DBUS_INTERFACE_ENTRY_GROUP)
180
self.group.connect_to_signal('StateChanged',
182
.entry_group_state_changed)
154
group = dbus.Interface(bus.get_object
156
server.EntryGroupNew()),
157
avahi.DBUS_INTERFACE_ENTRY_GROUP)
158
group.connect_to_signal('StateChanged',
159
entry_group_state_changed)
183
160
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
184
self.name, self.type)
185
self.group.AddService(
188
dbus.UInt32(0), # flags
189
self.name, self.type,
190
self.domain, self.host,
191
dbus.UInt16(self.port),
192
avahi.string_array_to_txt_array(self.TXT))
194
def entry_group_state_changed(self, state, error):
195
"""Derived from the Avahi example code"""
196
logger.debug(u"Avahi entry group state change: %i", state)
198
if state == avahi.ENTRY_GROUP_ESTABLISHED:
199
logger.debug(u"Zeroconf service established.")
200
elif state == avahi.ENTRY_GROUP_COLLISION:
201
logger.warning(u"Zeroconf service name collision.")
203
elif state == avahi.ENTRY_GROUP_FAILURE:
204
logger.critical(u"Avahi: Error in group state changed %s",
206
raise AvahiGroupError(u"State changed: %s"
209
"""Derived from the Avahi example code"""
210
if self.group is not None:
213
def server_state_changed(self, state):
214
"""Derived from the Avahi example code"""
215
logger.debug(u"Avahi server state change: %i", state)
216
if state == avahi.SERVER_COLLISION:
217
logger.error(u"Zeroconf server name collision")
219
elif state == avahi.SERVER_RUNNING:
222
"""Derived from the Avahi example code"""
223
if self.server is None:
224
self.server = dbus.Interface(
225
self.bus.get_object(avahi.DBUS_NAME,
226
avahi.DBUS_PATH_SERVER),
227
avahi.DBUS_INTERFACE_SERVER)
228
self.server.connect_to_signal(u"StateChanged",
229
self.server_state_changed)
230
self.server_state_changed(self.server.GetState())
161
service.name, service.type)
163
self.interface, # interface
164
self.protocol, # protocol
165
dbus.UInt32(0), # flags
166
self.name, self.type,
167
self.domain, self.host,
168
dbus.UInt16(self.port),
169
avahi.string_array_to_txt_array(self.TXT))
172
# From the Avahi example code:
173
group = None # our entry group
174
# End of Avahi example code
177
def _datetime_to_dbus(dt, variant_level=0):
178
"""Convert a UTC datetime.datetime() to a D-Bus type."""
179
return dbus.String(dt.isoformat(), variant_level=variant_level)
233
182
class Client(object):
234
183
"""A representation of a client host served by this server.
237
185
name: string; from the config file, used in log messages and
238
186
D-Bus identifiers
287
231
# Uppercase and remove spaces from fingerprint for later
288
232
# comparison purposes with return value from the fingerprint()
290
self.fingerprint = (config[u"fingerprint"].upper()
234
self.fingerprint = (config["fingerprint"].upper()
291
235
.replace(u" ", u""))
292
236
logger.debug(u" Fingerprint: %s", self.fingerprint)
293
if u"secret" in config:
294
self.secret = config[u"secret"].decode(u"base64")
295
elif u"secfile" in config:
296
with open(os.path.expanduser(os.path.expandvars
297
(config[u"secfile"])),
237
if "secret" in config:
238
self.secret = config["secret"].decode(u"base64")
239
elif "secfile" in config:
240
with closing(open(os.path.expanduser
242
(config["secfile"])))) as secfile:
299
243
self.secret = secfile.read()
301
245
raise TypeError(u"No secret or secfile for client %s"
303
self.host = config.get(u"host", u"")
247
self.host = config.get("host", "")
304
248
self.created = datetime.datetime.utcnow()
305
249
self.enabled = False
306
250
self.last_enabled = None
307
251
self.last_checked_ok = None
308
self.timeout = string_to_delta(config[u"timeout"])
309
self.interval = string_to_delta(config[u"interval"])
252
self.timeout = string_to_delta(config["timeout"])
253
self.interval = string_to_delta(config["interval"])
310
254
self.disable_hook = disable_hook
311
255
self.checker = None
312
256
self.checker_initiator_tag = None
313
257
self.disable_initiator_tag = None
314
258
self.checker_callback_tag = None
315
self.checker_command = config[u"checker"]
259
self.checker_command = config["checker"]
316
260
self.current_checker_command = None
317
261
self.last_connect = None
319
263
def enable(self):
320
264
"""Start this client's checker and timeout hooks"""
321
if getattr(self, u"enabled", False):
324
265
self.last_enabled = datetime.datetime.utcnow()
325
266
# Schedule a new checker to be started an 'interval' from now,
326
267
# and every interval from then on.
327
268
self.checker_initiator_tag = (gobject.timeout_add
328
269
(self.interval_milliseconds(),
329
270
self.start_checker))
271
# Also start a new checker *right now*.
330
273
# Schedule a disable() when 'timeout' has passed
331
274
self.disable_initiator_tag = (gobject.timeout_add
332
275
(self.timeout_milliseconds(),
334
277
self.enabled = True
335
# Also start a new checker *right now*.
338
def disable(self, quiet=True):
339
280
"""Disable this client."""
340
281
if not getattr(self, "enabled", False):
343
logger.info(u"Disabling client %s", self.name)
344
if getattr(self, u"disable_initiator_tag", False):
283
logger.info(u"Disabling client %s", self.name)
284
if getattr(self, "disable_initiator_tag", False):
345
285
gobject.source_remove(self.disable_initiator_tag)
346
286
self.disable_initiator_tag = None
347
if getattr(self, u"checker_initiator_tag", False):
287
if getattr(self, "checker_initiator_tag", False):
348
288
gobject.source_remove(self.checker_initiator_tag)
349
289
self.checker_initiator_tag = None
350
290
self.stop_checker()
465
395
if self.checker_callback_tag:
466
396
gobject.source_remove(self.checker_callback_tag)
467
397
self.checker_callback_tag = None
468
if getattr(self, u"checker", None) is None:
398
if getattr(self, "checker", None) is None:
470
400
logger.debug(u"Stopping checker for %(name)s", vars(self))
472
402
os.kill(self.checker.pid, signal.SIGTERM)
474
404
#if self.checker.poll() is None:
475
405
# os.kill(self.checker.pid, signal.SIGKILL)
476
406
except OSError, error:
477
407
if error.errno != errno.ESRCH: # No such process
479
409
self.checker = None
482
def dbus_service_property(dbus_interface, signature=u"v",
483
access=u"readwrite", byte_arrays=False):
484
"""Decorators for marking methods of a DBusObjectWithProperties to
485
become properties on the D-Bus.
487
The decorated method will be called with no arguments by "Get"
488
and with one argument by "Set".
490
The parameters, where they are supported, are the same as
491
dbus.service.method, except there is only "signature", since the
492
type from Get() and the type sent to Set() is the same.
494
# Encoding deeply encoded byte arrays is not supported yet by the
495
# "Set" method, so we fail early here:
496
if byte_arrays and signature != u"ay":
497
raise ValueError(u"Byte arrays not supported for non-'ay'"
498
u" signature %r" % signature)
500
func._dbus_is_property = True
501
func._dbus_interface = dbus_interface
502
func._dbus_signature = signature
503
func._dbus_access = access
504
func._dbus_name = func.__name__
505
if func._dbus_name.endswith(u"_dbus_property"):
506
func._dbus_name = func._dbus_name[:-14]
507
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
512
class DBusPropertyException(dbus.exceptions.DBusException):
513
"""A base class for D-Bus property-related exceptions
515
def __unicode__(self):
516
return unicode(str(self))
519
class DBusPropertyAccessException(DBusPropertyException):
520
"""A property's access permissions disallows an operation.
525
class DBusPropertyNotFound(DBusPropertyException):
526
"""An attempt was made to access a non-existing property.
531
class DBusObjectWithProperties(dbus.service.Object):
532
"""A D-Bus object with properties.
534
Classes inheriting from this can use the dbus_service_property
535
decorator to expose methods as D-Bus properties. It exposes the
536
standard Get(), Set(), and GetAll() methods on the D-Bus.
540
def _is_dbus_property(obj):
541
return getattr(obj, u"_dbus_is_property", False)
543
def _get_all_dbus_properties(self):
544
"""Returns a generator of (name, attribute) pairs
546
return ((prop._dbus_name, prop)
548
inspect.getmembers(self, self._is_dbus_property))
550
def _get_dbus_property(self, interface_name, property_name):
551
"""Returns a bound method if one exists which is a D-Bus
552
property with the specified name and interface.
554
for name in (property_name,
555
property_name + u"_dbus_property"):
556
prop = getattr(self, name, None)
558
or not self._is_dbus_property(prop)
559
or prop._dbus_name != property_name
560
or (interface_name and prop._dbus_interface
561
and interface_name != prop._dbus_interface)):
565
raise DBusPropertyNotFound(self.dbus_object_path + u":"
566
+ interface_name + u"."
569
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
571
def Get(self, interface_name, property_name):
572
"""Standard D-Bus property Get() method, see D-Bus standard.
574
prop = self._get_dbus_property(interface_name, property_name)
575
if prop._dbus_access == u"write":
576
raise DBusPropertyAccessException(property_name)
578
if not hasattr(value, u"variant_level"):
580
return type(value)(value, variant_level=value.variant_level+1)
582
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
583
def Set(self, interface_name, property_name, value):
584
"""Standard D-Bus property Set() method, see D-Bus standard.
586
prop = self._get_dbus_property(interface_name, property_name)
587
if prop._dbus_access == u"read":
588
raise DBusPropertyAccessException(property_name)
589
if prop._dbus_get_args_options[u"byte_arrays"]:
590
# The byte_arrays option is not supported yet on
591
# signatures other than "ay".
592
if prop._dbus_signature != u"ay":
594
value = dbus.ByteArray(''.join(unichr(byte)
598
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
599
out_signature=u"a{sv}")
600
def GetAll(self, interface_name):
601
"""Standard D-Bus property GetAll() method, see D-Bus
604
Note: Will not include properties with access="write".
607
for name, prop in self._get_all_dbus_properties():
609
and interface_name != prop._dbus_interface):
610
# Interface non-empty but did not match
612
# Ignore write-only properties
613
if prop._dbus_access == u"write":
616
if not hasattr(value, u"variant_level"):
619
all[name] = type(value)(value, variant_level=
620
value.variant_level+1)
621
return dbus.Dictionary(all, signature=u"sv")
623
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
625
path_keyword='object_path',
626
connection_keyword='connection')
627
def Introspect(self, object_path, connection):
628
"""Standard D-Bus method, overloaded to insert property tags.
630
xmlstring = dbus.service.Object.Introspect(self, object_path,
633
document = xml.dom.minidom.parseString(xmlstring)
634
def make_tag(document, name, prop):
635
e = document.createElement(u"property")
636
e.setAttribute(u"name", name)
637
e.setAttribute(u"type", prop._dbus_signature)
638
e.setAttribute(u"access", prop._dbus_access)
640
for if_tag in document.getElementsByTagName(u"interface"):
641
for tag in (make_tag(document, name, prop)
643
in self._get_all_dbus_properties()
644
if prop._dbus_interface
645
== if_tag.getAttribute(u"name")):
646
if_tag.appendChild(tag)
647
# Add the names to the return values for the
648
# "org.freedesktop.DBus.Properties" methods
649
if (if_tag.getAttribute(u"name")
650
== u"org.freedesktop.DBus.Properties"):
651
for cn in if_tag.getElementsByTagName(u"method"):
652
if cn.getAttribute(u"name") == u"Get":
653
for arg in cn.getElementsByTagName(u"arg"):
654
if (arg.getAttribute(u"direction")
656
arg.setAttribute(u"name", u"value")
657
elif cn.getAttribute(u"name") == u"GetAll":
658
for arg in cn.getElementsByTagName(u"arg"):
659
if (arg.getAttribute(u"direction")
661
arg.setAttribute(u"name", u"props")
662
xmlstring = document.toxml(u"utf-8")
664
except (AttributeError, xml.dom.DOMException,
665
xml.parsers.expat.ExpatError), error:
666
logger.error(u"Failed to override Introspection method",
671
class ClientDBus(Client, DBusObjectWithProperties):
411
def still_valid(self):
412
"""Has the timeout not yet passed for this client?"""
413
if not getattr(self, "enabled", False):
415
now = datetime.datetime.utcnow()
416
if self.last_checked_ok is None:
417
return now < (self.created + self.timeout)
419
return now < (self.last_checked_ok + self.timeout)
422
class ClientDBus(Client, dbus.service.Object):
672
423
"""A Client class using D-Bus
675
dbus_object_path: dbus.ObjectPath
676
bus: dbus.SystemBus()
425
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
678
427
# dbus.service.Object doesn't use super(), so we can't either.
680
def __init__(self, bus = None, *args, **kwargs):
429
def __init__(self, *args, **kwargs):
682
430
Client.__init__(self, *args, **kwargs)
683
431
# Only now, when this client is initialized, can it show up on
685
433
self.dbus_object_path = (dbus.ObjectPath
687
+ self.name.replace(u".", u"_")))
688
DBusObjectWithProperties.__init__(self, self.bus,
689
self.dbus_object_path)
692
def _datetime_to_dbus(dt, variant_level=0):
693
"""Convert a UTC datetime.datetime() to a D-Bus type."""
694
return dbus.String(dt.isoformat(),
695
variant_level=variant_level)
435
+ self.name.replace(".", "_")))
436
dbus.service.Object.__init__(self, bus,
437
self.dbus_object_path)
697
438
def enable(self):
698
oldstate = getattr(self, u"enabled", False)
439
oldstate = getattr(self, "enabled", False)
699
440
r = Client.enable(self)
700
441
if oldstate != self.enabled:
701
442
# Emit D-Bus signals
702
443
self.PropertyChanged(dbus.String(u"enabled"),
703
444
dbus.Boolean(True, variant_level=1))
704
self.PropertyChanged(
705
dbus.String(u"last_enabled"),
706
self._datetime_to_dbus(self.last_enabled,
445
self.PropertyChanged(dbus.String(u"last_enabled"),
446
(_datetime_to_dbus(self.last_enabled,
710
def disable(self, quiet = False):
711
oldstate = getattr(self, u"enabled", False)
712
r = Client.disable(self, quiet=quiet)
713
if not quiet and oldstate != self.enabled:
450
def disable(self, signal = True):
451
oldstate = getattr(self, "enabled", False)
452
r = Client.disable(self)
453
if signal and oldstate != self.enabled:
714
454
# Emit D-Bus signal
715
455
self.PropertyChanged(dbus.String(u"enabled"),
716
456
dbus.Boolean(False, variant_level=1))
769
509
# Emit D-Bus signal
770
510
self.CheckerStarted(self.current_checker_command)
771
511
self.PropertyChanged(
772
dbus.String(u"checker_running"),
512
dbus.String("checker_running"),
773
513
dbus.Boolean(True, variant_level=1))
776
516
def stop_checker(self, *args, **kwargs):
777
old_checker = getattr(self, u"checker", None)
517
old_checker = getattr(self, "checker", None)
778
518
r = Client.stop_checker(self, *args, **kwargs)
779
519
if (old_checker is not None
780
and getattr(self, u"checker", None) is None):
520
and getattr(self, "checker", None) is None):
781
521
self.PropertyChanged(dbus.String(u"checker_running"),
782
522
dbus.Boolean(False, variant_level=1))
785
## D-Bus methods, signals & properties
525
## D-Bus methods & signals
786
526
_interface = u"se.bsnet.fukt.Mandos.Client"
529
CheckedOK = dbus.service.method(_interface)(checked_ok)
530
CheckedOK.__name__ = "CheckedOK"
790
532
# CheckerCompleted - signal
791
@dbus.service.signal(_interface, signature=u"nxs")
533
@dbus.service.signal(_interface, signature="nxs")
792
534
def CheckerCompleted(self, exitcode, waitstatus, command):
796
538
# CheckerStarted - signal
797
@dbus.service.signal(_interface, signature=u"s")
539
@dbus.service.signal(_interface, signature="s")
798
540
def CheckerStarted(self, command):
544
# GetAllProperties - method
545
@dbus.service.method(_interface, out_signature="a{sv}")
546
def GetAllProperties(self):
548
return dbus.Dictionary({
550
dbus.String(self.name, variant_level=1),
551
dbus.String("fingerprint"):
552
dbus.String(self.fingerprint, variant_level=1),
554
dbus.String(self.host, variant_level=1),
555
dbus.String("created"):
556
_datetime_to_dbus(self.created, variant_level=1),
557
dbus.String("last_enabled"):
558
(_datetime_to_dbus(self.last_enabled,
560
if self.last_enabled is not None
561
else dbus.Boolean(False, variant_level=1)),
562
dbus.String("enabled"):
563
dbus.Boolean(self.enabled, variant_level=1),
564
dbus.String("last_checked_ok"):
565
(_datetime_to_dbus(self.last_checked_ok,
567
if self.last_checked_ok is not None
568
else dbus.Boolean (False, variant_level=1)),
569
dbus.String("timeout"):
570
dbus.UInt64(self.timeout_milliseconds(),
572
dbus.String("interval"):
573
dbus.UInt64(self.interval_milliseconds(),
575
dbus.String("checker"):
576
dbus.String(self.checker_command,
578
dbus.String("checker_running"):
579
dbus.Boolean(self.checker is not None,
581
dbus.String("object_path"):
582
dbus.ObjectPath(self.dbus_object_path,
586
# IsStillValid - method
587
@dbus.service.method(_interface, out_signature="b")
588
def IsStillValid(self):
589
return self.still_valid()
802
591
# PropertyChanged - signal
803
@dbus.service.signal(_interface, signature=u"sv")
592
@dbus.service.signal(_interface, signature="sv")
804
593
def PropertyChanged(self, property, value):
597
# ReceivedSecret - signal
809
598
@dbus.service.signal(_interface)
599
def ReceivedSecret(self):
845
669
# StopChecker - method
846
@dbus.service.method(_interface)
847
def StopChecker(self):
853
@dbus_service_property(_interface, signature=u"s", access=u"read")
854
def name_dbus_property(self):
855
return dbus.String(self.name)
857
# fingerprint - property
858
@dbus_service_property(_interface, signature=u"s", access=u"read")
859
def fingerprint_dbus_property(self):
860
return dbus.String(self.fingerprint)
863
@dbus_service_property(_interface, signature=u"s",
865
def host_dbus_property(self, value=None):
866
if value is None: # get
867
return dbus.String(self.host)
870
self.PropertyChanged(dbus.String(u"host"),
871
dbus.String(value, variant_level=1))
874
@dbus_service_property(_interface, signature=u"s", access=u"read")
875
def created_dbus_property(self):
876
return dbus.String(self._datetime_to_dbus(self.created))
878
# last_enabled - property
879
@dbus_service_property(_interface, signature=u"s", access=u"read")
880
def last_enabled_dbus_property(self):
881
if self.last_enabled is None:
882
return dbus.String(u"")
883
return dbus.String(self._datetime_to_dbus(self.last_enabled))
886
@dbus_service_property(_interface, signature=u"b",
888
def enabled_dbus_property(self, value=None):
889
if value is None: # get
890
return dbus.Boolean(self.enabled)
896
# last_checked_ok - property
897
@dbus_service_property(_interface, signature=u"s",
899
def last_checked_ok_dbus_property(self, value=None):
900
if value is not None:
903
if self.last_checked_ok is None:
904
return dbus.String(u"")
905
return dbus.String(self._datetime_to_dbus(self
909
@dbus_service_property(_interface, signature=u"t",
911
def timeout_dbus_property(self, value=None):
912
if value is None: # get
913
return dbus.UInt64(self.timeout_milliseconds())
914
self.timeout = datetime.timedelta(0, 0, 0, value)
916
self.PropertyChanged(dbus.String(u"timeout"),
917
dbus.UInt64(value, variant_level=1))
918
if getattr(self, u"disable_initiator_tag", None) is None:
921
gobject.source_remove(self.disable_initiator_tag)
922
self.disable_initiator_tag = None
924
_timedelta_to_milliseconds((self
930
# The timeout has passed
933
self.disable_initiator_tag = (gobject.timeout_add
934
(time_to_die, self.disable))
936
# interval - property
937
@dbus_service_property(_interface, signature=u"t",
939
def interval_dbus_property(self, value=None):
940
if value is None: # get
941
return dbus.UInt64(self.interval_milliseconds())
942
self.interval = datetime.timedelta(0, 0, 0, value)
944
self.PropertyChanged(dbus.String(u"interval"),
945
dbus.UInt64(value, variant_level=1))
946
if getattr(self, u"checker_initiator_tag", None) is None:
948
# Reschedule checker run
949
gobject.source_remove(self.checker_initiator_tag)
950
self.checker_initiator_tag = (gobject.timeout_add
951
(value, self.start_checker))
952
self.start_checker() # Start one now, too
955
@dbus_service_property(_interface, signature=u"s",
957
def checker_dbus_property(self, value=None):
958
if value is None: # get
959
return dbus.String(self.checker_command)
960
self.checker_command = value
962
self.PropertyChanged(dbus.String(u"checker"),
963
dbus.String(self.checker_command,
966
# checker_running - property
967
@dbus_service_property(_interface, signature=u"b",
969
def checker_running_dbus_property(self, value=None):
970
if value is None: # get
971
return dbus.Boolean(self.checker is not None)
977
# object_path - property
978
@dbus_service_property(_interface, signature=u"o", access=u"read")
979
def object_path_dbus_property(self):
980
return self.dbus_object_path # is already a dbus.ObjectPath
983
@dbus_service_property(_interface, signature=u"ay",
984
access=u"write", byte_arrays=True)
985
def secret_dbus_property(self, value):
986
self.secret = str(value)
670
StopChecker = dbus.service.method(_interface)(stop_checker)
671
StopChecker.__name__ = "StopChecker"
991
class ClientHandler(socketserver.BaseRequestHandler, object):
992
"""A class to handle client connections.
994
Instantiated once for each connection to handle it.
676
def peer_certificate(session):
677
"Return the peer's OpenPGP certificate as a bytestring"
678
# If not an OpenPGP certificate...
679
if (gnutls.library.functions
680
.gnutls_certificate_type_get(session._c_object)
681
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
682
# ...do the normal thing
683
return session.peer_certificate
684
list_size = ctypes.c_uint(1)
685
cert_list = (gnutls.library.functions
686
.gnutls_certificate_get_peers
687
(session._c_object, ctypes.byref(list_size)))
688
if not bool(cert_list) and list_size.value != 0:
689
raise gnutls.errors.GNUTLSError("error getting peer"
691
if list_size.value == 0:
694
return ctypes.string_at(cert.data, cert.size)
697
def fingerprint(openpgp):
698
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
699
# New GnuTLS "datum" with the OpenPGP public key
700
datum = (gnutls.library.types
701
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
704
ctypes.c_uint(len(openpgp))))
705
# New empty GnuTLS certificate
706
crt = gnutls.library.types.gnutls_openpgp_crt_t()
707
(gnutls.library.functions
708
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
709
# Import the OpenPGP public key into the certificate
710
(gnutls.library.functions
711
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
712
gnutls.library.constants
713
.GNUTLS_OPENPGP_FMT_RAW))
714
# Verify the self signature in the key
715
crtverify = ctypes.c_uint()
716
(gnutls.library.functions
717
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
718
if crtverify.value != 0:
719
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
720
raise gnutls.errors.CertificateSecurityError("Verify failed")
721
# New buffer for the fingerprint
722
buf = ctypes.create_string_buffer(20)
723
buf_len = ctypes.c_size_t()
724
# Get the fingerprint from the certificate into the buffer
725
(gnutls.library.functions
726
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
727
ctypes.byref(buf_len)))
728
# Deinit the certificate
729
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
730
# Convert the buffer to a Python bytestring
731
fpr = ctypes.string_at(buf, buf_len.value)
732
# Convert the bytestring to hexadecimal notation
733
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
737
class TCP_handler(SocketServer.BaseRequestHandler, object):
738
"""A TCP request handler class.
739
Instantiated by IPv6_TCPServer for each request to handle it.
995
740
Note: This will run in its own forked process."""
997
742
def handle(self):
998
743
logger.info(u"TCP connection from: %s",
999
744
unicode(self.client_address))
1000
logger.debug(u"IPC Pipe FD: %d",
1001
self.server.child_pipe[1].fileno())
745
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
1002
746
# Open IPC pipe to parent process
1003
with contextlib.nested(self.server.child_pipe[1],
1004
self.server.parent_pipe[0]
1005
) as (ipc, ipc_return):
747
with closing(os.fdopen(self.server.pipe[1], "w", 1)) as ipc:
1006
748
session = (gnutls.connection
1007
749
.ClientSession(self.request,
1008
750
gnutls.connection
1009
751
.X509Credentials()))
753
line = self.request.makefile().readline()
754
logger.debug(u"Protocol version: %r", line)
756
if int(line.strip().split()[0]) > 1:
758
except (ValueError, IndexError, RuntimeError), error:
759
logger.error(u"Unknown protocol version: %s", error)
1011
762
# Note: gnutls.connection.X509Credentials is really a
1012
763
# generic GnuTLS certificate credentials object so long as
1013
764
# no X.509 keys are added to it. Therefore, we can use it
1014
765
# here despite using OpenPGP certificates.
1016
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1017
# u"+AES-256-CBC", u"+SHA1",
1018
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
767
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
768
# "+AES-256-CBC", "+SHA1",
769
# "+COMP-NULL", "+CTYPE-OPENPGP",
1020
771
# Use a fallback default, since this MUST be set.
1021
priority = self.server.gnutls_priority
1022
if priority is None:
1023
priority = u"NORMAL"
772
priority = self.server.settings.get("priority", "NORMAL")
1024
773
(gnutls.library.functions
1025
774
.gnutls_priority_set_direct(session._c_object,
1026
775
priority, None))
1028
# Start communication using the Mandos protocol
1029
# Get protocol number
1030
line = self.request.makefile().readline()
1031
logger.debug(u"Protocol version: %r", line)
1033
if int(line.strip().split()[0]) > 1:
1035
except (ValueError, IndexError, RuntimeError), error:
1036
logger.error(u"Unknown protocol version: %s", error)
1039
# Start GnuTLS connection
1041
778
session.handshake()
1042
779
except gnutls.errors.GNUTLSError, error:
1047
784
logger.debug(u"Handshake succeeded")
1050
fpr = self.fingerprint(self.peer_certificate
1052
except (TypeError, gnutls.errors.GNUTLSError), error:
1053
logger.warning(u"Bad certificate: %s", error)
1055
logger.debug(u"Fingerprint: %s", fpr)
1057
for c in self.server.clients:
1058
if c.fingerprint == fpr:
1062
ipc.write(u"NOTFOUND %s %s\n"
1063
% (fpr, unicode(self.client_address)))
1066
class ClientProxy(object):
1067
"""Client proxy object. Not for calling methods."""
1068
def __init__(self, client):
1069
self.client = client
1070
def __getattr__(self, name):
1071
if name.startswith("ipc_"):
1073
ipc.write("%s %s\n" % (name[4:].upper(),
1076
if not hasattr(self.client, name):
1077
raise AttributeError
1078
ipc.write(u"GETATTR %s %s\n"
1079
% (name, self.client.fingerprint))
1080
return pickle.load(ipc_return)
1081
clientproxy = ClientProxy(client)
1082
# Have to check if client.enabled, since it is
1083
# possible that the client was disabled since the
1084
# GnuTLS session was established.
1085
if not clientproxy.enabled:
1086
clientproxy.ipc_disabled()
1089
clientproxy.ipc_sending()
1091
while sent_size < len(client.secret):
1092
sent = session.send(client.secret[sent_size:])
1093
logger.debug(u"Sent: %d, remaining: %d",
1094
sent, len(client.secret)
1095
- (sent_size + sent))
1101
def peer_certificate(session):
1102
"Return the peer's OpenPGP certificate as a bytestring"
1103
# If not an OpenPGP certificate...
1104
if (gnutls.library.functions
1105
.gnutls_certificate_type_get(session._c_object)
1106
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1107
# ...do the normal thing
1108
return session.peer_certificate
1109
list_size = ctypes.c_uint(1)
1110
cert_list = (gnutls.library.functions
1111
.gnutls_certificate_get_peers
1112
(session._c_object, ctypes.byref(list_size)))
1113
if not bool(cert_list) and list_size.value != 0:
1114
raise gnutls.errors.GNUTLSError(u"error getting peer"
1116
if list_size.value == 0:
1119
return ctypes.string_at(cert.data, cert.size)
1122
def fingerprint(openpgp):
1123
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1124
# New GnuTLS "datum" with the OpenPGP public key
1125
datum = (gnutls.library.types
1126
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1129
ctypes.c_uint(len(openpgp))))
1130
# New empty GnuTLS certificate
1131
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1132
(gnutls.library.functions
1133
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1134
# Import the OpenPGP public key into the certificate
1135
(gnutls.library.functions
1136
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1137
gnutls.library.constants
1138
.GNUTLS_OPENPGP_FMT_RAW))
1139
# Verify the self signature in the key
1140
crtverify = ctypes.c_uint()
1141
(gnutls.library.functions
1142
.gnutls_openpgp_crt_verify_self(crt, 0,
1143
ctypes.byref(crtverify)))
1144
if crtverify.value != 0:
1145
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1146
raise (gnutls.errors.CertificateSecurityError
1148
# New buffer for the fingerprint
1149
buf = ctypes.create_string_buffer(20)
1150
buf_len = ctypes.c_size_t()
1151
# Get the fingerprint from the certificate into the buffer
1152
(gnutls.library.functions
1153
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1154
ctypes.byref(buf_len)))
1155
# Deinit the certificate
1156
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1157
# Convert the buffer to a Python bytestring
1158
fpr = ctypes.string_at(buf, buf_len.value)
1159
# Convert the bytestring to hexadecimal notation
1160
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1164
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
1165
"""Like socketserver.ForkingMixIn, but also pass a pipe pair."""
786
fpr = fingerprint(peer_certificate(session))
787
except (TypeError, gnutls.errors.GNUTLSError), error:
788
logger.warning(u"Bad certificate: %s", error)
791
logger.debug(u"Fingerprint: %s", fpr)
793
for c in self.server.clients:
794
if c.fingerprint == fpr:
798
logger.warning(u"Client not found for fingerprint: %s",
800
ipc.write("NOTFOUND %s\n" % fpr)
803
# Have to check if client.still_valid(), since it is
804
# possible that the client timed out while establishing
805
# the GnuTLS session.
806
if not client.still_valid():
807
logger.warning(u"Client %(name)s is invalid",
809
ipc.write("INVALID %s\n" % client.name)
812
ipc.write("SENDING %s\n" % client.name)
814
while sent_size < len(client.secret):
815
sent = session.send(client.secret[sent_size:])
816
logger.debug(u"Sent: %d, remaining: %d",
817
sent, len(client.secret)
818
- (sent_size + sent))
823
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
824
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
825
Assumes a gobject.MainLoop event loop.
1166
827
def process_request(self, request, client_address):
1167
"""Overrides and wraps the original process_request().
1169
This function creates a new pipe in self.pipe
828
"""This overrides and wraps the original process_request().
829
This function creates a new pipe in self.pipe
1171
# Child writes to child_pipe
1172
self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1173
# Parent writes to parent_pipe
1174
self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1175
super(ForkingMixInWithPipes,
831
self.pipe = os.pipe()
832
super(ForkingMixInWithPipe,
1176
833
self).process_request(request, client_address)
1177
# Close unused ends for parent
1178
self.parent_pipe[0].close() # close read end
1179
self.child_pipe[1].close() # close write end
1180
self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
1181
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
834
os.close(self.pipe[1]) # close write end
835
# Call "handle_ipc" for both data and EOF events
836
gobject.io_add_watch(self.pipe[0],
837
gobject.IO_IN | gobject.IO_HUP,
839
def handle_ipc(source, condition):
1182
840
"""Dummy function; override as necessary"""
1183
child_pipe_fd.close()
1184
parent_pipe_fd.close()
1187
class IPv6_TCPServer(ForkingMixInWithPipes,
1188
socketserver.TCPServer, object):
845
class IPv6_TCPServer(ForkingMixInWithPipe,
846
SocketServer.TCPServer, object):
1189
847
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
849
settings: Server settings
850
clients: Set() of Client objects
1192
851
enabled: Boolean; whether this server is activated yet
1193
interface: None or a network interface name (string)
1194
use_ipv6: Boolean; to use IPv6 or not
1196
def __init__(self, server_address, RequestHandlerClass,
1197
interface=None, use_ipv6=True):
1198
self.interface = interface
1200
self.address_family = socket.AF_INET6
1201
socketserver.TCPServer.__init__(self, server_address,
1202
RequestHandlerClass)
853
address_family = socket.AF_INET6
854
def __init__(self, *args, **kwargs):
855
if "settings" in kwargs:
856
self.settings = kwargs["settings"]
857
del kwargs["settings"]
858
if "clients" in kwargs:
859
self.clients = kwargs["clients"]
860
del kwargs["clients"]
861
if "use_ipv6" in kwargs:
862
if not kwargs["use_ipv6"]:
863
self.address_family = socket.AF_INET
864
del kwargs["use_ipv6"]
866
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
1203
867
def server_bind(self):
1204
868
"""This overrides the normal server_bind() function
1205
869
to bind to an interface if one was specified, and also NOT to
1206
870
bind to an address or port if they were not specified."""
1207
if self.interface is not None:
1208
if SO_BINDTODEVICE is None:
1209
logger.error(u"SO_BINDTODEVICE does not exist;"
1210
u" cannot bind to interface %s",
1214
self.socket.setsockopt(socket.SOL_SOCKET,
1218
except socket.error, error:
1219
if error[0] == errno.EPERM:
1220
logger.error(u"No permission to"
1221
u" bind to interface %s",
1223
elif error[0] == errno.ENOPROTOOPT:
1224
logger.error(u"SO_BINDTODEVICE not available;"
1225
u" cannot bind to interface %s",
871
if self.settings["interface"]:
872
# 25 is from /usr/include/asm-i486/socket.h
873
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
875
self.socket.setsockopt(socket.SOL_SOCKET,
877
self.settings["interface"])
878
except socket.error, error:
879
if error[0] == errno.EPERM:
880
logger.error(u"No permission to"
881
u" bind to interface %s",
882
self.settings["interface"])
1229
885
# Only bind(2) the socket if we really need to.
1230
886
if self.server_address[0] or self.server_address[1]:
1231
887
if not self.server_address[0]:
1232
888
if self.address_family == socket.AF_INET6:
1233
any_address = u"::" # in6addr_any
889
any_address = "::" # in6addr_any
1235
891
any_address = socket.INADDR_ANY
1236
892
self.server_address = (any_address,
1238
894
elif not self.server_address[1]:
1239
895
self.server_address = (self.server_address[0],
1241
# if self.interface:
897
# if self.settings["interface"]:
1242
898
# self.server_address = (self.server_address[0],
1245
901
# if_nametoindex
1247
return socketserver.TCPServer.server_bind(self)
1250
class MandosServer(IPv6_TCPServer):
1254
clients: set of Client objects
1255
gnutls_priority GnuTLS priority string
1256
use_dbus: Boolean; to emit D-Bus signals or not
1258
Assumes a gobject.MainLoop event loop.
1260
def __init__(self, server_address, RequestHandlerClass,
1261
interface=None, use_ipv6=True, clients=None,
1262
gnutls_priority=None, use_dbus=True):
1263
self.enabled = False
1264
self.clients = clients
1265
if self.clients is None:
1266
self.clients = set()
1267
self.use_dbus = use_dbus
1268
self.gnutls_priority = gnutls_priority
1269
IPv6_TCPServer.__init__(self, server_address,
1270
RequestHandlerClass,
1271
interface = interface,
1272
use_ipv6 = use_ipv6)
904
return super(IPv6_TCPServer, self).server_bind()
1273
905
def server_activate(self):
1274
906
if self.enabled:
1275
return socketserver.TCPServer.server_activate(self)
907
return super(IPv6_TCPServer, self).server_activate()
1276
908
def enable(self):
1277
909
self.enabled = True
1278
def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1279
# Call "handle_ipc" for both data and EOF events
1280
gobject.io_add_watch(child_pipe_fd.fileno(),
1281
gobject.IO_IN | gobject.IO_HUP,
1282
functools.partial(self.handle_ipc,
1283
reply = parent_pipe_fd,
1284
sender= child_pipe_fd))
1285
def handle_ipc(self, source, condition, reply=None, sender=None):
910
def handle_ipc(self, source, condition, file_objects={}):
1286
911
condition_names = {
1287
gobject.IO_IN: u"IN", # There is data to read.
1288
gobject.IO_OUT: u"OUT", # Data can be written (without
1290
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1291
gobject.IO_ERR: u"ERR", # Error condition.
1292
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1293
# broken, usually for pipes and
912
gobject.IO_IN: "IN", # There is data to read.
913
gobject.IO_OUT: "OUT", # Data can be written (without
915
gobject.IO_PRI: "PRI", # There is urgent data to read.
916
gobject.IO_ERR: "ERR", # Error condition.
917
gobject.IO_HUP: "HUP" # Hung up (the connection has been
918
# broken, usually for pipes and
1296
921
conditions_string = ' | '.join(name
1297
922
for cond, name in
1298
923
condition_names.iteritems()
1299
924
if cond & condition)
1300
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
925
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1301
926
conditions_string)
928
# Turn the pipe file descriptor into a Python file object
929
if source not in file_objects:
930
file_objects[source] = os.fdopen(source, "r", 1)
1303
932
# Read a line from the file object
1304
cmdline = sender.readline()
933
cmdline = file_objects[source].readline()
1305
934
if not cmdline: # Empty line means end of file
1306
# close the IPC pipes
936
file_objects[source].close()
937
del file_objects[source]
1310
939
# Stop calling this function
1313
logger.debug(u"IPC command: %r", cmdline)
942
logger.debug("IPC command: %r\n" % cmdline)
1315
944
# Parse and act on command
1316
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1318
if cmd == u"NOTFOUND":
1319
fpr, address = args.split(None, 1)
1320
logger.warning(u"Client not found for fingerprint: %s, ad"
1321
u"dress: %s", fpr, address)
945
cmd, args = cmdline.split(None, 1)
946
if cmd == "NOTFOUND":
947
if self.settings["use_dbus"]:
1323
948
# Emit D-Bus signal
1324
mandos_dbus_service.ClientNotFound(fpr, address)
1325
elif cmd == u"DISABLED":
1326
for client in self.clients:
1327
if client.name == args:
1328
logger.warning(u"Client %s is disabled", args)
949
mandos_dbus_service.ClientNotFound(args)
950
elif cmd == "INVALID":
951
if self.settings["use_dbus"]:
952
for client in self.clients:
953
if client.name == args:
1330
954
# Emit D-Bus signal
1331
955
client.Rejected()
1334
logger.error(u"Unknown client %s is disabled", args)
1335
elif cmd == u"SENDING":
957
elif cmd == "SENDING":
1336
958
for client in self.clients:
1337
959
if client.name == args:
1338
logger.info(u"Sending secret to %s", client.name)
1339
960
client.checked_ok()
961
if self.settings["use_dbus"]:
1341
962
# Emit D-Bus signal
1345
logger.error(u"Sending secret to unknown client %s",
1347
elif cmd == u"GETATTR":
1348
attr_name, fpr = args.split(None, 1)
1349
for client in self.clients:
1350
if client.fingerprint == fpr:
1351
attr_value = getattr(client, attr_name, None)
1352
logger.debug("IPC reply: %r", attr_value)
1353
pickle.dump(attr_value, reply)
1356
logger.error(u"Client %s on address %s requesting "
1357
u"attribute %s not found", fpr, address,
1359
pickle.dump(None, reply)
963
client.ReceivedSecret()
1361
logger.error(u"Unknown IPC command: %r", cmdline)
966
logger.error("Unknown IPC command: %r", cmdline)
1363
968
# Keep calling this function
1396
1001
elif suffix == u"w":
1397
1002
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1399
raise ValueError(u"Unknown suffix %r" % suffix)
1400
except (ValueError, IndexError), e:
1401
raise ValueError(e.message)
1005
except (ValueError, IndexError):
1402
1007
timevalue += delta
1403
1008
return timevalue
1011
def server_state_changed(state):
1012
"""Derived from the Avahi example code"""
1013
if state == avahi.SERVER_COLLISION:
1014
logger.error(u"Zeroconf server name collision")
1016
elif state == avahi.SERVER_RUNNING:
1020
def entry_group_state_changed(state, error):
1021
"""Derived from the Avahi example code"""
1022
logger.debug(u"Avahi state change: %i", state)
1024
if state == avahi.ENTRY_GROUP_ESTABLISHED:
1025
logger.debug(u"Zeroconf service established.")
1026
elif state == avahi.ENTRY_GROUP_COLLISION:
1027
logger.warning(u"Zeroconf service name collision.")
1029
elif state == avahi.ENTRY_GROUP_FAILURE:
1030
logger.critical(u"Avahi: Error in group state changed %s",
1032
raise AvahiGroupError(u"State changed: %s" % unicode(error))
1406
1034
def if_nametoindex(interface):
1407
"""Call the C function if_nametoindex(), or equivalent
1409
Note: This function cannot accept a unicode string."""
1035
"""Call the C function if_nametoindex(), or equivalent"""
1410
1036
global if_nametoindex
1412
1038
if_nametoindex = (ctypes.cdll.LoadLibrary
1413
(ctypes.util.find_library(u"c"))
1039
(ctypes.util.find_library("c"))
1414
1040
.if_nametoindex)
1415
1041
except (OSError, AttributeError):
1416
logger.warning(u"Doing if_nametoindex the hard way")
1042
if "struct" not in sys.modules:
1044
if "fcntl" not in sys.modules:
1417
1046
def if_nametoindex(interface):
1418
1047
"Get an interface index the hard way, i.e. using fcntl()"
1419
1048
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1420
with contextlib.closing(socket.socket()) as s:
1049
with closing(socket.socket()) as s:
1421
1050
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1422
struct.pack(str(u"16s16x"),
1424
interface_index = struct.unpack(str(u"I"),
1051
struct.pack("16s16x", interface))
1052
interface_index = struct.unpack("I", ifreq[16:20])[0]
1426
1053
return interface_index
1427
1054
return if_nametoindex(interface)
1430
1057
def daemon(nochdir = False, noclose = False):
1431
1058
"""See daemon(3). Standard BSD Unix function.
1433
1059
This should really exist as os.daemon, but it doesn't (yet)."""
1437
1063
if not nochdir:
1441
1067
if not noclose:
1457
##################################################################
1082
######################################################################
1458
1083
# Parsing of options, both command line and config file
1460
1085
parser = optparse.OptionParser(version = "%%prog %s" % version)
1461
parser.add_option("-i", u"--interface", type=u"string",
1462
metavar="IF", help=u"Bind to interface IF")
1463
parser.add_option("-a", u"--address", type=u"string",
1464
help=u"Address to listen for requests on")
1465
parser.add_option("-p", u"--port", type=u"int",
1466
help=u"Port number to receive requests on")
1467
parser.add_option("--check", action=u"store_true",
1468
help=u"Run self-test")
1469
parser.add_option("--debug", action=u"store_true",
1470
help=u"Debug mode; run in foreground and log to"
1472
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1473
u" priority string (see GnuTLS documentation)")
1474
parser.add_option("--servicename", type=u"string",
1475
metavar=u"NAME", help=u"Zeroconf service name")
1476
parser.add_option("--configdir", type=u"string",
1477
default=u"/etc/mandos", metavar=u"DIR",
1478
help=u"Directory to search for configuration"
1480
parser.add_option("--no-dbus", action=u"store_false",
1481
dest=u"use_dbus", help=u"Do not provide D-Bus"
1482
u" system bus interface")
1483
parser.add_option("--no-ipv6", action=u"store_false",
1484
dest=u"use_ipv6", help=u"Do not use IPv6")
1086
parser.add_option("-i", "--interface", type="string",
1087
metavar="IF", help="Bind to interface IF")
1088
parser.add_option("-a", "--address", type="string",
1089
help="Address to listen for requests on")
1090
parser.add_option("-p", "--port", type="int",
1091
help="Port number to receive requests on")
1092
parser.add_option("--check", action="store_true",
1093
help="Run self-test")
1094
parser.add_option("--debug", action="store_true",
1095
help="Debug mode; run in foreground and log to"
1097
parser.add_option("--priority", type="string", help="GnuTLS"
1098
" priority string (see GnuTLS documentation)")
1099
parser.add_option("--servicename", type="string", metavar="NAME",
1100
help="Zeroconf service name")
1101
parser.add_option("--configdir", type="string",
1102
default="/etc/mandos", metavar="DIR",
1103
help="Directory to search for configuration"
1105
parser.add_option("--no-dbus", action="store_false",
1107
help="Do not provide D-Bus system bus"
1109
parser.add_option("--no-ipv6", action="store_false",
1110
dest="use_ipv6", help="Do not use IPv6")
1485
1111
options = parser.parse_args()[0]
1487
1113
if options.check:
1492
1118
# Default values for config file for server-global settings
1493
server_defaults = { u"interface": u"",
1498
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1499
u"servicename": u"Mandos",
1500
u"use_dbus": u"True",
1501
u"use_ipv6": u"True",
1119
server_defaults = { "interface": "",
1124
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1125
"servicename": "Mandos",
1504
1130
# Parse config file for server-global settings
1505
server_config = configparser.SafeConfigParser(server_defaults)
1131
server_config = ConfigParser.SafeConfigParser(server_defaults)
1506
1132
del server_defaults
1507
server_config.read(os.path.join(options.configdir,
1133
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1509
1134
# Convert the SafeConfigParser object to a dict
1510
1135
server_settings = server_config.defaults()
1511
1136
# Use the appropriate methods on the non-string config options
1512
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1513
server_settings[option] = server_config.getboolean(u"DEFAULT",
1137
server_settings["debug"] = server_config.getboolean("DEFAULT",
1139
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1141
server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1515
1143
if server_settings["port"]:
1516
server_settings["port"] = server_config.getint(u"DEFAULT",
1144
server_settings["port"] = server_config.getint("DEFAULT",
1518
1146
del server_config
1520
1148
# Override the settings from the config file with command line
1521
1149
# options, if set.
1522
for option in (u"interface", u"address", u"port", u"debug",
1523
u"priority", u"servicename", u"configdir",
1524
u"use_dbus", u"use_ipv6"):
1150
for option in ("interface", "address", "port", "debug",
1151
"priority", "servicename", "configdir",
1152
"use_dbus", "use_ipv6"):
1525
1153
value = getattr(options, option)
1526
1154
if value is not None:
1527
1155
server_settings[option] = value
1529
# Force all strings to be unicode
1530
for option in server_settings.keys():
1531
if type(server_settings[option]) is str:
1532
server_settings[option] = unicode(server_settings[option])
1533
1157
# Now we have our good server settings in "server_settings"
1535
1159
##################################################################
1537
1161
# For convenience
1538
debug = server_settings[u"debug"]
1539
use_dbus = server_settings[u"use_dbus"]
1540
use_ipv6 = server_settings[u"use_ipv6"]
1162
debug = server_settings["debug"]
1163
use_dbus = server_settings["use_dbus"]
1164
use_ipv6 = server_settings["use_ipv6"]
1543
1167
syslogger.setLevel(logging.WARNING)
1544
1168
console.setLevel(logging.WARNING)
1546
if server_settings[u"servicename"] != u"Mandos":
1170
if server_settings["servicename"] != "Mandos":
1547
1171
syslogger.setFormatter(logging.Formatter
1548
(u'Mandos (%s) [%%(process)d]:'
1549
u' %%(levelname)s: %%(message)s'
1550
% server_settings[u"servicename"]))
1172
('Mandos (%s) [%%(process)d]:'
1173
' %%(levelname)s: %%(message)s'
1174
% server_settings["servicename"]))
1552
1176
# Parse config file with clients
1553
client_defaults = { u"timeout": u"1h",
1555
u"checker": u"fping -q -- %%(host)s",
1177
client_defaults = { "timeout": "1h",
1179
"checker": "fping -q -- %%(host)s",
1558
client_config = configparser.SafeConfigParser(client_defaults)
1559
client_config.read(os.path.join(server_settings[u"configdir"],
1182
client_config = ConfigParser.SafeConfigParser(client_defaults)
1183
client_config.read(os.path.join(server_settings["configdir"],
1562
1186
global mandos_dbus_service
1563
1187
mandos_dbus_service = None
1565
tcp_server = MandosServer((server_settings[u"address"],
1566
server_settings[u"port"]),
1568
interface=server_settings[u"interface"],
1571
server_settings[u"priority"],
1573
pidfilename = u"/var/run/mandos.pid"
1190
tcp_server = IPv6_TCPServer((server_settings["address"],
1191
server_settings["port"]),
1193
settings=server_settings,
1194
clients=clients, use_ipv6=use_ipv6)
1195
pidfilename = "/var/run/mandos.pid"
1575
pidfile = open(pidfilename, u"w")
1197
pidfile = open(pidfilename, "w")
1576
1198
except IOError:
1577
logger.error(u"Could not open file %r", pidfilename)
1199
logger.error("Could not open file %r", pidfilename)
1580
uid = pwd.getpwnam(u"_mandos").pw_uid
1581
gid = pwd.getpwnam(u"_mandos").pw_gid
1202
uid = pwd.getpwnam("_mandos").pw_uid
1203
gid = pwd.getpwnam("_mandos").pw_gid
1582
1204
except KeyError:
1584
uid = pwd.getpwnam(u"mandos").pw_uid
1585
gid = pwd.getpwnam(u"mandos").pw_gid
1206
uid = pwd.getpwnam("mandos").pw_uid
1207
gid = pwd.getpwnam("mandos").pw_gid
1586
1208
except KeyError:
1588
uid = pwd.getpwnam(u"nobody").pw_uid
1589
gid = pwd.getpwnam(u"nobody").pw_gid
1210
uid = pwd.getpwnam("nobody").pw_uid
1211
gid = pwd.getpwnam("nogroup").pw_gid
1590
1212
except KeyError:
1606
1228
@gnutls.library.types.gnutls_log_func
1607
1229
def debug_gnutls(level, string):
1608
logger.debug(u"GnuTLS: %s", string[:-1])
1230
logger.debug("GnuTLS: %s", string[:-1])
1610
1232
(gnutls.library.functions
1611
1233
.gnutls_global_set_log_function(debug_gnutls))
1236
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1237
service = AvahiService(name = server_settings["servicename"],
1238
servicetype = "_mandos._tcp",
1239
protocol = protocol)
1240
if server_settings["interface"]:
1241
service.interface = (if_nametoindex
1242
(server_settings["interface"]))
1613
1244
global main_loop
1614
1247
# From the Avahi example code
1615
1248
DBusGMainLoop(set_as_default=True )
1616
1249
main_loop = gobject.MainLoop()
1617
1250
bus = dbus.SystemBus()
1251
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1252
avahi.DBUS_PATH_SERVER),
1253
avahi.DBUS_INTERFACE_SERVER)
1618
1254
# End of Avahi example code
1621
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1622
bus, do_not_queue=True)
1623
except dbus.exceptions.NameExistsException, e:
1624
logger.error(unicode(e) + u", disabling D-Bus")
1626
server_settings[u"use_dbus"] = False
1627
tcp_server.use_dbus = False
1628
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1629
service = AvahiService(name = server_settings[u"servicename"],
1630
servicetype = u"_mandos._tcp",
1631
protocol = protocol, bus = bus)
1632
if server_settings["interface"]:
1633
service.interface = (if_nametoindex
1634
(str(server_settings[u"interface"])))
1256
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1636
1258
client_class = Client
1638
client_class = functools.partial(ClientDBus, bus = bus)
1639
tcp_server.clients.update(set(
1260
client_class = ClientDBus
1640
1262
client_class(name = section,
1641
1263
config= dict(client_config.items(section)))
1642
1264
for section in client_config.sections()))
1643
if not tcp_server.clients:
1644
1266
logger.warning(u"No clients defined")
1677
1315
class MandosDBusService(dbus.service.Object):
1678
1316
"""A D-Bus proxy object"""
1679
1317
def __init__(self):
1680
dbus.service.Object.__init__(self, bus, u"/")
1318
dbus.service.Object.__init__(self, bus, "/")
1681
1319
_interface = u"se.bsnet.fukt.Mandos"
1683
@dbus.service.signal(_interface, signature=u"o")
1684
def ClientAdded(self, objpath):
1688
@dbus.service.signal(_interface, signature=u"ss")
1689
def ClientNotFound(self, fingerprint, address):
1693
@dbus.service.signal(_interface, signature=u"os")
1321
@dbus.service.signal(_interface, signature="oa{sv}")
1322
def ClientAdded(self, objpath, properties):
1326
@dbus.service.signal(_interface, signature="s")
1327
def ClientNotFound(self, fingerprint):
1331
@dbus.service.signal(_interface, signature="os")
1694
1332
def ClientRemoved(self, objpath, name):
1698
@dbus.service.method(_interface, out_signature=u"ao")
1336
@dbus.service.method(_interface, out_signature="ao")
1699
1337
def GetAllClients(self):
1701
return dbus.Array(c.dbus_object_path
1702
for c in tcp_server.clients)
1339
return dbus.Array(c.dbus_object_path for c in clients)
1704
@dbus.service.method(_interface,
1705
out_signature=u"a{oa{sv}}")
1341
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1706
1342
def GetAllClientsWithProperties(self):
1708
1344
return dbus.Dictionary(
1709
((c.dbus_object_path, c.GetAll(u""))
1710
for c in tcp_server.clients),
1711
signature=u"oa{sv}")
1345
((c.dbus_object_path, c.GetAllProperties())
1713
@dbus.service.method(_interface, in_signature=u"o")
1349
@dbus.service.method(_interface, in_signature="o")
1714
1350
def RemoveClient(self, object_path):
1716
for c in tcp_server.clients:
1717
1353
if c.dbus_object_path == object_path:
1718
tcp_server.clients.remove(c)
1719
1355
c.remove_from_connection()
1720
1356
# Don't signal anything except ClientRemoved
1721
c.disable(quiet=True)
1357
c.disable(signal=False)
1722
1358
# Emit D-Bus signal
1723
1359
self.ClientRemoved(object_path, c.name)
1725
raise KeyError(object_path)
1729
1365
mandos_dbus_service = MandosDBusService()
1732
"Cleanup function; run on exit"
1735
while tcp_server.clients:
1736
client = tcp_server.clients.pop()
1738
client.remove_from_connection()
1739
client.disable_hook = None
1740
# Don't signal anything except ClientRemoved
1741
client.disable(quiet=True)
1744
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1747
atexit.register(cleanup)
1749
for client in tcp_server.clients:
1367
for client in clients:
1751
1369
# Emit D-Bus signal
1752
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1370
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1371
client.GetAllProperties())
1753
1372
client.enable()
1755
1374
tcp_server.enable()