149
125
self.rename_count = 0
150
126
self.max_renames = max_renames
151
self.protocol = protocol
152
self.group = None # our entry group
155
self.entry_group_state_changed_match = None
156
127
def rename(self):
157
128
"""Derived from the Avahi example code"""
158
129
if self.rename_count >= self.max_renames:
159
logger.critical("No suitable Zeroconf service name found"
160
" after %i retries, exiting.",
130
logger.critical(u"No suitable Zeroconf service name found"
131
u" after %i retries, exiting.",
161
132
self.rename_count)
162
raise AvahiServiceError("Too many renames")
163
self.name = unicode(self.server.GetAlternativeServiceName(self.name))
164
logger.info("Changing Zeroconf service name to %r ...",
133
raise AvahiServiceError(u"Too many renames")
134
self.name = server.GetAlternativeServiceName(self.name)
135
logger.info(u"Changing Zeroconf service name to %r ...",
166
137
syslogger.setFormatter(logging.Formatter
167
('Mandos (%s) [%%(process)d]:'
168
' %%(levelname)s: %%(message)s'
138
('Mandos (%s): %%(levelname)s:'
139
' %%(message)s' % self.name))
173
except dbus.exceptions.DBusException as error:
174
logger.critical("DBusException: %s", error)
177
142
self.rename_count += 1
178
143
def remove(self):
179
144
"""Derived from the Avahi example code"""
180
if self.entry_group_state_changed_match is not None:
181
self.entry_group_state_changed_match.remove()
182
self.entry_group_state_changed_match = None
183
if self.group is not None:
145
if group is not None:
186
148
"""Derived from the Avahi example code"""
188
if self.group is None:
189
self.group = dbus.Interface(
190
self.bus.get_object(avahi.DBUS_NAME,
191
self.server.EntryGroupNew()),
192
avahi.DBUS_INTERFACE_ENTRY_GROUP)
193
self.entry_group_state_changed_match = (
194
self.group.connect_to_signal(
195
'StateChanged', self .entry_group_state_changed))
196
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
197
self.name, self.type)
198
self.group.AddService(
201
dbus.UInt32(0), # flags
202
self.name, self.type,
203
self.domain, self.host,
204
dbus.UInt16(self.port),
205
avahi.string_array_to_txt_array(self.TXT))
207
def entry_group_state_changed(self, state, error):
208
"""Derived from the Avahi example code"""
209
logger.debug("Avahi entry group state change: %i", state)
211
if state == avahi.ENTRY_GROUP_ESTABLISHED:
212
logger.debug("Zeroconf service established.")
213
elif state == avahi.ENTRY_GROUP_COLLISION:
214
logger.info("Zeroconf service name collision.")
216
elif state == avahi.ENTRY_GROUP_FAILURE:
217
logger.critical("Avahi: Error in group state changed %s",
219
raise AvahiGroupError("State changed: %s"
222
"""Derived from the Avahi example code"""
223
if self.group is not None:
226
except (dbus.exceptions.UnknownMethodException,
227
dbus.exceptions.DBusException) as e:
231
def server_state_changed(self, state, error=None):
232
"""Derived from the Avahi example code"""
233
logger.debug("Avahi server state change: %i", state)
234
bad_states = { avahi.SERVER_INVALID:
235
"Zeroconf server invalid",
236
avahi.SERVER_REGISTERING: None,
237
avahi.SERVER_COLLISION:
238
"Zeroconf server name collision",
239
avahi.SERVER_FAILURE:
240
"Zeroconf server failure" }
241
if state in bad_states:
242
if bad_states[state] is not None:
244
logger.error(bad_states[state])
246
logger.error(bad_states[state] + ": %r", error)
248
elif state == avahi.SERVER_RUNNING:
252
logger.debug("Unknown state: %r", state)
254
logger.debug("Unknown state: %r: %r", state, error)
256
"""Derived from the Avahi example code"""
257
if self.server is None:
258
self.server = dbus.Interface(
259
self.bus.get_object(avahi.DBUS_NAME,
260
avahi.DBUS_PATH_SERVER,
261
follow_name_owner_changes=True),
262
avahi.DBUS_INTERFACE_SERVER)
263
self.server.connect_to_signal("StateChanged",
264
self.server_state_changed)
265
self.server_state_changed(self.server.GetState())
268
def _timedelta_to_milliseconds(td):
269
"Convert a datetime.timedelta() to milliseconds"
270
return ((td.days * 24 * 60 * 60 * 1000)
271
+ (td.seconds * 1000)
272
+ (td.microseconds // 1000))
274
class Client(object):
151
group = dbus.Interface(bus.get_object
153
server.EntryGroupNew()),
154
avahi.DBUS_INTERFACE_ENTRY_GROUP)
155
group.connect_to_signal('StateChanged',
156
entry_group_state_changed)
157
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
158
service.name, service.type)
160
self.interface, # interface
161
avahi.PROTO_INET6, # protocol
162
dbus.UInt32(0), # flags
163
self.name, self.type,
164
self.domain, self.host,
165
dbus.UInt16(self.port),
166
avahi.string_array_to_txt_array(self.TXT))
169
# From the Avahi example code:
170
group = None # our entry group
171
# End of Avahi example code
174
def _datetime_to_dbus(dt, variant_level=0):
175
"""Convert a UTC datetime.datetime() to a D-Bus type."""
176
return dbus.String(dt.isoformat(), variant_level=variant_level)
179
class Client(dbus.service.Object):
275
180
"""A representation of a client host served by this server.
278
_approved: bool(); 'None' if not yet approved/disapproved
279
approval_delay: datetime.timedelta(); Time to wait for approval
280
approval_duration: datetime.timedelta(); Duration of one approval
182
name: string; from the config file, used in log messages and
184
fingerprint: string (40 or 32 hexadecimal digits); used to
185
uniquely identify the client
186
secret: bytestring; sent verbatim (over TLS) to client
187
host: string; available for use by the checker command
188
created: datetime.datetime(); (UTC) object creation
189
last_enabled: datetime.datetime(); (UTC)
191
last_checked_ok: datetime.datetime(); (UTC) or None
192
timeout: datetime.timedelta(); How long from last_checked_ok
193
until this client is invalid
194
interval: datetime.timedelta(); How often to start a new checker
195
disable_hook: If set, called by disable() as disable_hook(self)
281
196
checker: subprocess.Popen(); a running checker process used
282
197
to see if the client lives.
283
198
'None' if no process is running.
284
checker_callback_tag: a gobject event source tag, or None
285
checker_command: string; External command which is run to check
286
if client lives. %() expansions are done at
199
checker_initiator_tag: a gobject event source tag, or None
200
disable_initiator_tag: - '' -
201
checker_callback_tag: - '' -
202
checker_command: string; External command which is run to check if
203
client lives. %() expansions are done at
287
204
runtime with vars(self) as dict, so that for
288
205
instance %(name)s can be used in the command.
289
checker_initiator_tag: a gobject event source tag, or None
290
created: datetime.datetime(); (UTC) object creation
291
current_checker_command: string; current running checker_command
292
disable_hook: If set, called by disable() as disable_hook(self)
293
disable_initiator_tag: a gobject event source tag, or None
295
fingerprint: string (40 or 32 hexadecimal digits); used to
296
uniquely identify the client
297
host: string; available for use by the checker command
298
interval: datetime.timedelta(); How often to start a new checker
299
last_approval_request: datetime.datetime(); (UTC) or None
300
last_checked_ok: datetime.datetime(); (UTC) or None
301
last_enabled: datetime.datetime(); (UTC)
302
name: string; from the config file, used in log messages and
304
secret: bytestring; sent verbatim (over TLS) to client
305
timeout: datetime.timedelta(); How long from last_checked_ok
306
until this client is disabled
307
extended_timeout: extra long timeout when password has been sent
308
runtime_expansions: Allowed attributes for runtime expansion.
309
expires: datetime.datetime(); time (UTC) when a client will be
206
use_dbus: bool(); Whether to provide D-Bus interface and signals
207
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
313
runtime_expansions = ("approval_delay", "approval_duration",
314
"created", "enabled", "fingerprint",
315
"host", "interval", "last_checked_ok",
316
"last_enabled", "name", "timeout")
318
209
def timeout_milliseconds(self):
319
210
"Return the 'timeout' attribute in milliseconds"
320
return _timedelta_to_milliseconds(self.timeout)
322
def extended_timeout_milliseconds(self):
323
"Return the 'extended_timeout' attribute in milliseconds"
324
return _timedelta_to_milliseconds(self.extended_timeout)
211
return ((self.timeout.days * 24 * 60 * 60 * 1000)
212
+ (self.timeout.seconds * 1000)
213
+ (self.timeout.microseconds // 1000))
326
215
def interval_milliseconds(self):
327
216
"Return the 'interval' attribute in milliseconds"
328
return _timedelta_to_milliseconds(self.interval)
330
def approval_delay_milliseconds(self):
331
return _timedelta_to_milliseconds(self.approval_delay)
333
def __init__(self, name = None, disable_hook=None, config=None):
217
return ((self.interval.days * 24 * 60 * 60 * 1000)
218
+ (self.interval.seconds * 1000)
219
+ (self.interval.microseconds // 1000))
221
def __init__(self, name = None, disable_hook=None, config=None,
334
223
"""Note: the 'checker' key in 'config' sets the
335
224
'checker_command' attribute and *not* the 'checker'
338
227
if config is None:
340
logger.debug("Creating client %r", self.name)
229
logger.debug(u"Creating client %r", self.name)
230
self.use_dbus = False # During __init__
341
231
# Uppercase and remove spaces from fingerprint for later
342
232
# comparison purposes with return value from the fingerprint()
344
234
self.fingerprint = (config["fingerprint"].upper()
346
logger.debug(" Fingerprint: %s", self.fingerprint)
236
logger.debug(u" Fingerprint: %s", self.fingerprint)
347
237
if "secret" in config:
348
self.secret = config["secret"].decode("base64")
238
self.secret = config["secret"].decode(u"base64")
349
239
elif "secfile" in config:
350
with open(os.path.expanduser(os.path.expandvars
351
(config["secfile"])),
240
with closing(open(os.path.expanduser
242
(config["secfile"])))) as secfile:
353
243
self.secret = secfile.read()
355
raise TypeError("No secret or secfile for client %s"
245
raise TypeError(u"No secret or secfile for client %s"
357
247
self.host = config.get("host", "")
358
248
self.created = datetime.datetime.utcnow()
359
249
self.enabled = False
360
self.last_approval_request = None
361
250
self.last_enabled = None
362
251
self.last_checked_ok = None
363
252
self.timeout = string_to_delta(config["timeout"])
364
self.extended_timeout = string_to_delta(config["extended_timeout"])
365
253
self.interval = string_to_delta(config["interval"])
366
254
self.disable_hook = disable_hook
367
255
self.checker = None
368
256
self.checker_initiator_tag = None
369
257
self.disable_initiator_tag = None
371
258
self.checker_callback_tag = None
372
259
self.checker_command = config["checker"]
373
self.current_checker_command = None
374
260
self.last_connect = None
375
self._approved = None
376
self.approved_by_default = config.get("approved_by_default",
378
self.approvals_pending = 0
379
self.approval_delay = string_to_delta(
380
config["approval_delay"])
381
self.approval_duration = string_to_delta(
382
config["approval_duration"])
383
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
261
# Only now, when this client is initialized, can it show up on
263
self.use_dbus = use_dbus
265
self.dbus_object_path = (dbus.ObjectPath
267
+ self.name.replace(".", "_")))
268
dbus.service.Object.__init__(self, bus,
269
self.dbus_object_path)
385
def send_changedstate(self):
386
self.changedstate.acquire()
387
self.changedstate.notify_all()
388
self.changedstate.release()
390
271
def enable(self):
391
272
"""Start this client's checker and timeout hooks"""
392
if getattr(self, "enabled", False):
395
self.send_changedstate()
273
self.last_enabled = datetime.datetime.utcnow()
396
274
# Schedule a new checker to be started an 'interval' from now,
397
275
# and every interval from then on.
398
276
self.checker_initiator_tag = (gobject.timeout_add
399
277
(self.interval_milliseconds(),
400
278
self.start_checker))
279
# Also start a new checker *right now*.
401
281
# Schedule a disable() when 'timeout' has passed
402
self.expires = datetime.datetime.utcnow() + self.timeout
403
282
self.disable_initiator_tag = (gobject.timeout_add
404
283
(self.timeout_milliseconds(),
406
285
self.enabled = True
407
self.last_enabled = datetime.datetime.utcnow()
408
# Also start a new checker *right now*.
288
self.PropertyChanged(dbus.String(u"enabled"),
289
dbus.Boolean(True, variant_level=1))
290
self.PropertyChanged(dbus.String(u"last_enabled"),
291
(_datetime_to_dbus(self.last_enabled,
411
def disable(self, quiet=True):
412
295
"""Disable this client."""
413
296
if not getattr(self, "enabled", False):
416
self.send_changedstate()
418
logger.info("Disabling client %s", self.name)
298
logger.info(u"Disabling client %s", self.name)
419
299
if getattr(self, "disable_initiator_tag", False):
420
300
gobject.source_remove(self.disable_initiator_tag)
421
301
self.disable_initiator_tag = None
423
302
if getattr(self, "checker_initiator_tag", False):
424
303
gobject.source_remove(self.checker_initiator_tag)
425
304
self.checker_initiator_tag = None
551
431
self.checker_callback_tag = None
552
432
if getattr(self, "checker", None) is None:
554
logger.debug("Stopping checker for %(name)s", vars(self))
434
logger.debug(u"Stopping checker for %(name)s", vars(self))
556
436
os.kill(self.checker.pid, signal.SIGTERM)
558
438
#if self.checker.poll() is None:
559
439
# os.kill(self.checker.pid, signal.SIGKILL)
560
except OSError as error:
440
except OSError, error:
561
441
if error.errno != errno.ESRCH: # No such process
563
443
self.checker = None
566
def dbus_service_property(dbus_interface, signature="v",
567
access="readwrite", byte_arrays=False):
568
"""Decorators for marking methods of a DBusObjectWithProperties to
569
become properties on the D-Bus.
571
The decorated method will be called with no arguments by "Get"
572
and with one argument by "Set".
574
The parameters, where they are supported, are the same as
575
dbus.service.method, except there is only "signature", since the
576
type from Get() and the type sent to Set() is the same.
578
# Encoding deeply encoded byte arrays is not supported yet by the
579
# "Set" method, so we fail early here:
580
if byte_arrays and signature != "ay":
581
raise ValueError("Byte arrays not supported for non-'ay'"
582
" signature %r" % signature)
584
func._dbus_is_property = True
585
func._dbus_interface = dbus_interface
586
func._dbus_signature = signature
587
func._dbus_access = access
588
func._dbus_name = func.__name__
589
if func._dbus_name.endswith("_dbus_property"):
590
func._dbus_name = func._dbus_name[:-14]
591
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
596
class DBusPropertyException(dbus.exceptions.DBusException):
597
"""A base class for D-Bus property-related exceptions
599
def __unicode__(self):
600
return unicode(str(self))
603
class DBusPropertyAccessException(DBusPropertyException):
604
"""A property's access permissions disallows an operation.
609
class DBusPropertyNotFound(DBusPropertyException):
610
"""An attempt was made to access a non-existing property.
615
class DBusObjectWithProperties(dbus.service.Object):
616
"""A D-Bus object with properties.
618
Classes inheriting from this can use the dbus_service_property
619
decorator to expose methods as D-Bus properties. It exposes the
620
standard Get(), Set(), and GetAll() methods on the D-Bus.
624
def _is_dbus_property(obj):
625
return getattr(obj, "_dbus_is_property", False)
627
def _get_all_dbus_properties(self):
628
"""Returns a generator of (name, attribute) pairs
630
return ((prop.__get__(self)._dbus_name, prop.__get__(self))
631
for cls in self.__class__.__mro__
632
for name, prop in inspect.getmembers(cls, self._is_dbus_property))
634
def _get_dbus_property(self, interface_name, property_name):
635
"""Returns a bound method if one exists which is a D-Bus
636
property with the specified name and interface.
638
for cls in self.__class__.__mro__:
639
for name, value in inspect.getmembers(cls, self._is_dbus_property):
640
if value._dbus_name == property_name and value._dbus_interface == interface_name:
641
return value.__get__(self)
644
raise DBusPropertyNotFound(self.dbus_object_path + ":"
645
+ interface_name + "."
649
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
651
def Get(self, interface_name, property_name):
652
"""Standard D-Bus property Get() method, see D-Bus standard.
654
prop = self._get_dbus_property(interface_name, property_name)
655
if prop._dbus_access == "write":
656
raise DBusPropertyAccessException(property_name)
658
if not hasattr(value, "variant_level"):
660
return type(value)(value, variant_level=value.variant_level+1)
662
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
663
def Set(self, interface_name, property_name, value):
664
"""Standard D-Bus property Set() method, see D-Bus standard.
666
prop = self._get_dbus_property(interface_name, property_name)
667
if prop._dbus_access == "read":
668
raise DBusPropertyAccessException(property_name)
669
if prop._dbus_get_args_options["byte_arrays"]:
670
# The byte_arrays option is not supported yet on
671
# signatures other than "ay".
672
if prop._dbus_signature != "ay":
674
value = dbus.ByteArray(''.join(unichr(byte)
678
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
679
out_signature="a{sv}")
680
def GetAll(self, interface_name):
681
"""Standard D-Bus property GetAll() method, see D-Bus
684
Note: Will not include properties with access="write".
687
for name, prop in self._get_all_dbus_properties():
689
and interface_name != prop._dbus_interface):
690
# Interface non-empty but did not match
692
# Ignore write-only properties
693
if prop._dbus_access == "write":
696
if not hasattr(value, "variant_level"):
699
all[name] = type(value)(value, variant_level=
700
value.variant_level+1)
701
return dbus.Dictionary(all, signature="sv")
703
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
705
path_keyword='object_path',
706
connection_keyword='connection')
707
def Introspect(self, object_path, connection):
708
"""Standard D-Bus method, overloaded to insert property tags.
710
xmlstring = dbus.service.Object.Introspect(self, object_path,
713
document = xml.dom.minidom.parseString(xmlstring)
714
def make_tag(document, name, prop):
715
e = document.createElement("property")
716
e.setAttribute("name", name)
717
e.setAttribute("type", prop._dbus_signature)
718
e.setAttribute("access", prop._dbus_access)
720
for if_tag in document.getElementsByTagName("interface"):
721
for tag in (make_tag(document, name, prop)
723
in self._get_all_dbus_properties()
724
if prop._dbus_interface
725
== if_tag.getAttribute("name")):
726
if_tag.appendChild(tag)
727
# Add the names to the return values for the
728
# "org.freedesktop.DBus.Properties" methods
729
if (if_tag.getAttribute("name")
730
== "org.freedesktop.DBus.Properties"):
731
for cn in if_tag.getElementsByTagName("method"):
732
if cn.getAttribute("name") == "Get":
733
for arg in cn.getElementsByTagName("arg"):
734
if (arg.getAttribute("direction")
736
arg.setAttribute("name", "value")
737
elif cn.getAttribute("name") == "GetAll":
738
for arg in cn.getElementsByTagName("arg"):
739
if (arg.getAttribute("direction")
741
arg.setAttribute("name", "props")
742
xmlstring = document.toxml("utf-8")
744
except (AttributeError, xml.dom.DOMException,
745
xml.parsers.expat.ExpatError) as error:
746
logger.error("Failed to override Introspection method",
751
def datetime_to_dbus (dt, variant_level=0):
752
"""Convert a UTC datetime.datetime() to a D-Bus type."""
754
return dbus.String("", variant_level = variant_level)
755
return dbus.String(dt.isoformat(),
756
variant_level=variant_level)
758
class transitional_dbus_metaclass(DBusObjectWithProperties.__metaclass__):
759
def __new__(mcs, name, bases, attr):
760
for attrname, old_dbusobj in inspect.getmembers(bases[0]):
761
new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.")
762
if (getattr(old_dbusobj, "_dbus_is_signal", False)
763
and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
764
unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars,
765
old_dbusobj.__closure__))["func"].cell_contents
766
newfunc = types.FunctionType(unwrappedfunc.func_code,
767
unwrappedfunc.func_globals,
768
unwrappedfunc.func_name,
769
unwrappedfunc.func_defaults,
770
unwrappedfunc.func_closure)
771
new_dbusfunc = dbus.service.signal(
772
new_interface, old_dbusobj._dbus_signature)(newfunc)
773
attr["_transitional_" + attrname] = new_dbusfunc
775
def fixscope(func1, func2):
776
def newcall(*args, **kwargs):
777
func1(*args, **kwargs)
778
func2(*args, **kwargs)
781
attr[attrname] = fixscope(old_dbusobj, new_dbusfunc)
783
elif (getattr(old_dbusobj, "_dbus_is_method", False)
784
and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
785
new_dbusfunc = (dbus.service.method
787
old_dbusobj._dbus_in_signature,
788
old_dbusobj._dbus_out_signature)
790
(old_dbusobj.func_code,
791
old_dbusobj.func_globals,
792
old_dbusobj.func_name,
793
old_dbusobj.func_defaults,
794
old_dbusobj.func_closure)))
796
attr[attrname] = new_dbusfunc
797
elif (getattr(old_dbusobj, "_dbus_is_property", False)
798
and old_dbusobj._dbus_interface.startswith("se.bsnet.fukt.Mandos")):
799
new_dbusfunc = (dbus_service_property
801
old_dbusobj._dbus_signature,
802
old_dbusobj._dbus_access,
803
old_dbusobj._dbus_get_args_options["byte_arrays"])
805
(old_dbusobj.func_code,
806
old_dbusobj.func_globals,
807
old_dbusobj.func_name,
808
old_dbusobj.func_defaults,
809
old_dbusobj.func_closure)))
811
attr[attrname] = new_dbusfunc
812
return type.__new__(mcs, name, bases, attr)
814
class ClientDBus(Client, DBusObjectWithProperties):
815
"""A Client class using D-Bus
818
dbus_object_path: dbus.ObjectPath
819
bus: dbus.SystemBus()
822
runtime_expansions = (Client.runtime_expansions
823
+ ("dbus_object_path",))
825
# dbus.service.Object doesn't use super(), so we can't either.
827
def __init__(self, bus = None, *args, **kwargs):
828
self._approvals_pending = 0
830
Client.__init__(self, *args, **kwargs)
831
# Only now, when this client is initialized, can it show up on
833
client_object_name = unicode(self.name).translate(
836
self.dbus_object_path = (dbus.ObjectPath
837
("/clients/" + client_object_name))
838
DBusObjectWithProperties.__init__(self, self.bus,
839
self.dbus_object_path)
841
def notifychangeproperty(transform_func,
842
dbus_name, type_func=lambda x: x,
844
""" Modify a variable so that its a property that announce its
846
transform_fun: Function that takes a value and transform it to
848
dbus_name: DBus name of the variable
849
type_func: Function that transform the value before sending it
851
variant_level: DBus variant level. default: 1
854
def setter(self, value):
855
old_value = real_value[0]
856
real_value[0] = value
857
if hasattr(self, "dbus_object_path"):
858
if type_func(old_value) != type_func(real_value[0]):
859
dbus_value = transform_func(type_func(real_value[0]),
861
self.PropertyChanged(dbus.String(dbus_name),
864
return property(lambda self: real_value[0], setter)
867
expires = notifychangeproperty(datetime_to_dbus, "Expires")
868
approvals_pending = notifychangeproperty(dbus.Boolean,
871
enabled = notifychangeproperty(dbus.Boolean, "Enabled")
872
last_enabled = notifychangeproperty(datetime_to_dbus,
874
checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
875
type_func = lambda checker: checker is not None)
876
last_checked_ok = notifychangeproperty(datetime_to_dbus,
878
last_approval_request = notifychangeproperty(datetime_to_dbus,
879
"LastApprovalRequest")
880
approved_by_default = notifychangeproperty(dbus.Boolean,
882
approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
883
type_func = _timedelta_to_milliseconds)
884
approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
885
type_func = _timedelta_to_milliseconds)
886
host = notifychangeproperty(dbus.String, "Host")
887
timeout = notifychangeproperty(dbus.UInt16, "Timeout",
888
type_func = _timedelta_to_milliseconds)
889
extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
890
type_func = _timedelta_to_milliseconds)
891
interval = notifychangeproperty(dbus.UInt16, "Interval",
892
type_func = _timedelta_to_milliseconds)
893
checker_command = notifychangeproperty(dbus.String, "Checker")
895
del notifychangeproperty
897
def __del__(self, *args, **kwargs):
899
self.remove_from_connection()
902
if hasattr(DBusObjectWithProperties, "__del__"):
903
DBusObjectWithProperties.__del__(self, *args, **kwargs)
904
Client.__del__(self, *args, **kwargs)
906
def checker_callback(self, pid, condition, command,
908
self.checker_callback_tag = None
910
if os.WIFEXITED(condition):
911
exitstatus = os.WEXITSTATUS(condition)
913
self.CheckerCompleted(dbus.Int16(exitstatus),
914
dbus.Int64(condition),
915
dbus.String(command))
918
self.CheckerCompleted(dbus.Int16(-1),
919
dbus.Int64(condition),
920
dbus.String(command))
922
return Client.checker_callback(self, pid, condition, command,
925
def start_checker(self, *args, **kwargs):
926
old_checker = self.checker
927
if self.checker is not None:
928
old_checker_pid = self.checker.pid
930
old_checker_pid = None
931
r = Client.start_checker(self, *args, **kwargs)
932
# Only if new checker process was started
933
if (self.checker is not None
934
and old_checker_pid != self.checker.pid):
936
self.CheckerStarted(self.current_checker_command)
939
def _reset_approved(self):
940
self._approved = None
943
def approve(self, value=True):
944
self.send_changedstate()
945
self._approved = value
946
gobject.timeout_add(_timedelta_to_milliseconds
947
(self.approval_duration),
948
self._reset_approved)
951
## D-Bus methods, signals & properties
952
_interface = "se.bsnet.fukt.Mandos.Client"
445
self.PropertyChanged(dbus.String(u"checker_running"),
446
dbus.Boolean(False, variant_level=1))
448
def still_valid(self):
449
"""Has the timeout not yet passed for this client?"""
450
if not getattr(self, "enabled", False):
452
now = datetime.datetime.utcnow()
453
if self.last_checked_ok is None:
454
return now < (self.created + self.timeout)
456
return now < (self.last_checked_ok + self.timeout)
458
## D-Bus methods & signals
459
_interface = u"se.bsnet.fukt.Mandos.Client"
462
CheckedOK = dbus.service.method(_interface)(checked_ok)
463
CheckedOK.__name__ = "CheckedOK"
956
465
# CheckerCompleted - signal
957
466
@dbus.service.signal(_interface, signature="nxs")
1025
590
# StopChecker - method
1026
@dbus.service.method(_interface)
1027
def StopChecker(self):
1032
# ApprovalPending - property
1033
@dbus_service_property(_interface, signature="b", access="read")
1034
def ApprovalPending_dbus_property(self):
1035
return dbus.Boolean(bool(self.approvals_pending))
1037
# ApprovedByDefault - property
1038
@dbus_service_property(_interface, signature="b",
1040
def ApprovedByDefault_dbus_property(self, value=None):
1041
if value is None: # get
1042
return dbus.Boolean(self.approved_by_default)
1043
self.approved_by_default = bool(value)
1045
# ApprovalDelay - property
1046
@dbus_service_property(_interface, signature="t",
1048
def ApprovalDelay_dbus_property(self, value=None):
1049
if value is None: # get
1050
return dbus.UInt64(self.approval_delay_milliseconds())
1051
self.approval_delay = datetime.timedelta(0, 0, 0, value)
1053
# ApprovalDuration - property
1054
@dbus_service_property(_interface, signature="t",
1056
def ApprovalDuration_dbus_property(self, value=None):
1057
if value is None: # get
1058
return dbus.UInt64(_timedelta_to_milliseconds(
1059
self.approval_duration))
1060
self.approval_duration = datetime.timedelta(0, 0, 0, value)
1063
@dbus_service_property(_interface, signature="s", access="read")
1064
def Name_dbus_property(self):
1065
return dbus.String(self.name)
1067
# Fingerprint - property
1068
@dbus_service_property(_interface, signature="s", access="read")
1069
def Fingerprint_dbus_property(self):
1070
return dbus.String(self.fingerprint)
1073
@dbus_service_property(_interface, signature="s",
1075
def Host_dbus_property(self, value=None):
1076
if value is None: # get
1077
return dbus.String(self.host)
1080
# Created - property
1081
@dbus_service_property(_interface, signature="s", access="read")
1082
def Created_dbus_property(self):
1083
return dbus.String(datetime_to_dbus(self.created))
1085
# LastEnabled - property
1086
@dbus_service_property(_interface, signature="s", access="read")
1087
def LastEnabled_dbus_property(self):
1088
return datetime_to_dbus(self.last_enabled)
1090
# Enabled - property
1091
@dbus_service_property(_interface, signature="b",
1093
def Enabled_dbus_property(self, value=None):
1094
if value is None: # get
1095
return dbus.Boolean(self.enabled)
1101
# LastCheckedOK - property
1102
@dbus_service_property(_interface, signature="s",
1104
def LastCheckedOK_dbus_property(self, value=None):
1105
if value is not None:
1108
return datetime_to_dbus(self.last_checked_ok)
1110
# Expires - property
1111
@dbus_service_property(_interface, signature="s", access="read")
1112
def Expires_dbus_property(self):
1113
return datetime_to_dbus(self.expires)
1115
# LastApprovalRequest - property
1116
@dbus_service_property(_interface, signature="s", access="read")
1117
def LastApprovalRequest_dbus_property(self):
1118
return datetime_to_dbus(self.last_approval_request)
1120
# Timeout - property
1121
@dbus_service_property(_interface, signature="t",
1123
def Timeout_dbus_property(self, value=None):
1124
if value is None: # get
1125
return dbus.UInt64(self.timeout_milliseconds())
1126
self.timeout = datetime.timedelta(0, 0, 0, value)
1127
if getattr(self, "disable_initiator_tag", None) is None:
1129
# Reschedule timeout
1130
gobject.source_remove(self.disable_initiator_tag)
1131
self.disable_initiator_tag = None
1133
time_to_die = (self.
1134
_timedelta_to_milliseconds((self
1139
if time_to_die <= 0:
1140
# The timeout has passed
1143
self.expires = (datetime.datetime.utcnow()
1144
+ datetime.timedelta(milliseconds = time_to_die))
1145
self.disable_initiator_tag = (gobject.timeout_add
1146
(time_to_die, self.disable))
1148
# ExtendedTimeout - property
1149
@dbus_service_property(_interface, signature="t",
1151
def ExtendedTimeout_dbus_property(self, value=None):
1152
if value is None: # get
1153
return dbus.UInt64(self.extended_timeout_milliseconds())
1154
self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1156
# Interval - property
1157
@dbus_service_property(_interface, signature="t",
1159
def Interval_dbus_property(self, value=None):
1160
if value is None: # get
1161
return dbus.UInt64(self.interval_milliseconds())
1162
self.interval = datetime.timedelta(0, 0, 0, value)
1163
if getattr(self, "checker_initiator_tag", None) is None:
1165
# Reschedule checker run
1166
gobject.source_remove(self.checker_initiator_tag)
1167
self.checker_initiator_tag = (gobject.timeout_add
1168
(value, self.start_checker))
1169
self.start_checker() # Start one now, too
1171
# Checker - property
1172
@dbus_service_property(_interface, signature="s",
1174
def Checker_dbus_property(self, value=None):
1175
if value is None: # get
1176
return dbus.String(self.checker_command)
1177
self.checker_command = value
1179
# CheckerRunning - property
1180
@dbus_service_property(_interface, signature="b",
1182
def CheckerRunning_dbus_property(self, value=None):
1183
if value is None: # get
1184
return dbus.Boolean(self.checker is not None)
1186
self.start_checker()
1190
# ObjectPath - property
1191
@dbus_service_property(_interface, signature="o", access="read")
1192
def ObjectPath_dbus_property(self):
1193
return self.dbus_object_path # is already a dbus.ObjectPath
1196
@dbus_service_property(_interface, signature="ay",
1197
access="write", byte_arrays=True)
1198
def Secret_dbus_property(self, value):
1199
self.secret = str(value)
591
StopChecker = dbus.service.method(_interface)(stop_checker)
592
StopChecker.__name__ = "StopChecker"
1204
class ProxyClient(object):
1205
def __init__(self, child_pipe, fpr, address):
1206
self._pipe = child_pipe
1207
self._pipe.send(('init', fpr, address))
1208
if not self._pipe.recv():
1211
def __getattribute__(self, name):
1212
if(name == '_pipe'):
1213
return super(ProxyClient, self).__getattribute__(name)
1214
self._pipe.send(('getattr', name))
1215
data = self._pipe.recv()
1216
if data[0] == 'data':
1218
if data[0] == 'function':
1219
def func(*args, **kwargs):
1220
self._pipe.send(('funcall', name, args, kwargs))
1221
return self._pipe.recv()[1]
1224
def __setattr__(self, name, value):
1225
if(name == '_pipe'):
1226
return super(ProxyClient, self).__setattr__(name, value)
1227
self._pipe.send(('setattr', name, value))
1229
class ClientDBusTransitional(ClientDBus):
1230
__metaclass__ = transitional_dbus_metaclass
1232
class ClientHandler(socketserver.BaseRequestHandler, object):
1233
"""A class to handle client connections.
1235
Instantiated once for each connection to handle it.
597
def peer_certificate(session):
598
"Return the peer's OpenPGP certificate as a bytestring"
599
# If not an OpenPGP certificate...
600
if (gnutls.library.functions
601
.gnutls_certificate_type_get(session._c_object)
602
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
603
# ...do the normal thing
604
return session.peer_certificate
605
list_size = ctypes.c_uint(1)
606
cert_list = (gnutls.library.functions
607
.gnutls_certificate_get_peers
608
(session._c_object, ctypes.byref(list_size)))
609
if not bool(cert_list) and list_size.value != 0:
610
raise gnutls.errors.GNUTLSError("error getting peer"
612
if list_size.value == 0:
615
return ctypes.string_at(cert.data, cert.size)
618
def fingerprint(openpgp):
619
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
620
# New GnuTLS "datum" with the OpenPGP public key
621
datum = (gnutls.library.types
622
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
625
ctypes.c_uint(len(openpgp))))
626
# New empty GnuTLS certificate
627
crt = gnutls.library.types.gnutls_openpgp_crt_t()
628
(gnutls.library.functions
629
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
630
# Import the OpenPGP public key into the certificate
631
(gnutls.library.functions
632
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
633
gnutls.library.constants
634
.GNUTLS_OPENPGP_FMT_RAW))
635
# Verify the self signature in the key
636
crtverify = ctypes.c_uint()
637
(gnutls.library.functions
638
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
639
if crtverify.value != 0:
640
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
641
raise gnutls.errors.CertificateSecurityError("Verify failed")
642
# New buffer for the fingerprint
643
buf = ctypes.create_string_buffer(20)
644
buf_len = ctypes.c_size_t()
645
# Get the fingerprint from the certificate into the buffer
646
(gnutls.library.functions
647
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
648
ctypes.byref(buf_len)))
649
# Deinit the certificate
650
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
651
# Convert the buffer to a Python bytestring
652
fpr = ctypes.string_at(buf, buf_len.value)
653
# Convert the bytestring to hexadecimal notation
654
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
658
class TCP_handler(SocketServer.BaseRequestHandler, object):
659
"""A TCP request handler class.
660
Instantiated by IPv6_TCPServer for each request to handle it.
1236
661
Note: This will run in its own forked process."""
1238
663
def handle(self):
1239
with contextlib.closing(self.server.child_pipe) as child_pipe:
1240
logger.info("TCP connection from: %s",
1241
unicode(self.client_address))
1242
logger.debug("Pipe FD: %d",
1243
self.server.child_pipe.fileno())
1245
session = (gnutls.connection
1246
.ClientSession(self.request,
1248
.X509Credentials()))
1250
# Note: gnutls.connection.X509Credentials is really a
1251
# generic GnuTLS certificate credentials object so long as
1252
# no X.509 keys are added to it. Therefore, we can use it
1253
# here despite using OpenPGP certificates.
1255
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1256
# "+AES-256-CBC", "+SHA1",
1257
# "+COMP-NULL", "+CTYPE-OPENPGP",
1259
# Use a fallback default, since this MUST be set.
1260
priority = self.server.gnutls_priority
1261
if priority is None:
1263
(gnutls.library.functions
1264
.gnutls_priority_set_direct(session._c_object,
1267
# Start communication using the Mandos protocol
1268
# Get protocol number
1269
line = self.request.makefile().readline()
1270
logger.debug("Protocol version: %r", line)
1272
if int(line.strip().split()[0]) > 1:
1274
except (ValueError, IndexError, RuntimeError) as error:
1275
logger.error("Unknown protocol version: %s", error)
1278
# Start GnuTLS connection
1281
except gnutls.errors.GNUTLSError as error:
1282
logger.warning("Handshake failed: %s", error)
1283
# Do not run session.bye() here: the session is not
1284
# established. Just abandon the request.
1286
logger.debug("Handshake succeeded")
1288
approval_required = False
1291
fpr = self.fingerprint(self.peer_certificate
1294
gnutls.errors.GNUTLSError) as error:
1295
logger.warning("Bad certificate: %s", error)
1297
logger.debug("Fingerprint: %s", fpr)
1300
client = ProxyClient(child_pipe, fpr,
1301
self.client_address)
1305
if client.approval_delay:
1306
delay = client.approval_delay
1307
client.approvals_pending += 1
1308
approval_required = True
1311
if not client.enabled:
1312
logger.info("Client %s is disabled",
1314
if self.server.use_dbus:
1316
client.Rejected("Disabled")
1319
if client._approved or not client.approval_delay:
1320
#We are approved or approval is disabled
1322
elif client._approved is None:
1323
logger.info("Client %s needs approval",
1325
if self.server.use_dbus:
1327
client.NeedApproval(
1328
client.approval_delay_milliseconds(),
1329
client.approved_by_default)
1331
logger.warning("Client %s was not approved",
1333
if self.server.use_dbus:
1335
client.Rejected("Denied")
1338
#wait until timeout or approved
1339
#x = float(client._timedelta_to_milliseconds(delay))
1340
time = datetime.datetime.now()
1341
client.changedstate.acquire()
1342
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1343
client.changedstate.release()
1344
time2 = datetime.datetime.now()
1345
if (time2 - time) >= delay:
1346
if not client.approved_by_default:
1347
logger.warning("Client %s timed out while"
1348
" waiting for approval",
1350
if self.server.use_dbus:
1352
client.Rejected("Approval timed out")
1357
delay -= time2 - time
1360
while sent_size < len(client.secret):
1362
sent = session.send(client.secret[sent_size:])
1363
except gnutls.errors.GNUTLSError as error:
1364
logger.warning("gnutls send failed")
1366
logger.debug("Sent: %d, remaining: %d",
1367
sent, len(client.secret)
1368
- (sent_size + sent))
1371
logger.info("Sending secret to %s", client.name)
1372
# bump the timeout as if seen
1373
client.checked_ok(client.extended_timeout)
1374
if self.server.use_dbus:
1379
if approval_required:
1380
client.approvals_pending -= 1
1383
except gnutls.errors.GNUTLSError as error:
1384
logger.warning("GnuTLS bye failed")
1387
def peer_certificate(session):
1388
"Return the peer's OpenPGP certificate as a bytestring"
1389
# If not an OpenPGP certificate...
1390
if (gnutls.library.functions
1391
.gnutls_certificate_type_get(session._c_object)
1392
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1393
# ...do the normal thing
1394
return session.peer_certificate
1395
list_size = ctypes.c_uint(1)
1396
cert_list = (gnutls.library.functions
1397
.gnutls_certificate_get_peers
1398
(session._c_object, ctypes.byref(list_size)))
1399
if not bool(cert_list) and list_size.value != 0:
1400
raise gnutls.errors.GNUTLSError("error getting peer"
1402
if list_size.value == 0:
1405
return ctypes.string_at(cert.data, cert.size)
1408
def fingerprint(openpgp):
1409
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
1410
# New GnuTLS "datum" with the OpenPGP public key
1411
datum = (gnutls.library.types
1412
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
1415
ctypes.c_uint(len(openpgp))))
1416
# New empty GnuTLS certificate
1417
crt = gnutls.library.types.gnutls_openpgp_crt_t()
1418
(gnutls.library.functions
1419
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
1420
# Import the OpenPGP public key into the certificate
1421
(gnutls.library.functions
1422
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
1423
gnutls.library.constants
1424
.GNUTLS_OPENPGP_FMT_RAW))
1425
# Verify the self signature in the key
1426
crtverify = ctypes.c_uint()
1427
(gnutls.library.functions
1428
.gnutls_openpgp_crt_verify_self(crt, 0,
1429
ctypes.byref(crtverify)))
1430
if crtverify.value != 0:
1431
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1432
raise (gnutls.errors.CertificateSecurityError
1434
# New buffer for the fingerprint
1435
buf = ctypes.create_string_buffer(20)
1436
buf_len = ctypes.c_size_t()
1437
# Get the fingerprint from the certificate into the buffer
1438
(gnutls.library.functions
1439
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
1440
ctypes.byref(buf_len)))
1441
# Deinit the certificate
1442
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1443
# Convert the buffer to a Python bytestring
1444
fpr = ctypes.string_at(buf, buf_len.value)
1445
# Convert the bytestring to hexadecimal notation
1446
hex_fpr = ''.join("%02X" % ord(char) for char in fpr)
1450
class MultiprocessingMixIn(object):
1451
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1452
def sub_process_main(self, request, address):
1454
self.finish_request(request, address)
1456
self.handle_error(request, address)
1457
self.close_request(request)
1459
def process_request(self, request, address):
1460
"""Start a new process to process the request."""
1461
multiprocessing.Process(target = self.sub_process_main,
1462
args = (request, address)).start()
1465
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1466
""" adds a pipe to the MixIn """
1467
def process_request(self, request, client_address):
1468
"""Overrides and wraps the original process_request().
1470
This function creates a new pipe in self.pipe
1472
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1474
super(MultiprocessingMixInWithPipe,
1475
self).process_request(request, client_address)
1476
self.child_pipe.close()
1477
self.add_pipe(parent_pipe)
1479
def add_pipe(self, parent_pipe):
1480
"""Dummy function; override as necessary"""
1481
raise NotImplementedError
1484
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1485
socketserver.TCPServer, object):
1486
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
664
logger.info(u"TCP connection from: %s",
665
unicode(self.client_address))
666
session = (gnutls.connection
667
.ClientSession(self.request,
671
line = self.request.makefile().readline()
672
logger.debug(u"Protocol version: %r", line)
674
if int(line.strip().split()[0]) > 1:
676
except (ValueError, IndexError, RuntimeError), error:
677
logger.error(u"Unknown protocol version: %s", error)
680
# Note: gnutls.connection.X509Credentials is really a generic
681
# GnuTLS certificate credentials object so long as no X.509
682
# keys are added to it. Therefore, we can use it here despite
683
# using OpenPGP certificates.
685
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
686
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
688
# Use a fallback default, since this MUST be set.
689
priority = self.server.settings.get("priority", "NORMAL")
690
(gnutls.library.functions
691
.gnutls_priority_set_direct(session._c_object,
696
except gnutls.errors.GNUTLSError, error:
697
logger.warning(u"Handshake failed: %s", error)
698
# Do not run session.bye() here: the session is not
699
# established. Just abandon the request.
701
logger.debug(u"Handshake succeeded")
703
fpr = fingerprint(peer_certificate(session))
704
except (TypeError, gnutls.errors.GNUTLSError), error:
705
logger.warning(u"Bad certificate: %s", error)
708
logger.debug(u"Fingerprint: %s", fpr)
710
for c in self.server.clients:
711
if c.fingerprint == fpr:
715
logger.warning(u"Client not found for fingerprint: %s",
719
# Have to check if client.still_valid(), since it is possible
720
# that the client timed out while establishing the GnuTLS
722
if not client.still_valid():
723
logger.warning(u"Client %(name)s is invalid",
727
## This won't work here, since we're in a fork.
728
# client.checked_ok()
730
while sent_size < len(client.secret):
731
sent = session.send(client.secret[sent_size:])
732
logger.debug(u"Sent: %d, remaining: %d",
733
sent, len(client.secret)
734
- (sent_size + sent))
739
class IPv6_TCPServer(SocketServer.ForkingMixIn,
740
SocketServer.TCPServer, object):
741
"""IPv6 TCP server. Accepts 'None' as address and/or port.
743
settings: Server settings
744
clients: Set() of Client objects
1489
745
enabled: Boolean; whether this server is activated yet
1490
interface: None or a network interface name (string)
1491
use_ipv6: Boolean; to use IPv6 or not
1493
def __init__(self, server_address, RequestHandlerClass,
1494
interface=None, use_ipv6=True):
1495
self.interface = interface
1497
self.address_family = socket.AF_INET6
1498
socketserver.TCPServer.__init__(self, server_address,
1499
RequestHandlerClass)
747
address_family = socket.AF_INET6
748
def __init__(self, *args, **kwargs):
749
if "settings" in kwargs:
750
self.settings = kwargs["settings"]
751
del kwargs["settings"]
752
if "clients" in kwargs:
753
self.clients = kwargs["clients"]
754
del kwargs["clients"]
756
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
1500
757
def server_bind(self):
1501
758
"""This overrides the normal server_bind() function
1502
759
to bind to an interface if one was specified, and also NOT to
1503
760
bind to an address or port if they were not specified."""
1504
if self.interface is not None:
1505
if SO_BINDTODEVICE is None:
1506
logger.error("SO_BINDTODEVICE does not exist;"
1507
" cannot bind to interface %s",
1511
self.socket.setsockopt(socket.SOL_SOCKET,
1515
except socket.error as error:
1516
if error[0] == errno.EPERM:
1517
logger.error("No permission to"
1518
" bind to interface %s",
1520
elif error[0] == errno.ENOPROTOOPT:
1521
logger.error("SO_BINDTODEVICE not available;"
1522
" cannot bind to interface %s",
761
if self.settings["interface"]:
762
# 25 is from /usr/include/asm-i486/socket.h
763
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
765
self.socket.setsockopt(socket.SOL_SOCKET,
767
self.settings["interface"])
768
except socket.error, error:
769
if error[0] == errno.EPERM:
770
logger.error(u"No permission to"
771
u" bind to interface %s",
772
self.settings["interface"])
1526
775
# Only bind(2) the socket if we really need to.
1527
776
if self.server_address[0] or self.server_address[1]:
1528
777
if not self.server_address[0]:
1529
if self.address_family == socket.AF_INET6:
1530
any_address = "::" # in6addr_any
1532
any_address = socket.INADDR_ANY
1533
self.server_address = (any_address,
779
self.server_address = (in6addr_any,
1534
780
self.server_address[1])
1535
781
elif not self.server_address[1]:
1536
782
self.server_address = (self.server_address[0],
1538
# if self.interface:
784
# if self.settings["interface"]:
1539
785
# self.server_address = (self.server_address[0],
1542
788
# if_nametoindex
1544
return socketserver.TCPServer.server_bind(self)
1547
class MandosServer(IPv6_TCPServer):
1551
clients: set of Client objects
1552
gnutls_priority GnuTLS priority string
1553
use_dbus: Boolean; to emit D-Bus signals or not
1555
Assumes a gobject.MainLoop event loop.
1557
def __init__(self, server_address, RequestHandlerClass,
1558
interface=None, use_ipv6=True, clients=None,
1559
gnutls_priority=None, use_dbus=True):
1560
self.enabled = False
1561
self.clients = clients
1562
if self.clients is None:
1563
self.clients = set()
1564
self.use_dbus = use_dbus
1565
self.gnutls_priority = gnutls_priority
1566
IPv6_TCPServer.__init__(self, server_address,
1567
RequestHandlerClass,
1568
interface = interface,
1569
use_ipv6 = use_ipv6)
791
return super(IPv6_TCPServer, self).server_bind()
1570
792
def server_activate(self):
1571
793
if self.enabled:
1572
return socketserver.TCPServer.server_activate(self)
794
return super(IPv6_TCPServer, self).server_activate()
1573
795
def enable(self):
1574
796
self.enabled = True
1575
def add_pipe(self, parent_pipe):
1576
# Call "handle_ipc" for both data and EOF events
1577
gobject.io_add_watch(parent_pipe.fileno(),
1578
gobject.IO_IN | gobject.IO_HUP,
1579
functools.partial(self.handle_ipc,
1580
parent_pipe = parent_pipe))
1582
def handle_ipc(self, source, condition, parent_pipe=None,
1583
client_object=None):
1585
gobject.IO_IN: "IN", # There is data to read.
1586
gobject.IO_OUT: "OUT", # Data can be written (without
1588
gobject.IO_PRI: "PRI", # There is urgent data to read.
1589
gobject.IO_ERR: "ERR", # Error condition.
1590
gobject.IO_HUP: "HUP" # Hung up (the connection has been
1591
# broken, usually for pipes and
1594
conditions_string = ' | '.join(name
1596
condition_names.iteritems()
1597
if cond & condition)
1598
# error or the other end of multiprocessing.Pipe has closed
1599
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1602
# Read a request from the child
1603
request = parent_pipe.recv()
1604
command = request[0]
1606
if command == 'init':
1608
address = request[2]
1610
for c in self.clients:
1611
if c.fingerprint == fpr:
1615
logger.info("Client not found for fingerprint: %s, ad"
1616
"dress: %s", fpr, address)
1619
mandos_dbus_service.ClientNotFound(fpr, address[0])
1620
parent_pipe.send(False)
1623
gobject.io_add_watch(parent_pipe.fileno(),
1624
gobject.IO_IN | gobject.IO_HUP,
1625
functools.partial(self.handle_ipc,
1626
parent_pipe = parent_pipe,
1627
client_object = client))
1628
parent_pipe.send(True)
1629
# remove the old hook in favor of the new above hook on same fileno
1631
if command == 'funcall':
1632
funcname = request[1]
1636
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1638
if command == 'getattr':
1639
attrname = request[1]
1640
if callable(client_object.__getattribute__(attrname)):
1641
parent_pipe.send(('function',))
1643
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1645
if command == 'setattr':
1646
attrname = request[1]
1648
setattr(client_object, attrname, value)
1653
799
def string_to_delta(interval):