107
129
max_renames: integer; maximum number of renames
108
130
rename_count: integer; counter so we only rename after collisions
109
131
a sensible number of times
132
group: D-Bus Entry Group
134
bus: dbus.SystemBus()
111
136
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
112
servicetype = None, port = None, TXT = None, domain = "",
113
host = "", max_renames = 32768):
137
servicetype = None, port = None, TXT = None,
138
domain = u"", host = u"", max_renames = 32768,
139
protocol = avahi.PROTO_UNSPEC, bus = None):
114
140
self.interface = interface
116
142
self.type = servicetype
144
self.TXT = TXT if TXT is not None else []
122
145
self.domain = domain
124
147
self.rename_count = 0
125
148
self.max_renames = max_renames
149
self.protocol = protocol
150
self.group = None # our entry group
126
153
def rename(self):
127
154
"""Derived from the Avahi example code"""
128
155
if self.rename_count >= self.max_renames:
129
156
logger.critical(u"No suitable Zeroconf service name found"
130
157
u" after %i retries, exiting.",
131
158
self.rename_count)
132
raise AvahiServiceError("Too many renames")
133
self.name = server.GetAlternativeServiceName(self.name)
159
raise AvahiServiceError(u"Too many renames")
160
self.name = unicode(self.server.GetAlternativeServiceName(self.name))
134
161
logger.info(u"Changing Zeroconf service name to %r ...",
136
syslogger.setFormatter(logging.Formatter\
137
('Mandos (%s): %%(levelname)s:'
138
' %%(message)s' % self.name))
163
syslogger.setFormatter(logging.Formatter
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)
141
174
self.rename_count += 1
142
175
def remove(self):
143
176
"""Derived from the Avahi example code"""
144
if group is not None:
177
if self.group is not None:
147
180
"""Derived from the Avahi example code"""
150
group = dbus.Interface\
151
(bus.get_object(avahi.DBUS_NAME,
152
server.EntryGroupNew()),
153
avahi.DBUS_INTERFACE_ENTRY_GROUP)
154
group.connect_to_signal('StateChanged',
155
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)
156
189
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
157
service.name, service.type)
159
self.interface, # interface
160
avahi.PROTO_INET6, # protocol
161
dbus.UInt32(0), # flags
162
self.name, self.type,
163
self.domain, self.host,
164
dbus.UInt16(self.port),
165
avahi.string_array_to_txt_array(self.TXT))
168
# From the Avahi example code:
169
group = None # our entry group
170
# End of Avahi example code
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())
173
239
class Client(object):
174
240
"""A representation of a client host served by this server.
176
name: string; from the config file, used in log messages
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
246
checker: subprocess.Popen(); a running checker process used
247
to see if the client lives.
248
'None' if no process is running.
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
252
runtime with vars(self) as dict, so that for
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
256
current_checker_command: string; current running checker_command
257
disable_hook: If set, called by disable() as disable_hook(self)
258
disable_initiator_tag: a gobject event source tag, or None
177
260
fingerprint: string (40 or 32 hexadecimal digits); used to
178
261
uniquely identify the client
179
secret: bytestring; sent verbatim (over TLS) to client
180
host: string; available for use by the checker command
181
created: datetime.datetime(); object creation, not client host
182
last_checked_ok: datetime.datetime() or None if not yet checked OK
183
timeout: datetime.timedelta(); How long from last_checked_ok
184
until this client is invalid
185
interval: datetime.timedelta(); How often to start a new checker
186
stop_hook: If set, called by stop() as stop_hook(self)
187
checker: subprocess.Popen(); a running checker process used
188
to see if the client lives.
189
'None' if no process is running.
190
checker_initiator_tag: a gobject event source tag, or None
191
stop_initiator_tag: - '' -
192
checker_callback_tag: - '' -
193
checker_command: string; External command which is run to check if
194
client lives. %() expansions are done at
195
runtime with vars(self) as dict, so that for
196
instance %(name)s can be used in the command.
198
_timeout: Real variable for 'timeout'
199
_interval: Real variable for 'interval'
200
_timeout_milliseconds: Used when calling gobject.timeout_add()
201
_interval_milliseconds: - '' -
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.
203
def _set_timeout(self, timeout):
204
"Setter function for 'timeout' attribute"
205
self._timeout = timeout
206
self._timeout_milliseconds = ((self.timeout.days
207
* 24 * 60 * 60 * 1000)
208
+ (self.timeout.seconds * 1000)
209
+ (self.timeout.microseconds
211
timeout = property(lambda self: self._timeout,
214
def _set_interval(self, interval):
215
"Setter function for 'interval' attribute"
216
self._interval = interval
217
self._interval_milliseconds = ((self.interval.days
218
* 24 * 60 * 60 * 1000)
219
+ (self.interval.seconds
221
+ (self.interval.microseconds
223
interval = property(lambda self: self._interval,
226
def __init__(self, name = None, stop_hook=None, config=None):
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))
287
def timeout_milliseconds(self):
288
"Return the 'timeout' attribute in milliseconds"
289
return self._timedelta_to_milliseconds(self.timeout)
291
def interval_milliseconds(self):
292
"Return the 'interval' attribute in milliseconds"
293
return self._timedelta_to_milliseconds(self.interval)
295
def approval_delay_milliseconds(self):
296
return self._timedelta_to_milliseconds(self.approval_delay)
298
def __init__(self, name = None, disable_hook=None, config=None):
227
299
"""Note: the 'checker' key in 'config' sets the
228
300
'checker_command' attribute and *not* the 'checker'
230
303
if config is None:
233
305
logger.debug(u"Creating client %r", self.name)
234
306
# Uppercase and remove spaces from fingerprint for later
235
307
# comparison purposes with return value from the fingerprint()
237
self.fingerprint = config["fingerprint"].upper()\
309
self.fingerprint = (config[u"fingerprint"].upper()
239
311
logger.debug(u" Fingerprint: %s", self.fingerprint)
240
if "secret" in config:
241
self.secret = config["secret"].decode(u"base64")
242
elif "secfile" in config:
243
secfile = open(os.path.expanduser(os.path.expandvars
244
(config["secfile"])))
245
self.secret = secfile.read()
312
if u"secret" in config:
313
self.secret = config[u"secret"].decode(u"base64")
314
elif u"secfile" in config:
315
with open(os.path.expanduser(os.path.expandvars
316
(config[u"secfile"])),
318
self.secret = secfile.read()
248
320
raise TypeError(u"No secret or secfile for client %s"
250
self.host = config.get("host", "")
251
self.created = datetime.datetime.now()
322
self.host = config.get(u"host", u"")
323
self.created = datetime.datetime.utcnow()
325
self.last_approval_request = None
326
self.last_enabled = None
252
327
self.last_checked_ok = None
253
self.timeout = string_to_delta(config["timeout"])
254
self.interval = string_to_delta(config["interval"])
255
self.stop_hook = stop_hook
328
self.timeout = string_to_delta(config[u"timeout"])
329
self.interval = string_to_delta(config[u"interval"])
330
self.disable_hook = disable_hook
256
331
self.checker = None
257
332
self.checker_initiator_tag = None
258
self.stop_initiator_tag = None
333
self.disable_initiator_tag = None
259
334
self.checker_callback_tag = None
260
self.check_command = config["checker"]
335
self.checker_command = config[u"checker"]
336
self.current_checker_command = None
337
self.last_connect = None
338
self._approved = None
339
self.approved_by_default = config.get(u"approved_by_default",
341
self.approvals_pending = 0
342
self.approval_delay = string_to_delta(
343
config[u"approval_delay"])
344
self.approval_duration = string_to_delta(
345
config[u"approval_duration"])
346
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
348
def send_changedstate(self):
349
self.changedstate.acquire()
350
self.changedstate.notify_all()
351
self.changedstate.release()
262
354
"""Start this client's checker and timeout hooks"""
355
if getattr(self, u"enabled", False):
358
self.send_changedstate()
359
self.last_enabled = datetime.datetime.utcnow()
263
360
# Schedule a new checker to be started an 'interval' from now,
264
361
# and every interval from then on.
265
self.checker_initiator_tag = gobject.timeout_add\
266
(self._interval_milliseconds,
362
self.checker_initiator_tag = (gobject.timeout_add
363
(self.interval_milliseconds(),
365
# Schedule a disable() when 'timeout' has passed
366
self.disable_initiator_tag = (gobject.timeout_add
367
(self.timeout_milliseconds(),
268
370
# Also start a new checker *right now*.
269
371
self.start_checker()
270
# Schedule a stop() when 'timeout' has passed
271
self.stop_initiator_tag = gobject.timeout_add\
272
(self._timeout_milliseconds,
276
The possibility that a client might be restarted is left open,
277
but not currently used."""
278
# If this client doesn't have a secret, it is already stopped.
279
if hasattr(self, "secret") and self.secret:
280
logger.info(u"Stopping client %s", self.name)
373
def disable(self, quiet=True):
374
"""Disable this client."""
375
if not getattr(self, "enabled", False):
284
if getattr(self, "stop_initiator_tag", False):
285
gobject.source_remove(self.stop_initiator_tag)
286
self.stop_initiator_tag = None
287
if getattr(self, "checker_initiator_tag", False):
378
self.send_changedstate()
380
logger.info(u"Disabling client %s", self.name)
381
if getattr(self, u"disable_initiator_tag", False):
382
gobject.source_remove(self.disable_initiator_tag)
383
self.disable_initiator_tag = None
384
if getattr(self, u"checker_initiator_tag", False):
288
385
gobject.source_remove(self.checker_initiator_tag)
289
386
self.checker_initiator_tag = None
290
387
self.stop_checker()
388
if self.disable_hook:
389
self.disable_hook(self)
293
391
# Do not run this again if called by a gobject.timeout_add
295
394
def __del__(self):
296
self.stop_hook = None
298
def checker_callback(self, pid, condition):
395
self.disable_hook = None
398
def checker_callback(self, pid, condition, command):
299
399
"""The checker has completed, so take appropriate actions."""
300
now = datetime.datetime.now()
301
400
self.checker_callback_tag = None
302
401
self.checker = None
303
if os.WIFEXITED(condition) \
304
and (os.WEXITSTATUS(condition) == 0):
305
logger.info(u"Checker for %(name)s succeeded",
307
self.last_checked_ok = now
308
gobject.source_remove(self.stop_initiator_tag)
309
self.stop_initiator_tag = gobject.timeout_add\
310
(self._timeout_milliseconds,
312
elif not os.WIFEXITED(condition):
402
if os.WIFEXITED(condition):
403
exitstatus = os.WEXITSTATUS(condition)
405
logger.info(u"Checker for %(name)s succeeded",
409
logger.info(u"Checker for %(name)s failed",
313
412
logger.warning(u"Checker for %(name)s crashed?",
316
logger.info(u"Checker for %(name)s failed",
415
def checked_ok(self):
416
"""Bump up the timeout for this client.
418
This should only be called when the client has been seen,
421
self.last_checked_ok = datetime.datetime.utcnow()
422
gobject.source_remove(self.disable_initiator_tag)
423
self.disable_initiator_tag = (gobject.timeout_add
424
(self.timeout_milliseconds(),
427
def need_approval(self):
428
self.last_approval_request = datetime.datetime.utcnow()
318
430
def start_checker(self):
319
431
"""Start a new checker subprocess if one is not running.
320
433
If a checker already exists, leave it running and do
322
435
# The reason for not killing a running checker is that if we
351
485
# always replaced by /dev/null.)
352
486
self.checker = subprocess.Popen(command,
355
self.checker_callback_tag = gobject.child_watch_add\
357
self.checker_callback)
488
shell=True, cwd=u"/")
489
self.checker_callback_tag = (gobject.child_watch_add
491
self.checker_callback,
493
# The checker may have completed before the gobject
494
# watch was added. Check for this.
495
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
497
gobject.source_remove(self.checker_callback_tag)
498
self.checker_callback(pid, status, command)
358
499
except OSError, error:
359
500
logger.error(u"Failed to start subprocess: %s",
361
502
# Re-run this periodically if run by gobject.timeout_add
363
505
def stop_checker(self):
364
506
"""Force the checker process, if any, to stop."""
365
507
if self.checker_callback_tag:
366
508
gobject.source_remove(self.checker_callback_tag)
367
509
self.checker_callback_tag = None
368
if getattr(self, "checker", None) is None:
510
if getattr(self, u"checker", None) is None:
370
512
logger.debug(u"Stopping checker for %(name)s", vars(self))
372
514
os.kill(self.checker.pid, signal.SIGTERM)
374
516
#if self.checker.poll() is None:
375
517
# os.kill(self.checker.pid, signal.SIGKILL)
376
518
except OSError, error:
377
519
if error.errno != errno.ESRCH: # No such process
379
521
self.checker = None
380
def still_valid(self):
381
"""Has the timeout not yet passed for this client?"""
382
now = datetime.datetime.now()
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
871
_interface = u"se.bsnet.fukt.Mandos.Client"
875
# CheckerCompleted - signal
876
@dbus.service.signal(_interface, signature=u"nxs")
877
def CheckerCompleted(self, exitcode, waitstatus, command):
881
# CheckerStarted - signal
882
@dbus.service.signal(_interface, signature=u"s")
883
def CheckerStarted(self, command):
887
# PropertyChanged - signal
888
@dbus.service.signal(_interface, signature=u"sv")
889
def PropertyChanged(self, property, value):
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()
927
@dbus.service.method(_interface)
932
# StartChecker - method
933
@dbus.service.method(_interface)
934
def StartChecker(self):
939
@dbus.service.method(_interface)
944
# StopChecker - method
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:
383
1041
if self.last_checked_ok is None:
384
return now < (self.created + self.timeout)
386
return now < (self.last_checked_ok + self.timeout)
389
def peer_certificate(session):
390
"Return the peer's OpenPGP certificate as a bytestring"
391
# If not an OpenPGP certificate...
392
if gnutls.library.functions.gnutls_certificate_type_get\
393
(session._c_object) \
394
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
395
# ...do the normal thing
396
return session.peer_certificate
397
list_size = ctypes.c_uint()
398
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
399
(session._c_object, ctypes.byref(list_size))
400
if list_size.value == 0:
403
return ctypes.string_at(cert.data, cert.size)
406
def fingerprint(openpgp):
407
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
408
# New GnuTLS "datum" with the OpenPGP public key
409
datum = gnutls.library.types.gnutls_datum_t\
410
(ctypes.cast(ctypes.c_char_p(openpgp),
411
ctypes.POINTER(ctypes.c_ubyte)),
412
ctypes.c_uint(len(openpgp)))
413
# New empty GnuTLS certificate
414
crt = gnutls.library.types.gnutls_openpgp_crt_t()
415
gnutls.library.functions.gnutls_openpgp_crt_init\
417
# Import the OpenPGP public key into the certificate
418
gnutls.library.functions.gnutls_openpgp_crt_import\
419
(crt, ctypes.byref(datum),
420
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
421
# Verify the self signature in the key
422
crtverify = ctypes.c_uint()
423
gnutls.library.functions.gnutls_openpgp_crt_verify_self\
424
(crt, 0, ctypes.byref(crtverify))
425
if crtverify.value != 0:
426
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
427
raise gnutls.errors.CertificateSecurityError("Verify failed")
428
# New buffer for the fingerprint
429
buf = ctypes.create_string_buffer(20)
430
buf_len = ctypes.c_size_t()
431
# Get the fingerprint from the certificate into the buffer
432
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
433
(crt, ctypes.byref(buf), ctypes.byref(buf_len))
434
# Deinit the certificate
435
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
436
# Convert the buffer to a Python bytestring
437
fpr = ctypes.string_at(buf, buf_len.value)
438
# Convert the bytestring to hexadecimal notation
439
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
443
class TCP_handler(SocketServer.BaseRequestHandler, object):
444
"""A TCP request handler class.
445
Instantiated by IPv6_TCPServer for each request to handle it.
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)
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.
446
1168
Note: This will run in its own forked process."""
448
1170
def handle(self):
449
logger.info(u"TCP connection from: %s",
450
unicode(self.client_address))
451
session = gnutls.connection.ClientSession\
452
(self.request, gnutls.connection.X509Credentials())
454
line = self.request.makefile().readline()
455
logger.debug(u"Protocol version: %r", line)
457
if int(line.strip().split()[0]) > 1:
459
except (ValueError, IndexError, RuntimeError), error:
460
logger.error(u"Unknown protocol version: %s", error)
463
# Note: gnutls.connection.X509Credentials is really a generic
464
# GnuTLS certificate credentials object so long as no X.509
465
# keys are added to it. Therefore, we can use it here despite
466
# using OpenPGP certificates.
468
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
469
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
471
priority = "NORMAL" # Fallback default, since this
473
if self.server.settings["priority"]:
474
priority = self.server.settings["priority"]
475
gnutls.library.functions.gnutls_priority_set_direct\
476
(session._c_object, priority, None)
480
except gnutls.errors.GNUTLSError, error:
481
logger.warning(u"Handshake failed: %s", error)
482
# Do not run session.bye() here: the session is not
483
# established. Just abandon the request.
486
fpr = fingerprint(peer_certificate(session))
487
except (TypeError, gnutls.errors.GNUTLSError), error:
488
logger.warning(u"Bad certificate: %s", error)
491
logger.debug(u"Fingerprint: %s", fpr)
493
for c in self.server.clients:
494
if c.fingerprint == fpr:
498
logger.warning(u"Client not found for fingerprint: %s",
502
# Have to check if client.still_valid(), since it is possible
503
# that the client timed out while establishing the GnuTLS
505
if not client.still_valid():
506
logger.warning(u"Client %(name)s is invalid",
511
while sent_size < len(client.secret):
512
sent = session.send(client.secret[sent_size:])
513
logger.debug(u"Sent: %d, remaining: %d",
514
sent, len(client.secret)
515
- (sent_size + sent))
520
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
521
"""IPv6 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
523
settings: Server settings
524
clients: Set() of Client objects
525
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
527
address_family = socket.AF_INET6
528
def __init__(self, *args, **kwargs):
529
if "settings" in kwargs:
530
self.settings = kwargs["settings"]
531
del kwargs["settings"]
532
if "clients" in kwargs:
533
self.clients = kwargs["clients"]
534
del kwargs["clients"]
536
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)
537
1429
def server_bind(self):
538
1430
"""This overrides the normal server_bind() function
539
1431
to bind to an interface if one was specified, and also NOT to
540
1432
bind to an address or port if they were not specified."""
541
if self.settings["interface"]:
542
# 25 is from /usr/include/asm-i486/socket.h
543
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
545
self.socket.setsockopt(socket.SOL_SOCKET,
547
self.settings["interface"])
548
except socket.error, error:
549
if error[0] == errno.EPERM:
550
logger.error(u"No permission to"
551
u" bind to interface %s",
552
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",
555
1455
# Only bind(2) the socket if we really need to.
556
1456
if self.server_address[0] or self.server_address[1]:
557
1457
if not self.server_address[0]:
559
self.server_address = (in6addr_any,
1458
if self.address_family == socket.AF_INET6:
1459
any_address = u"::" # in6addr_any
1461
any_address = socket.INADDR_ANY
1462
self.server_address = (any_address,
560
1463
self.server_address[1])
561
1464
elif not self.server_address[1]:
562
1465
self.server_address = (self.server_address[0],
564
# if self.settings["interface"]:
1467
# if self.interface:
565
1468
# self.server_address = (self.server_address[0],
568
1471
# if_nametoindex
571
return super(IPv6_TCPServer, self).server_bind()
1473
return socketserver.TCPServer.server_bind(self)
1476
class MandosServer(IPv6_TCPServer):
1480
clients: set of Client objects
1481
gnutls_priority GnuTLS priority string
1482
use_dbus: Boolean; to emit D-Bus signals or not
1484
Assumes a gobject.MainLoop event loop.
1486
def __init__(self, server_address, RequestHandlerClass,
1487
interface=None, use_ipv6=True, clients=None,
1488
gnutls_priority=None, use_dbus=True):
1489
self.enabled = False
1490
self.clients = clients
1491
if self.clients is None:
1492
self.clients = set()
1493
self.use_dbus = use_dbus
1494
self.gnutls_priority = gnutls_priority
1495
IPv6_TCPServer.__init__(self, server_address,
1496
RequestHandlerClass,
1497
interface = interface,
1498
use_ipv6 = use_ipv6)
572
1499
def server_activate(self):
573
1500
if self.enabled:
574
return super(IPv6_TCPServer, self).server_activate()
1501
return socketserver.TCPServer.server_activate(self)
575
1502
def enable(self):
576
1503
self.enabled = True
1504
def add_pipe(self, parent_pipe):
1505
# Call "handle_ipc" for both data and EOF events
1506
gobject.io_add_watch(parent_pipe.fileno(),
1507
gobject.IO_IN | gobject.IO_HUP,
1508
functools.partial(self.handle_ipc,
1509
parent_pipe = parent_pipe))
1511
def handle_ipc(self, source, condition, parent_pipe=None,
1512
client_object=None):
1514
gobject.IO_IN: u"IN", # There is data to read.
1515
gobject.IO_OUT: u"OUT", # Data can be written (without
1517
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1518
gobject.IO_ERR: u"ERR", # Error condition.
1519
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1520
# broken, usually for pipes and
1523
conditions_string = ' | '.join(name
1525
condition_names.iteritems()
1526
if cond & condition)
1527
# error or the other end of multiprocessing.Pipe has closed
1528
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1531
# Read a request from the child
1532
request = parent_pipe.recv()
1533
command = request[0]
1535
if command == 'init':
1537
address = request[2]
1539
for c in self.clients:
1540
if c.fingerprint == fpr:
1544
logger.warning(u"Client not found for fingerprint: %s, ad"
1545
u"dress: %s", fpr, address)
1548
mandos_dbus_service.ClientNotFound(fpr, address[0])
1549
parent_pipe.send(False)
1552
gobject.io_add_watch(parent_pipe.fileno(),
1553
gobject.IO_IN | gobject.IO_HUP,
1554
functools.partial(self.handle_ipc,
1555
parent_pipe = parent_pipe,
1556
client_object = client))
1557
parent_pipe.send(True)
1558
# remove the old hook in favor of the new above hook on same fileno
1560
if command == 'funcall':
1561
funcname = request[1]
1565
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1567
if command == 'getattr':
1568
attrname = request[1]
1569
if callable(client_object.__getattribute__(attrname)):
1570
parent_pipe.send(('function',))
1572
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1574
if command == 'setattr':
1575
attrname = request[1]
1577
setattr(client_object, attrname, value)
579
1582
def string_to_delta(interval):
580
1583
"""Parse a string and return a datetime.timedelta
582
>>> string_to_delta('7d')
1585
>>> string_to_delta(u'7d')
583
1586
datetime.timedelta(7)
584
>>> string_to_delta('60s')
1587
>>> string_to_delta(u'60s')
585
1588
datetime.timedelta(0, 60)
586
>>> string_to_delta('60m')
1589
>>> string_to_delta(u'60m')
587
1590
datetime.timedelta(0, 3600)
588
>>> string_to_delta('24h')
1591
>>> string_to_delta(u'24h')
589
1592
datetime.timedelta(1)
590
1593
>>> string_to_delta(u'1w')
591
1594
datetime.timedelta(7)
592
>>> string_to_delta('5m 30s')
1595
>>> string_to_delta(u'5m 30s')
593
1596
datetime.timedelta(0, 330)
595
1598
timevalue = datetime.timedelta(0)