133
157
u" after %i retries, exiting.",
134
158
self.rename_count)
135
159
raise AvahiServiceError(u"Too many renames")
136
self.name = server.GetAlternativeServiceName(self.name)
160
self.name = unicode(self.server.GetAlternativeServiceName(self.name))
137
161
logger.info(u"Changing Zeroconf service name to %r ...",
139
163
syslogger.setFormatter(logging.Formatter
140
('Mandos (%s): %%(levelname)s:'
141
' %%(message)s' % self.name))
164
(u'Mandos (%s) [%%(process)d]:'
165
u' %%(levelname)s: %%(message)s'
170
except dbus.exceptions.DBusException, error:
171
logger.critical(u"DBusException: %s", error)
144
174
self.rename_count += 1
145
175
def remove(self):
146
176
"""Derived from the Avahi example code"""
147
if group is not None:
177
if self.group is not None:
150
180
"""Derived from the Avahi example code"""
153
group = dbus.Interface(bus.get_object
155
server.EntryGroupNew()),
156
avahi.DBUS_INTERFACE_ENTRY_GROUP)
157
group.connect_to_signal('StateChanged',
158
entry_group_state_changed)
181
if self.group is None:
182
self.group = dbus.Interface(
183
self.bus.get_object(avahi.DBUS_NAME,
184
self.server.EntryGroupNew()),
185
avahi.DBUS_INTERFACE_ENTRY_GROUP)
186
self.group.connect_to_signal('StateChanged',
188
.entry_group_state_changed)
159
189
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
160
service.name, service.type)
162
self.interface, # interface
163
self.protocol, # protocol
164
dbus.UInt32(0), # flags
165
self.name, self.type,
166
self.domain, self.host,
167
dbus.UInt16(self.port),
168
avahi.string_array_to_txt_array(self.TXT))
171
# From the Avahi example code:
172
group = None # our entry group
173
# End of Avahi example code
176
def _datetime_to_dbus(dt, variant_level=0):
177
"""Convert a UTC datetime.datetime() to a D-Bus type."""
178
return dbus.String(dt.isoformat(), variant_level=variant_level)
181
class Client(dbus.service.Object):
190
self.name, self.type)
191
self.group.AddService(
194
dbus.UInt32(0), # flags
195
self.name, self.type,
196
self.domain, self.host,
197
dbus.UInt16(self.port),
198
avahi.string_array_to_txt_array(self.TXT))
200
def entry_group_state_changed(self, state, error):
201
"""Derived from the Avahi example code"""
202
logger.debug(u"Avahi entry group state change: %i", state)
204
if state == avahi.ENTRY_GROUP_ESTABLISHED:
205
logger.debug(u"Zeroconf service established.")
206
elif state == avahi.ENTRY_GROUP_COLLISION:
207
logger.warning(u"Zeroconf service name collision.")
209
elif state == avahi.ENTRY_GROUP_FAILURE:
210
logger.critical(u"Avahi: Error in group state changed %s",
212
raise AvahiGroupError(u"State changed: %s"
215
"""Derived from the Avahi example code"""
216
if self.group is not None:
219
def server_state_changed(self, state):
220
"""Derived from the Avahi example code"""
221
logger.debug(u"Avahi server state change: %i", state)
222
if state == avahi.SERVER_COLLISION:
223
logger.error(u"Zeroconf server name collision")
225
elif state == avahi.SERVER_RUNNING:
228
"""Derived from the Avahi example code"""
229
if self.server is None:
230
self.server = dbus.Interface(
231
self.bus.get_object(avahi.DBUS_NAME,
232
avahi.DBUS_PATH_SERVER),
233
avahi.DBUS_INTERFACE_SERVER)
234
self.server.connect_to_signal(u"StateChanged",
235
self.server_state_changed)
236
self.server_state_changed(self.server.GetState())
239
class Client(object):
182
240
"""A representation of a client host served by this server.
184
name: string; from the config file, used in log messages and
186
fingerprint: string (40 or 32 hexadecimal digits); used to
187
uniquely identify the client
188
secret: bytestring; sent verbatim (over TLS) to client
189
host: string; available for use by the checker command
190
created: datetime.datetime(); (UTC) object creation
191
last_enabled: datetime.datetime(); (UTC)
193
last_checked_ok: datetime.datetime(); (UTC) or None
194
timeout: datetime.timedelta(); How long from last_checked_ok
195
until this client is invalid
196
interval: datetime.timedelta(); How often to start a new checker
197
disable_hook: If set, called by disable() as disable_hook(self)
243
_approved: bool(); 'None' if not yet approved/disapproved
244
approval_delay: datetime.timedelta(); Time to wait for approval
245
approval_duration: datetime.timedelta(); Duration of one approval
198
246
checker: subprocess.Popen(); a running checker process used
199
247
to see if the client lives.
200
248
'None' if no process is running.
201
checker_initiator_tag: a gobject event source tag, or None
202
disable_initiator_tag: - '' -
203
checker_callback_tag: - '' -
204
checker_command: string; External command which is run to check if
205
client lives. %() expansions are done at
249
checker_callback_tag: a gobject event source tag, or None
250
checker_command: string; External command which is run to check
251
if client lives. %() expansions are done at
206
252
runtime with vars(self) as dict, so that for
207
253
instance %(name)s can be used in the command.
254
checker_initiator_tag: a gobject event source tag, or None
255
created: datetime.datetime(); (UTC) object creation
208
256
current_checker_command: string; current running checker_command
209
use_dbus: bool(); Whether to provide D-Bus interface and signals
210
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
257
disable_hook: If set, called by disable() as disable_hook(self)
258
disable_initiator_tag: a gobject event source tag, or None
260
fingerprint: string (40 or 32 hexadecimal digits); used to
261
uniquely identify the client
262
host: string; available for use by the checker command
263
interval: datetime.timedelta(); How often to start a new checker
264
last_approval_request: datetime.datetime(); (UTC) or None
265
last_checked_ok: datetime.datetime(); (UTC) or None
266
last_enabled: datetime.datetime(); (UTC)
267
name: string; from the config file, used in log messages and
269
secret: bytestring; sent verbatim (over TLS) to client
270
timeout: datetime.timedelta(); How long from last_checked_ok
271
until this client is disabled
272
runtime_expansions: Allowed attributes for runtime expansion.
275
runtime_expansions = (u"approval_delay", u"approval_duration",
276
u"created", u"enabled", u"fingerprint",
277
u"host", u"interval", u"last_checked_ok",
278
u"last_enabled", u"name", u"timeout")
281
def _timedelta_to_milliseconds(td):
282
"Convert a datetime.timedelta() to milliseconds"
283
return ((td.days * 24 * 60 * 60 * 1000)
284
+ (td.seconds * 1000)
285
+ (td.microseconds // 1000))
212
287
def timeout_milliseconds(self):
213
288
"Return the 'timeout' attribute in milliseconds"
214
return ((self.timeout.days * 24 * 60 * 60 * 1000)
215
+ (self.timeout.seconds * 1000)
216
+ (self.timeout.microseconds // 1000))
289
return self._timedelta_to_milliseconds(self.timeout)
218
291
def interval_milliseconds(self):
219
292
"Return the 'interval' attribute in milliseconds"
220
return ((self.interval.days * 24 * 60 * 60 * 1000)
221
+ (self.interval.seconds * 1000)
222
+ (self.interval.microseconds // 1000))
293
return self._timedelta_to_milliseconds(self.interval)
295
def approval_delay_milliseconds(self):
296
return self._timedelta_to_milliseconds(self.approval_delay)
224
def __init__(self, name = None, disable_hook=None, config=None,
298
def __init__(self, name = None, disable_hook=None, config=None):
226
299
"""Note: the 'checker' key in 'config' sets the
227
300
'checker_command' attribute and *not* the 'checker'
444
507
if self.checker_callback_tag:
445
508
gobject.source_remove(self.checker_callback_tag)
446
509
self.checker_callback_tag = None
447
if getattr(self, "checker", None) is None:
510
if getattr(self, u"checker", None) is None:
449
512
logger.debug(u"Stopping checker for %(name)s", vars(self))
451
514
os.kill(self.checker.pid, signal.SIGTERM)
453
516
#if self.checker.poll() is None:
454
517
# os.kill(self.checker.pid, signal.SIGKILL)
455
518
except OSError, error:
456
519
if error.errno != errno.ESRCH: # No such process
458
521
self.checker = None
460
self.PropertyChanged(dbus.String(u"checker_running"),
461
dbus.Boolean(False, variant_level=1))
463
def still_valid(self):
464
"""Has the timeout not yet passed for this client?"""
465
if not getattr(self, "enabled", False):
467
now = datetime.datetime.utcnow()
468
if self.last_checked_ok is None:
469
return now < (self.created + self.timeout)
471
return now < (self.last_checked_ok + self.timeout)
473
## D-Bus methods & signals
523
def dbus_service_property(dbus_interface, signature=u"v",
524
access=u"readwrite", byte_arrays=False):
525
"""Decorators for marking methods of a DBusObjectWithProperties to
526
become properties on the D-Bus.
528
The decorated method will be called with no arguments by "Get"
529
and with one argument by "Set".
531
The parameters, where they are supported, are the same as
532
dbus.service.method, except there is only "signature", since the
533
type from Get() and the type sent to Set() is the same.
535
# Encoding deeply encoded byte arrays is not supported yet by the
536
# "Set" method, so we fail early here:
537
if byte_arrays and signature != u"ay":
538
raise ValueError(u"Byte arrays not supported for non-'ay'"
539
u" signature %r" % signature)
541
func._dbus_is_property = True
542
func._dbus_interface = dbus_interface
543
func._dbus_signature = signature
544
func._dbus_access = access
545
func._dbus_name = func.__name__
546
if func._dbus_name.endswith(u"_dbus_property"):
547
func._dbus_name = func._dbus_name[:-14]
548
func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
553
class DBusPropertyException(dbus.exceptions.DBusException):
554
"""A base class for D-Bus property-related exceptions
556
def __unicode__(self):
557
return unicode(str(self))
560
class DBusPropertyAccessException(DBusPropertyException):
561
"""A property's access permissions disallows an operation.
566
class DBusPropertyNotFound(DBusPropertyException):
567
"""An attempt was made to access a non-existing property.
572
class DBusObjectWithProperties(dbus.service.Object):
573
"""A D-Bus object with properties.
575
Classes inheriting from this can use the dbus_service_property
576
decorator to expose methods as D-Bus properties. It exposes the
577
standard Get(), Set(), and GetAll() methods on the D-Bus.
581
def _is_dbus_property(obj):
582
return getattr(obj, u"_dbus_is_property", False)
584
def _get_all_dbus_properties(self):
585
"""Returns a generator of (name, attribute) pairs
587
return ((prop._dbus_name, prop)
589
inspect.getmembers(self, self._is_dbus_property))
591
def _get_dbus_property(self, interface_name, property_name):
592
"""Returns a bound method if one exists which is a D-Bus
593
property with the specified name and interface.
595
for name in (property_name,
596
property_name + u"_dbus_property"):
597
prop = getattr(self, name, None)
599
or not self._is_dbus_property(prop)
600
or prop._dbus_name != property_name
601
or (interface_name and prop._dbus_interface
602
and interface_name != prop._dbus_interface)):
606
raise DBusPropertyNotFound(self.dbus_object_path + u":"
607
+ interface_name + u"."
610
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
612
def Get(self, interface_name, property_name):
613
"""Standard D-Bus property Get() method, see D-Bus standard.
615
prop = self._get_dbus_property(interface_name, property_name)
616
if prop._dbus_access == u"write":
617
raise DBusPropertyAccessException(property_name)
619
if not hasattr(value, u"variant_level"):
621
return type(value)(value, variant_level=value.variant_level+1)
623
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
624
def Set(self, interface_name, property_name, value):
625
"""Standard D-Bus property Set() method, see D-Bus standard.
627
prop = self._get_dbus_property(interface_name, property_name)
628
if prop._dbus_access == u"read":
629
raise DBusPropertyAccessException(property_name)
630
if prop._dbus_get_args_options[u"byte_arrays"]:
631
# The byte_arrays option is not supported yet on
632
# signatures other than "ay".
633
if prop._dbus_signature != u"ay":
635
value = dbus.ByteArray(''.join(unichr(byte)
639
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
640
out_signature=u"a{sv}")
641
def GetAll(self, interface_name):
642
"""Standard D-Bus property GetAll() method, see D-Bus
645
Note: Will not include properties with access="write".
648
for name, prop in self._get_all_dbus_properties():
650
and interface_name != prop._dbus_interface):
651
# Interface non-empty but did not match
653
# Ignore write-only properties
654
if prop._dbus_access == u"write":
657
if not hasattr(value, u"variant_level"):
660
all[name] = type(value)(value, variant_level=
661
value.variant_level+1)
662
return dbus.Dictionary(all, signature=u"sv")
664
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
666
path_keyword='object_path',
667
connection_keyword='connection')
668
def Introspect(self, object_path, connection):
669
"""Standard D-Bus method, overloaded to insert property tags.
671
xmlstring = dbus.service.Object.Introspect(self, object_path,
674
document = xml.dom.minidom.parseString(xmlstring)
675
def make_tag(document, name, prop):
676
e = document.createElement(u"property")
677
e.setAttribute(u"name", name)
678
e.setAttribute(u"type", prop._dbus_signature)
679
e.setAttribute(u"access", prop._dbus_access)
681
for if_tag in document.getElementsByTagName(u"interface"):
682
for tag in (make_tag(document, name, prop)
684
in self._get_all_dbus_properties()
685
if prop._dbus_interface
686
== if_tag.getAttribute(u"name")):
687
if_tag.appendChild(tag)
688
# Add the names to the return values for the
689
# "org.freedesktop.DBus.Properties" methods
690
if (if_tag.getAttribute(u"name")
691
== u"org.freedesktop.DBus.Properties"):
692
for cn in if_tag.getElementsByTagName(u"method"):
693
if cn.getAttribute(u"name") == u"Get":
694
for arg in cn.getElementsByTagName(u"arg"):
695
if (arg.getAttribute(u"direction")
697
arg.setAttribute(u"name", u"value")
698
elif cn.getAttribute(u"name") == u"GetAll":
699
for arg in cn.getElementsByTagName(u"arg"):
700
if (arg.getAttribute(u"direction")
702
arg.setAttribute(u"name", u"props")
703
xmlstring = document.toxml(u"utf-8")
705
except (AttributeError, xml.dom.DOMException,
706
xml.parsers.expat.ExpatError), error:
707
logger.error(u"Failed to override Introspection method",
712
class ClientDBus(Client, DBusObjectWithProperties):
713
"""A Client class using D-Bus
716
dbus_object_path: dbus.ObjectPath
717
bus: dbus.SystemBus()
720
runtime_expansions = (Client.runtime_expansions
721
+ (u"dbus_object_path",))
723
# dbus.service.Object doesn't use super(), so we can't either.
725
def __init__(self, bus = None, *args, **kwargs):
726
self._approvals_pending = 0
728
Client.__init__(self, *args, **kwargs)
729
# Only now, when this client is initialized, can it show up on
731
client_object_name = unicode(self.name).translate(
732
{ord(u"."): ord(u"_"),
733
ord(u"-"): ord(u"_")})
734
self.dbus_object_path = (dbus.ObjectPath
735
(u"/clients/" + client_object_name))
736
DBusObjectWithProperties.__init__(self, self.bus,
737
self.dbus_object_path)
739
def _get_approvals_pending(self):
740
return self._approvals_pending
741
def _set_approvals_pending(self, value):
742
old_value = self._approvals_pending
743
self._approvals_pending = value
745
if (hasattr(self, "dbus_object_path")
746
and bval is not bool(old_value)):
747
dbus_bool = dbus.Boolean(bval, variant_level=1)
748
self.PropertyChanged(dbus.String(u"ApprovalPending"),
751
approvals_pending = property(_get_approvals_pending,
752
_set_approvals_pending)
753
del _get_approvals_pending, _set_approvals_pending
756
def _datetime_to_dbus(dt, variant_level=0):
757
"""Convert a UTC datetime.datetime() to a D-Bus type."""
758
return dbus.String(dt.isoformat(),
759
variant_level=variant_level)
762
oldstate = getattr(self, u"enabled", False)
763
r = Client.enable(self)
764
if oldstate != self.enabled:
766
self.PropertyChanged(dbus.String(u"Enabled"),
767
dbus.Boolean(True, variant_level=1))
768
self.PropertyChanged(
769
dbus.String(u"LastEnabled"),
770
self._datetime_to_dbus(self.last_enabled,
774
def disable(self, quiet = False):
775
oldstate = getattr(self, u"enabled", False)
776
r = Client.disable(self, quiet=quiet)
777
if not quiet and oldstate != self.enabled:
779
self.PropertyChanged(dbus.String(u"Enabled"),
780
dbus.Boolean(False, variant_level=1))
783
def __del__(self, *args, **kwargs):
785
self.remove_from_connection()
788
if hasattr(DBusObjectWithProperties, u"__del__"):
789
DBusObjectWithProperties.__del__(self, *args, **kwargs)
790
Client.__del__(self, *args, **kwargs)
792
def checker_callback(self, pid, condition, command,
794
self.checker_callback_tag = None
797
self.PropertyChanged(dbus.String(u"CheckerRunning"),
798
dbus.Boolean(False, variant_level=1))
799
if os.WIFEXITED(condition):
800
exitstatus = os.WEXITSTATUS(condition)
802
self.CheckerCompleted(dbus.Int16(exitstatus),
803
dbus.Int64(condition),
804
dbus.String(command))
807
self.CheckerCompleted(dbus.Int16(-1),
808
dbus.Int64(condition),
809
dbus.String(command))
811
return Client.checker_callback(self, pid, condition, command,
814
def checked_ok(self, *args, **kwargs):
815
r = Client.checked_ok(self, *args, **kwargs)
817
self.PropertyChanged(
818
dbus.String(u"LastCheckedOK"),
819
(self._datetime_to_dbus(self.last_checked_ok,
823
def need_approval(self, *args, **kwargs):
824
r = Client.need_approval(self, *args, **kwargs)
826
self.PropertyChanged(
827
dbus.String(u"LastApprovalRequest"),
828
(self._datetime_to_dbus(self.last_approval_request,
832
def start_checker(self, *args, **kwargs):
833
old_checker = self.checker
834
if self.checker is not None:
835
old_checker_pid = self.checker.pid
837
old_checker_pid = None
838
r = Client.start_checker(self, *args, **kwargs)
839
# Only if new checker process was started
840
if (self.checker is not None
841
and old_checker_pid != self.checker.pid):
843
self.CheckerStarted(self.current_checker_command)
844
self.PropertyChanged(
845
dbus.String(u"CheckerRunning"),
846
dbus.Boolean(True, variant_level=1))
849
def stop_checker(self, *args, **kwargs):
850
old_checker = getattr(self, u"checker", None)
851
r = Client.stop_checker(self, *args, **kwargs)
852
if (old_checker is not None
853
and getattr(self, u"checker", None) is None):
854
self.PropertyChanged(dbus.String(u"CheckerRunning"),
855
dbus.Boolean(False, variant_level=1))
858
def _reset_approved(self):
859
self._approved = None
862
def approve(self, value=True):
863
self.send_changedstate()
864
self._approved = value
865
gobject.timeout_add(self._timedelta_to_milliseconds
866
(self.approval_duration),
867
self._reset_approved)
870
## D-Bus methods, signals & properties
474
871
_interface = u"se.bsnet.fukt.Mandos.Client"
477
CheckedOK = dbus.service.method(_interface)(checked_ok)
478
CheckedOK.__name__ = "CheckedOK"
480
875
# CheckerCompleted - signal
481
@dbus.service.signal(_interface, signature="nxs")
876
@dbus.service.signal(_interface, signature=u"nxs")
482
877
def CheckerCompleted(self, exitcode, waitstatus, command):
486
881
# CheckerStarted - signal
487
@dbus.service.signal(_interface, signature="s")
882
@dbus.service.signal(_interface, signature=u"s")
488
883
def CheckerStarted(self, command):
492
# GetAllProperties - method
493
@dbus.service.method(_interface, out_signature="a{sv}")
494
def GetAllProperties(self):
496
return dbus.Dictionary({
498
dbus.String(self.name, variant_level=1),
499
dbus.String("fingerprint"):
500
dbus.String(self.fingerprint, variant_level=1),
502
dbus.String(self.host, variant_level=1),
503
dbus.String("created"):
504
_datetime_to_dbus(self.created, variant_level=1),
505
dbus.String("last_enabled"):
506
(_datetime_to_dbus(self.last_enabled,
508
if self.last_enabled is not None
509
else dbus.Boolean(False, variant_level=1)),
510
dbus.String("enabled"):
511
dbus.Boolean(self.enabled, variant_level=1),
512
dbus.String("last_checked_ok"):
513
(_datetime_to_dbus(self.last_checked_ok,
515
if self.last_checked_ok is not None
516
else dbus.Boolean (False, variant_level=1)),
517
dbus.String("timeout"):
518
dbus.UInt64(self.timeout_milliseconds(),
520
dbus.String("interval"):
521
dbus.UInt64(self.interval_milliseconds(),
523
dbus.String("checker"):
524
dbus.String(self.checker_command,
526
dbus.String("checker_running"):
527
dbus.Boolean(self.checker is not None,
529
dbus.String("object_path"):
530
dbus.ObjectPath(self.dbus_object_path,
534
# IsStillValid - method
535
IsStillValid = (dbus.service.method(_interface, out_signature="b")
537
IsStillValid.__name__ = "IsStillValid"
539
887
# PropertyChanged - signal
540
@dbus.service.signal(_interface, signature="sv")
888
@dbus.service.signal(_interface, signature=u"sv")
541
889
def PropertyChanged(self, property, value):
545
# SetChecker - method
546
@dbus.service.method(_interface, in_signature="s")
547
def SetChecker(self, checker):
548
"D-Bus setter method"
549
self.checker_command = checker
551
self.PropertyChanged(dbus.String(u"checker"),
552
dbus.String(self.checker_command,
556
@dbus.service.method(_interface, in_signature="s")
557
def SetHost(self, host):
558
"D-Bus setter method"
561
self.PropertyChanged(dbus.String(u"host"),
562
dbus.String(self.host, variant_level=1))
564
# SetInterval - method
565
@dbus.service.method(_interface, in_signature="t")
566
def SetInterval(self, milliseconds):
567
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
569
self.PropertyChanged(dbus.String(u"interval"),
570
(dbus.UInt64(self.interval_milliseconds(),
574
@dbus.service.method(_interface, in_signature="ay",
576
def SetSecret(self, secret):
577
"D-Bus setter method"
578
self.secret = str(secret)
580
# SetTimeout - method
581
@dbus.service.method(_interface, in_signature="t")
582
def SetTimeout(self, milliseconds):
583
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
585
self.PropertyChanged(dbus.String(u"timeout"),
586
(dbus.UInt64(self.timeout_milliseconds(),
894
@dbus.service.signal(_interface)
897
Is sent after a successful transfer of secret from the Mandos
898
server to mandos-client
903
@dbus.service.signal(_interface, signature=u"s")
904
def Rejected(self, reason):
908
# NeedApproval - signal
909
@dbus.service.signal(_interface, signature=u"tb")
910
def NeedApproval(self, timeout, default):
912
return self.need_approval()
917
@dbus.service.method(_interface, in_signature=u"b")
918
def Approve(self, value):
922
@dbus.service.method(_interface)
924
return self.checked_ok()
589
926
# Enable - method
590
Enable = dbus.service.method(_interface)(enable)
591
Enable.__name__ = "Enable"
927
@dbus.service.method(_interface)
593
932
# StartChecker - method
594
933
@dbus.service.method(_interface)
605
944
# StopChecker - method
606
StopChecker = dbus.service.method(_interface)(stop_checker)
607
StopChecker.__name__ = "StopChecker"
945
@dbus.service.method(_interface)
946
def StopChecker(self):
951
# ApprovalPending - property
952
@dbus_service_property(_interface, signature=u"b", access=u"read")
953
def ApprovalPending_dbus_property(self):
954
return dbus.Boolean(bool(self.approvals_pending))
956
# ApprovedByDefault - property
957
@dbus_service_property(_interface, signature=u"b",
959
def ApprovedByDefault_dbus_property(self, value=None):
960
if value is None: # get
961
return dbus.Boolean(self.approved_by_default)
962
self.approved_by_default = bool(value)
964
self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
965
dbus.Boolean(value, variant_level=1))
967
# ApprovalDelay - property
968
@dbus_service_property(_interface, signature=u"t",
970
def ApprovalDelay_dbus_property(self, value=None):
971
if value is None: # get
972
return dbus.UInt64(self.approval_delay_milliseconds())
973
self.approval_delay = datetime.timedelta(0, 0, 0, value)
975
self.PropertyChanged(dbus.String(u"ApprovalDelay"),
976
dbus.UInt64(value, variant_level=1))
978
# ApprovalDuration - property
979
@dbus_service_property(_interface, signature=u"t",
981
def ApprovalDuration_dbus_property(self, value=None):
982
if value is None: # get
983
return dbus.UInt64(self._timedelta_to_milliseconds(
984
self.approval_duration))
985
self.approval_duration = datetime.timedelta(0, 0, 0, value)
987
self.PropertyChanged(dbus.String(u"ApprovalDuration"),
988
dbus.UInt64(value, variant_level=1))
991
@dbus_service_property(_interface, signature=u"s", access=u"read")
992
def Name_dbus_property(self):
993
return dbus.String(self.name)
995
# Fingerprint - property
996
@dbus_service_property(_interface, signature=u"s", access=u"read")
997
def Fingerprint_dbus_property(self):
998
return dbus.String(self.fingerprint)
1001
@dbus_service_property(_interface, signature=u"s",
1002
access=u"readwrite")
1003
def Host_dbus_property(self, value=None):
1004
if value is None: # get
1005
return dbus.String(self.host)
1008
self.PropertyChanged(dbus.String(u"Host"),
1009
dbus.String(value, variant_level=1))
1011
# Created - property
1012
@dbus_service_property(_interface, signature=u"s", access=u"read")
1013
def Created_dbus_property(self):
1014
return dbus.String(self._datetime_to_dbus(self.created))
1016
# LastEnabled - property
1017
@dbus_service_property(_interface, signature=u"s", access=u"read")
1018
def LastEnabled_dbus_property(self):
1019
if self.last_enabled is None:
1020
return dbus.String(u"")
1021
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1023
# Enabled - property
1024
@dbus_service_property(_interface, signature=u"b",
1025
access=u"readwrite")
1026
def Enabled_dbus_property(self, value=None):
1027
if value is None: # get
1028
return dbus.Boolean(self.enabled)
1034
# LastCheckedOK - property
1035
@dbus_service_property(_interface, signature=u"s",
1036
access=u"readwrite")
1037
def LastCheckedOK_dbus_property(self, value=None):
1038
if value is not None:
1041
if self.last_checked_ok is None:
1042
return dbus.String(u"")
1043
return dbus.String(self._datetime_to_dbus(self
1046
# LastApprovalRequest - property
1047
@dbus_service_property(_interface, signature=u"s", access=u"read")
1048
def LastApprovalRequest_dbus_property(self):
1049
if self.last_approval_request is None:
1050
return dbus.String(u"")
1051
return dbus.String(self.
1052
_datetime_to_dbus(self
1053
.last_approval_request))
1055
# Timeout - property
1056
@dbus_service_property(_interface, signature=u"t",
1057
access=u"readwrite")
1058
def Timeout_dbus_property(self, value=None):
1059
if value is None: # get
1060
return dbus.UInt64(self.timeout_milliseconds())
1061
self.timeout = datetime.timedelta(0, 0, 0, value)
1063
self.PropertyChanged(dbus.String(u"Timeout"),
1064
dbus.UInt64(value, variant_level=1))
1065
if getattr(self, u"disable_initiator_tag", None) is None:
1067
# Reschedule timeout
1068
gobject.source_remove(self.disable_initiator_tag)
1069
self.disable_initiator_tag = None
1070
time_to_die = (self.
1071
_timedelta_to_milliseconds((self
1076
if time_to_die <= 0:
1077
# The timeout has passed
1080
self.disable_initiator_tag = (gobject.timeout_add
1081
(time_to_die, self.disable))
1083
# Interval - property
1084
@dbus_service_property(_interface, signature=u"t",
1085
access=u"readwrite")
1086
def Interval_dbus_property(self, value=None):
1087
if value is None: # get
1088
return dbus.UInt64(self.interval_milliseconds())
1089
self.interval = datetime.timedelta(0, 0, 0, value)
1091
self.PropertyChanged(dbus.String(u"Interval"),
1092
dbus.UInt64(value, variant_level=1))
1093
if getattr(self, u"checker_initiator_tag", None) is None:
1095
# Reschedule checker run
1096
gobject.source_remove(self.checker_initiator_tag)
1097
self.checker_initiator_tag = (gobject.timeout_add
1098
(value, self.start_checker))
1099
self.start_checker() # Start one now, too
1101
# Checker - property
1102
@dbus_service_property(_interface, signature=u"s",
1103
access=u"readwrite")
1104
def Checker_dbus_property(self, value=None):
1105
if value is None: # get
1106
return dbus.String(self.checker_command)
1107
self.checker_command = value
1109
self.PropertyChanged(dbus.String(u"Checker"),
1110
dbus.String(self.checker_command,
1113
# CheckerRunning - property
1114
@dbus_service_property(_interface, signature=u"b",
1115
access=u"readwrite")
1116
def CheckerRunning_dbus_property(self, value=None):
1117
if value is None: # get
1118
return dbus.Boolean(self.checker is not None)
1120
self.start_checker()
1124
# ObjectPath - property
1125
@dbus_service_property(_interface, signature=u"o", access=u"read")
1126
def ObjectPath_dbus_property(self):
1127
return self.dbus_object_path # is already a dbus.ObjectPath
1130
@dbus_service_property(_interface, signature=u"ay",
1131
access=u"write", byte_arrays=True)
1132
def Secret_dbus_property(self, value):
1133
self.secret = str(value)
612
def peer_certificate(session):
613
"Return the peer's OpenPGP certificate as a bytestring"
614
# If not an OpenPGP certificate...
615
if (gnutls.library.functions
616
.gnutls_certificate_type_get(session._c_object)
617
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
618
# ...do the normal thing
619
return session.peer_certificate
620
list_size = ctypes.c_uint(1)
621
cert_list = (gnutls.library.functions
622
.gnutls_certificate_get_peers
623
(session._c_object, ctypes.byref(list_size)))
624
if not bool(cert_list) and list_size.value != 0:
625
raise gnutls.errors.GNUTLSError("error getting peer"
627
if list_size.value == 0:
630
return ctypes.string_at(cert.data, cert.size)
633
def fingerprint(openpgp):
634
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
635
# New GnuTLS "datum" with the OpenPGP public key
636
datum = (gnutls.library.types
637
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
640
ctypes.c_uint(len(openpgp))))
641
# New empty GnuTLS certificate
642
crt = gnutls.library.types.gnutls_openpgp_crt_t()
643
(gnutls.library.functions
644
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
645
# Import the OpenPGP public key into the certificate
646
(gnutls.library.functions
647
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
648
gnutls.library.constants
649
.GNUTLS_OPENPGP_FMT_RAW))
650
# Verify the self signature in the key
651
crtverify = ctypes.c_uint()
652
(gnutls.library.functions
653
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
654
if crtverify.value != 0:
655
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
656
raise gnutls.errors.CertificateSecurityError("Verify failed")
657
# New buffer for the fingerprint
658
buf = ctypes.create_string_buffer(20)
659
buf_len = ctypes.c_size_t()
660
# Get the fingerprint from the certificate into the buffer
661
(gnutls.library.functions
662
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
663
ctypes.byref(buf_len)))
664
# Deinit the certificate
665
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
666
# Convert the buffer to a Python bytestring
667
fpr = ctypes.string_at(buf, buf_len.value)
668
# Convert the bytestring to hexadecimal notation
669
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
673
class TCP_handler(SocketServer.BaseRequestHandler, object):
674
"""A TCP request handler class.
675
Instantiated by IPv6_TCPServer for each request to handle it.
1138
class ProxyClient(object):
1139
def __init__(self, child_pipe, fpr, address):
1140
self._pipe = child_pipe
1141
self._pipe.send(('init', fpr, address))
1142
if not self._pipe.recv():
1145
def __getattribute__(self, name):
1146
if(name == '_pipe'):
1147
return super(ProxyClient, self).__getattribute__(name)
1148
self._pipe.send(('getattr', name))
1149
data = self._pipe.recv()
1150
if data[0] == 'data':
1152
if data[0] == 'function':
1153
def func(*args, **kwargs):
1154
self._pipe.send(('funcall', name, args, kwargs))
1155
return self._pipe.recv()[1]
1158
def __setattr__(self, name, value):
1159
if(name == '_pipe'):
1160
return super(ProxyClient, self).__setattr__(name, value)
1161
self._pipe.send(('setattr', name, value))
1164
class ClientHandler(socketserver.BaseRequestHandler, object):
1165
"""A class to handle client connections.
1167
Instantiated once for each connection to handle it.
676
1168
Note: This will run in its own forked process."""
678
1170
def handle(self):
679
logger.info(u"TCP connection from: %s",
680
unicode(self.client_address))
681
session = (gnutls.connection
682
.ClientSession(self.request,
686
line = self.request.makefile().readline()
687
logger.debug(u"Protocol version: %r", line)
689
if int(line.strip().split()[0]) > 1:
691
except (ValueError, IndexError, RuntimeError), error:
692
logger.error(u"Unknown protocol version: %s", error)
695
# Note: gnutls.connection.X509Credentials is really a generic
696
# GnuTLS certificate credentials object so long as no X.509
697
# keys are added to it. Therefore, we can use it here despite
698
# using OpenPGP certificates.
700
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
701
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
703
# Use a fallback default, since this MUST be set.
704
priority = self.server.settings.get("priority", "NORMAL")
705
(gnutls.library.functions
706
.gnutls_priority_set_direct(session._c_object,
711
except gnutls.errors.GNUTLSError, error:
712
logger.warning(u"Handshake failed: %s", error)
713
# Do not run session.bye() here: the session is not
714
# established. Just abandon the request.
716
logger.debug(u"Handshake succeeded")
718
fpr = fingerprint(peer_certificate(session))
719
except (TypeError, gnutls.errors.GNUTLSError), error:
720
logger.warning(u"Bad certificate: %s", error)
723
logger.debug(u"Fingerprint: %s", fpr)
725
for c in self.server.clients:
726
if c.fingerprint == fpr:
730
logger.warning(u"Client not found for fingerprint: %s",
734
# Have to check if client.still_valid(), since it is possible
735
# that the client timed out while establishing the GnuTLS
737
if not client.still_valid():
738
logger.warning(u"Client %(name)s is invalid",
742
## This won't work here, since we're in a fork.
743
# client.checked_ok()
745
while sent_size < len(client.secret):
746
sent = session.send(client.secret[sent_size:])
747
logger.debug(u"Sent: %d, remaining: %d",
748
sent, len(client.secret)
749
- (sent_size + sent))
754
class IPv6_TCPServer(SocketServer.ForkingMixIn,
755
SocketServer.TCPServer, object):
756
"""IPv6-capable TCP server. Accepts 'None' as address and/or port.
1171
with contextlib.closing(self.server.child_pipe) as child_pipe:
1172
logger.info(u"TCP connection from: %s",
1173
unicode(self.client_address))
1174
logger.debug(u"Pipe FD: %d",
1175
self.server.child_pipe.fileno())
1177
session = (gnutls.connection
1178
.ClientSession(self.request,
1180
.X509Credentials()))
1182
# Note: gnutls.connection.X509Credentials is really a
1183
# generic GnuTLS certificate credentials object so long as
1184
# no X.509 keys are added to it. Therefore, we can use it
1185
# here despite using OpenPGP certificates.
1187
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1188
# u"+AES-256-CBC", u"+SHA1",
1189
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1191
# Use a fallback default, since this MUST be set.
1192
priority = self.server.gnutls_priority
1193
if priority is None:
1194
priority = u"NORMAL"
1195
(gnutls.library.functions
1196
.gnutls_priority_set_direct(session._c_object,
1199
# Start communication using the Mandos protocol
1200
# Get protocol number
1201
line = self.request.makefile().readline()
1202
logger.debug(u"Protocol version: %r", line)
1204
if int(line.strip().split()[0]) > 1:
1206
except (ValueError, IndexError, RuntimeError), error:
1207
logger.error(u"Unknown protocol version: %s", error)
1210
# Start GnuTLS connection
1213
except gnutls.errors.GNUTLSError, error:
1214
logger.warning(u"Handshake failed: %s", error)
1215
# Do not run session.bye() here: the session is not
1216
# established. Just abandon the request.
1218
logger.debug(u"Handshake succeeded")
1220
approval_required = False
1223
fpr = self.fingerprint(self.peer_certificate
1225
except (TypeError, gnutls.errors.GNUTLSError), error:
1226
logger.warning(u"Bad certificate: %s", error)
1228
logger.debug(u"Fingerprint: %s", fpr)
1231
client = ProxyClient(child_pipe, fpr,
1232
self.client_address)
1236
if client.approval_delay:
1237
delay = client.approval_delay
1238
client.approvals_pending += 1
1239
approval_required = True
1242
if not client.enabled:
1243
logger.warning(u"Client %s is disabled",
1245
if self.server.use_dbus:
1247
client.Rejected("Disabled")
1250
if client._approved or not client.approval_delay:
1251
#We are approved or approval is disabled
1253
elif client._approved is None:
1254
logger.info(u"Client %s needs approval",
1256
if self.server.use_dbus:
1258
client.NeedApproval(
1259
client.approval_delay_milliseconds(),
1260
client.approved_by_default)
1262
logger.warning(u"Client %s was not approved",
1264
if self.server.use_dbus:
1266
client.Rejected("Denied")
1269
#wait until timeout or approved
1270
#x = float(client._timedelta_to_milliseconds(delay))
1271
time = datetime.datetime.now()
1272
client.changedstate.acquire()
1273
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1274
client.changedstate.release()
1275
time2 = datetime.datetime.now()
1276
if (time2 - time) >= delay:
1277
if not client.approved_by_default:
1278
logger.warning("Client %s timed out while"
1279
" waiting for approval",
1281
if self.server.use_dbus:
1283
client.Rejected("Approval timed out")
1288
delay -= time2 - time
1291
while sent_size < len(client.secret):
1293
sent = session.send(client.secret[sent_size:])
1294
except (gnutls.errors.GNUTLSError), error:
1295
logger.warning("gnutls send failed")
1297
logger.debug(u"Sent: %d, remaining: %d",
1298
sent, len(client.secret)
1299
- (sent_size + sent))
1302
logger.info(u"Sending secret to %s", client.name)
1303
# bump the timeout as if seen
1305
if self.server.use_dbus:
1310
if approval_required:
1311
client.approvals_pending -= 1
1314
except (gnutls.errors.GNUTLSError), error:
1315
logger.warning("GnuTLS bye failed")
1318
def peer_certificate(session):
1319
"Return the peer's OpenPGP certificate as a bytestring"
1320
# If not an OpenPGP certificate...
1321
if (gnutls.library.functions
1322
.gnutls_certificate_type_get(session._c_object)
1323
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1324
# ...do the normal thing
1325
return session.peer_certificate
1326
list_size = ctypes.c_uint(1)
1327
cert_list = (gnutls.library.functions
1328
.gnutls_certificate_get_peers
1329
(session._c_object, ctypes.byref(list_size)))
1330
if not bool(cert_list) and list_size.value != 0:
1331
raise gnutls.errors.GNUTLSError(u"error getting peer"
1333
if list_size.value == 0:
1336
return ctypes.string_at(cert.data, cert.size)
1339
def fingerprint(openpgp):
1340
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1341
# New GnuTLS "datum" with the OpenPGP public key
1342
datum = (gnutls.library.types
1343
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1346
ctypes.c_uint(len(openpgp))))
1347
# New empty GnuTLS certificate
1348
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1349
(gnutls.library.functions
1350
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1351
# Import the OpenPGP public key into the certificate
1352
(gnutls.library.functions
1353
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1354
gnutls.library.constants
1355
.GNUTLS_OPENPGP_FMT_RAW))
1356
# Verify the self signature in the key
1357
crtverify = ctypes.c_uint()
1358
(gnutls.library.functions
1359
.gnutls_openpgp_crt_verify_self(crt, 0,
1360
ctypes.byref(crtverify)))
1361
if crtverify.value != 0:
1362
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1363
raise (gnutls.errors.CertificateSecurityError
1365
# New buffer for the fingerprint
1366
buf = ctypes.create_string_buffer(20)
1367
buf_len = ctypes.c_size_t()
1368
# Get the fingerprint from the certificate into the buffer
1369
(gnutls.library.functions
1370
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1371
ctypes.byref(buf_len)))
1372
# Deinit the certificate
1373
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1374
# Convert the buffer to a Python bytestring
1375
fpr = ctypes.string_at(buf, buf_len.value)
1376
# Convert the bytestring to hexadecimal notation
1377
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1381
class MultiprocessingMixIn(object):
1382
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1383
def sub_process_main(self, request, address):
1385
self.finish_request(request, address)
1387
self.handle_error(request, address)
1388
self.close_request(request)
1390
def process_request(self, request, address):
1391
"""Start a new process to process the request."""
1392
multiprocessing.Process(target = self.sub_process_main,
1393
args = (request, address)).start()
1395
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1396
""" adds a pipe to the MixIn """
1397
def process_request(self, request, client_address):
1398
"""Overrides and wraps the original process_request().
1400
This function creates a new pipe in self.pipe
1402
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1404
super(MultiprocessingMixInWithPipe,
1405
self).process_request(request, client_address)
1406
self.child_pipe.close()
1407
self.add_pipe(parent_pipe)
1409
def add_pipe(self, parent_pipe):
1410
"""Dummy function; override as necessary"""
1413
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1414
socketserver.TCPServer, object):
1415
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
758
settings: Server settings
759
clients: Set() of Client objects
760
1418
enabled: Boolean; whether this server is activated yet
1419
interface: None or a network interface name (string)
1420
use_ipv6: Boolean; to use IPv6 or not
762
address_family = socket.AF_INET6
763
def __init__(self, *args, **kwargs):
764
if "settings" in kwargs:
765
self.settings = kwargs["settings"]
766
del kwargs["settings"]
767
if "clients" in kwargs:
768
self.clients = kwargs["clients"]
769
del kwargs["clients"]
770
if "use_ipv6" in kwargs:
771
if not kwargs["use_ipv6"]:
772
self.address_family = socket.AF_INET
773
del kwargs["use_ipv6"]
775
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
1422
def __init__(self, server_address, RequestHandlerClass,
1423
interface=None, use_ipv6=True):
1424
self.interface = interface
1426
self.address_family = socket.AF_INET6
1427
socketserver.TCPServer.__init__(self, server_address,
1428
RequestHandlerClass)
776
1429
def server_bind(self):
777
1430
"""This overrides the normal server_bind() function
778
1431
to bind to an interface if one was specified, and also NOT to
779
1432
bind to an address or port if they were not specified."""
780
if self.settings["interface"]:
781
# 25 is from /usr/include/asm-i486/socket.h
782
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
784
self.socket.setsockopt(socket.SOL_SOCKET,
786
self.settings["interface"])
787
except socket.error, error:
788
if error[0] == errno.EPERM:
789
logger.error(u"No permission to"
790
u" bind to interface %s",
791
self.settings["interface"])
1433
if self.interface is not None:
1434
if SO_BINDTODEVICE is None:
1435
logger.error(u"SO_BINDTODEVICE does not exist;"
1436
u" cannot bind to interface %s",
1440
self.socket.setsockopt(socket.SOL_SOCKET,
1444
except socket.error, error:
1445
if error[0] == errno.EPERM:
1446
logger.error(u"No permission to"
1447
u" bind to interface %s",
1449
elif error[0] == errno.ENOPROTOOPT:
1450
logger.error(u"SO_BINDTODEVICE not available;"
1451
u" cannot bind to interface %s",
794
1455
# Only bind(2) the socket if we really need to.
795
1456
if self.server_address[0] or self.server_address[1]:
796
1457
if not self.server_address[0]:
797
1458
if self.address_family == socket.AF_INET6:
798
any_address = "::" # in6addr_any
1459
any_address = u"::" # in6addr_any
800
1461
any_address = socket.INADDR_ANY
801
1462
self.server_address = (any_address,
963
1709
# Default values for config file for server-global settings
964
server_defaults = { "interface": "",
969
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
970
"servicename": "Mandos",
1710
server_defaults = { u"interface": u"",
1715
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1716
u"servicename": u"Mandos",
1717
u"use_dbus": u"True",
1718
u"use_ipv6": u"True",
975
1722
# Parse config file for server-global settings
976
server_config = ConfigParser.SafeConfigParser(server_defaults)
1723
server_config = configparser.SafeConfigParser(server_defaults)
977
1724
del server_defaults
978
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1725
server_config.read(os.path.join(options.configdir,
979
1727
# Convert the SafeConfigParser object to a dict
980
1728
server_settings = server_config.defaults()
981
1729
# Use the appropriate methods on the non-string config options
982
server_settings["debug"] = server_config.getboolean("DEFAULT",
984
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
986
server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1730
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1731
server_settings[option] = server_config.getboolean(u"DEFAULT",
988
1733
if server_settings["port"]:
989
server_settings["port"] = server_config.getint("DEFAULT",
1734
server_settings["port"] = server_config.getint(u"DEFAULT",
991
1736
del server_config
993
1738
# Override the settings from the config file with command line
994
1739
# options, if set.
995
for option in ("interface", "address", "port", "debug",
996
"priority", "servicename", "configdir",
997
"use_dbus", "use_ipv6"):
1740
for option in (u"interface", u"address", u"port", u"debug",
1741
u"priority", u"servicename", u"configdir",
1742
u"use_dbus", u"use_ipv6", u"debuglevel"):
998
1743
value = getattr(options, option)
999
1744
if value is not None:
1000
1745
server_settings[option] = value
1747
# Force all strings to be unicode
1748
for option in server_settings.keys():
1749
if type(server_settings[option]) is str:
1750
server_settings[option] = unicode(server_settings[option])
1002
1751
# Now we have our good server settings in "server_settings"
1753
##################################################################
1004
1755
# For convenience
1005
debug = server_settings["debug"]
1006
use_dbus = server_settings["use_dbus"]
1007
use_ipv6 = server_settings["use_ipv6"]
1010
syslogger.setLevel(logging.WARNING)
1011
console.setLevel(logging.WARNING)
1013
if server_settings["servicename"] != "Mandos":
1756
debug = server_settings[u"debug"]
1757
debuglevel = server_settings[u"debuglevel"]
1758
use_dbus = server_settings[u"use_dbus"]
1759
use_ipv6 = server_settings[u"use_ipv6"]
1761
if server_settings[u"servicename"] != u"Mandos":
1014
1762
syslogger.setFormatter(logging.Formatter
1015
('Mandos (%s): %%(levelname)s:'
1017
% server_settings["servicename"]))
1763
(u'Mandos (%s) [%%(process)d]:'
1764
u' %%(levelname)s: %%(message)s'
1765
% server_settings[u"servicename"]))
1019
1767
# Parse config file with clients
1020
client_defaults = { "timeout": "1h",
1022
"checker": "fping -q -- %%(host)s",
1768
client_defaults = { u"timeout": u"1h",
1770
u"checker": u"fping -q -- %%(host)s",
1772
u"approval_delay": u"0s",
1773
u"approval_duration": u"1s",
1025
client_config = ConfigParser.SafeConfigParser(client_defaults)
1026
client_config.read(os.path.join(server_settings["configdir"],
1030
tcp_server = IPv6_TCPServer((server_settings["address"],
1031
server_settings["port"]),
1033
settings=server_settings,
1034
clients=clients, use_ipv6=use_ipv6)
1035
pidfilename = "/var/run/mandos.pid"
1037
pidfile = open(pidfilename, "w")
1039
logger.error("Could not open file %r", pidfilename)
1042
uid = pwd.getpwnam("_mandos").pw_uid
1043
gid = pwd.getpwnam("_mandos").pw_gid
1775
client_config = configparser.SafeConfigParser(client_defaults)
1776
client_config.read(os.path.join(server_settings[u"configdir"],
1779
global mandos_dbus_service
1780
mandos_dbus_service = None
1782
tcp_server = MandosServer((server_settings[u"address"],
1783
server_settings[u"port"]),
1785
interface=(server_settings[u"interface"]
1789
server_settings[u"priority"],
1792
pidfilename = u"/var/run/mandos.pid"
1794
pidfile = open(pidfilename, u"w")
1796
logger.error(u"Could not open file %r", pidfilename)
1799
uid = pwd.getpwnam(u"_mandos").pw_uid
1800
gid = pwd.getpwnam(u"_mandos").pw_gid
1044
1801
except KeyError:
1046
uid = pwd.getpwnam("mandos").pw_uid
1047
gid = pwd.getpwnam("mandos").pw_gid
1803
uid = pwd.getpwnam(u"mandos").pw_uid
1804
gid = pwd.getpwnam(u"mandos").pw_gid
1048
1805
except KeyError:
1050
uid = pwd.getpwnam("nobody").pw_uid
1051
gid = pwd.getpwnam("nogroup").pw_gid
1807
uid = pwd.getpwnam(u"nobody").pw_uid
1808
gid = pwd.getpwnam(u"nobody").pw_gid
1052
1809
except KeyError:
1059
1816
if error[0] != errno.EPERM:
1062
# Enable all possible GnuTLS debugging
1819
if not debug and not debuglevel:
1820
syslogger.setLevel(logging.WARNING)
1821
console.setLevel(logging.WARNING)
1823
level = getattr(logging, debuglevel.upper())
1824
syslogger.setLevel(level)
1825
console.setLevel(level)
1828
# Enable all possible GnuTLS debugging
1064
1830
# "Use a log level over 10 to enable all debugging options."
1065
1831
# - GnuTLS manual
1066
1832
gnutls.library.functions.gnutls_global_set_log_level(11)
1068
1834
@gnutls.library.types.gnutls_log_func
1069
1835
def debug_gnutls(level, string):
1070
logger.debug("GnuTLS: %s", string[:-1])
1836
logger.debug(u"GnuTLS: %s", string[:-1])
1072
1838
(gnutls.library.functions
1073
1839
.gnutls_global_set_log_function(debug_gnutls))
1841
# Redirect stdin so all checkers get /dev/null
1842
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1843
os.dup2(null, sys.stdin.fileno())
1847
# No console logging
1848
logger.removeHandler(console)
1076
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1077
service = AvahiService(name = server_settings["servicename"],
1078
servicetype = "_mandos._tcp",
1079
protocol = protocol)
1080
if server_settings["interface"]:
1081
service.interface = (if_nametoindex
1082
(server_settings["interface"]))
1084
1851
global main_loop
1087
1852
# From the Avahi example code
1088
1853
DBusGMainLoop(set_as_default=True )
1089
1854
main_loop = gobject.MainLoop()
1090
1855
bus = dbus.SystemBus()
1091
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1092
avahi.DBUS_PATH_SERVER),
1093
avahi.DBUS_INTERFACE_SERVER)
1094
1856
# End of Avahi example code
1096
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1098
clients.update(Set(Client(name = section,
1100
= dict(client_config.items(section)),
1101
use_dbus = use_dbus)
1102
for section in client_config.sections()))
1104
logger.warning(u"No clients defined")
1107
# Redirect stdin so all checkers get /dev/null
1108
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1109
os.dup2(null, sys.stdin.fileno())
1113
# No console logging
1114
logger.removeHandler(console)
1859
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1860
bus, do_not_queue=True)
1861
except dbus.exceptions.NameExistsException, e:
1862
logger.error(unicode(e) + u", disabling D-Bus")
1864
server_settings[u"use_dbus"] = False
1865
tcp_server.use_dbus = False
1866
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1867
service = AvahiService(name = server_settings[u"servicename"],
1868
servicetype = u"_mandos._tcp",
1869
protocol = protocol, bus = bus)
1870
if server_settings["interface"]:
1871
service.interface = (if_nametoindex
1872
(str(server_settings[u"interface"])))
1115
1875
# Close all input and output, do double fork, etc.
1120
pidfile.write(str(pid) + "\n")
1124
logger.error(u"Could not write to file %r with PID %d",
1127
# "pidfile" was never created
1132
"Cleanup function; run on exit"
1134
# From the Avahi example code
1135
if not group is None:
1138
# End of Avahi example code
1141
client = clients.pop()
1142
client.disable_hook = None
1145
atexit.register(cleanup)
1878
global multiprocessing_manager
1879
multiprocessing_manager = multiprocessing.Manager()
1881
client_class = Client
1883
client_class = functools.partial(ClientDBus, bus = bus)
1884
def client_config_items(config, section):
1885
special_settings = {
1886
"approved_by_default":
1887
lambda: config.getboolean(section,
1888
"approved_by_default"),
1890
for name, value in config.items(section):
1892
yield (name, special_settings[name]())
1896
tcp_server.clients.update(set(
1897
client_class(name = section,
1898
config= dict(client_config_items(
1899
client_config, section)))
1900
for section in client_config.sections()))
1901
if not tcp_server.clients:
1902
logger.warning(u"No clients defined")
1908
pidfile.write(str(pid) + "\n")
1911
logger.error(u"Could not write to file %r with PID %d",
1914
# "pidfile" was never created
1148
1918
signal.signal(signal.SIGINT, signal.SIG_IGN)
1149
1920
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1150
1921
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1153
class MandosServer(dbus.service.Object):
1924
class MandosDBusService(dbus.service.Object):
1154
1925
"""A D-Bus proxy object"""
1155
1926
def __init__(self):
1156
dbus.service.Object.__init__(self, bus, "/")
1927
dbus.service.Object.__init__(self, bus, u"/")
1157
1928
_interface = u"se.bsnet.fukt.Mandos"
1159
@dbus.service.signal(_interface, signature="oa{sv}")
1160
def ClientAdded(self, objpath, properties):
1164
@dbus.service.signal(_interface, signature="os")
1930
@dbus.service.signal(_interface, signature=u"o")
1931
def ClientAdded(self, objpath):
1935
@dbus.service.signal(_interface, signature=u"ss")
1936
def ClientNotFound(self, fingerprint, address):
1940
@dbus.service.signal(_interface, signature=u"os")
1165
1941
def ClientRemoved(self, objpath, name):
1169
@dbus.service.method(_interface, out_signature="ao")
1945
@dbus.service.method(_interface, out_signature=u"ao")
1170
1946
def GetAllClients(self):
1172
return dbus.Array(c.dbus_object_path for c in clients)
1948
return dbus.Array(c.dbus_object_path
1949
for c in tcp_server.clients)
1174
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1951
@dbus.service.method(_interface,
1952
out_signature=u"a{oa{sv}}")
1175
1953
def GetAllClientsWithProperties(self):
1177
1955
return dbus.Dictionary(
1178
((c.dbus_object_path, c.GetAllProperties())
1956
((c.dbus_object_path, c.GetAll(u""))
1957
for c in tcp_server.clients),
1958
signature=u"oa{sv}")
1182
@dbus.service.method(_interface, in_signature="o")
1960
@dbus.service.method(_interface, in_signature=u"o")
1183
1961
def RemoveClient(self, object_path):
1963
for c in tcp_server.clients:
1186
1964
if c.dbus_object_path == object_path:
1965
tcp_server.clients.remove(c)
1966
c.remove_from_connection()
1188
1967
# Don't signal anything except ClientRemoved
1968
c.disable(quiet=True)
1191
1969
# Emit D-Bus signal
1192
1970
self.ClientRemoved(object_path, c.name)
1972
raise KeyError(object_path)
1198
mandos_server = MandosServer()
1200
for client in clients:
1976
mandos_dbus_service = MandosDBusService()
1979
"Cleanup function; run on exit"
1982
while tcp_server.clients:
1983
client = tcp_server.clients.pop()
1985
client.remove_from_connection()
1986
client.disable_hook = None
1987
# Don't signal anything except ClientRemoved
1988
client.disable(quiet=True)
1991
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1994
atexit.register(cleanup)
1996
for client in tcp_server.clients:
1202
1998
# Emit D-Bus signal
1203
mandos_server.ClientAdded(client.dbus_object_path,
1204
client.GetAllProperties())
1999
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1205
2000
client.enable()
1207
2002
tcp_server.enable()