148
140
self.rename_count = 0
149
141
self.max_renames = max_renames
150
142
self.protocol = protocol
151
self.group = None # our entry group
154
143
def rename(self):
155
144
"""Derived from the Avahi example code"""
156
145
if self.rename_count >= self.max_renames:
157
logger.critical("No suitable Zeroconf service name found"
158
" after %i retries, exiting.",
146
logger.critical(u"No suitable Zeroconf service name found"
147
u" after %i retries, exiting.",
159
148
self.rename_count)
160
raise AvahiServiceError("Too many renames")
161
self.name = unicode(self.server.GetAlternativeServiceName(self.name))
162
logger.info("Changing Zeroconf service name to %r ...",
149
raise AvahiServiceError(u"Too many renames")
150
self.name = server.GetAlternativeServiceName(self.name)
151
logger.info(u"Changing Zeroconf service name to %r ...",
164
153
syslogger.setFormatter(logging.Formatter
165
('Mandos (%s) [%%(process)d]:'
166
' %%(levelname)s: %%(message)s'
154
(u'Mandos (%s) [%%(process)d]:'
155
u' %%(levelname)s: %%(message)s'
171
except dbus.exceptions.DBusException, error:
172
logger.critical("DBusException: %s", error)
175
159
self.rename_count += 1
176
160
def remove(self):
177
161
"""Derived from the Avahi example code"""
178
if self.group is not None:
162
if group is not None:
181
165
"""Derived from the Avahi example code"""
182
if self.group is None:
183
self.group = dbus.Interface(
184
self.bus.get_object(avahi.DBUS_NAME,
185
self.server.EntryGroupNew()),
186
avahi.DBUS_INTERFACE_ENTRY_GROUP)
187
self.group.connect_to_signal('StateChanged',
189
.entry_group_state_changed)
190
logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
191
self.name, self.type)
192
self.group.AddService(
195
dbus.UInt32(0), # flags
196
self.name, self.type,
197
self.domain, self.host,
198
dbus.UInt16(self.port),
199
avahi.string_array_to_txt_array(self.TXT))
201
def entry_group_state_changed(self, state, error):
202
"""Derived from the Avahi example code"""
203
logger.debug("Avahi entry group state change: %i", state)
205
if state == avahi.ENTRY_GROUP_ESTABLISHED:
206
logger.debug("Zeroconf service established.")
207
elif state == avahi.ENTRY_GROUP_COLLISION:
208
logger.info("Zeroconf service name collision.")
210
elif state == avahi.ENTRY_GROUP_FAILURE:
211
logger.critical("Avahi: Error in group state changed %s",
213
raise AvahiGroupError("State changed: %s"
216
"""Derived from the Avahi example code"""
217
if self.group is not None:
220
def server_state_changed(self, state):
221
"""Derived from the Avahi example code"""
222
logger.debug("Avahi server state change: %i", state)
223
if state == avahi.SERVER_COLLISION:
224
logger.error("Zeroconf server name collision")
226
elif state == avahi.SERVER_RUNNING:
229
"""Derived from the Avahi example code"""
230
if self.server is None:
231
self.server = dbus.Interface(
232
self.bus.get_object(avahi.DBUS_NAME,
233
avahi.DBUS_PATH_SERVER),
234
avahi.DBUS_INTERFACE_SERVER)
235
self.server.connect_to_signal("StateChanged",
236
self.server_state_changed)
237
self.server_state_changed(self.server.GetState())
168
group = dbus.Interface(bus.get_object
170
server.EntryGroupNew()),
171
avahi.DBUS_INTERFACE_ENTRY_GROUP)
172
group.connect_to_signal('StateChanged',
173
entry_group_state_changed)
174
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
175
service.name, service.type)
177
self.interface, # interface
178
self.protocol, # protocol
179
dbus.UInt32(0), # flags
180
self.name, self.type,
181
self.domain, self.host,
182
dbus.UInt16(self.port),
183
avahi.string_array_to_txt_array(self.TXT))
186
# From the Avahi example code:
187
group = None # our entry group
188
# End of Avahi example code
191
def _datetime_to_dbus(dt, variant_level=0):
192
"""Convert a UTC datetime.datetime() to a D-Bus type."""
193
return dbus.String(dt.isoformat(), variant_level=variant_level)
240
196
class Client(object):
241
197
"""A representation of a client host served by this server.
244
_approved: bool(); 'None' if not yet approved/disapproved
245
approval_delay: datetime.timedelta(); Time to wait for approval
246
approval_duration: datetime.timedelta(); Duration of one approval
200
name: string; from the config file, used in log messages and
202
fingerprint: string (40 or 32 hexadecimal digits); used to
203
uniquely identify the client
204
secret: bytestring; sent verbatim (over TLS) to client
205
host: string; available for use by the checker command
206
created: datetime.datetime(); (UTC) object creation
207
last_enabled: datetime.datetime(); (UTC)
209
last_checked_ok: datetime.datetime(); (UTC) or None
210
timeout: datetime.timedelta(); How long from last_checked_ok
211
until this client is invalid
212
interval: datetime.timedelta(); How often to start a new checker
213
disable_hook: If set, called by disable() as disable_hook(self)
247
214
checker: subprocess.Popen(); a running checker process used
248
215
to see if the client lives.
249
216
'None' if no process is running.
250
checker_callback_tag: a gobject event source tag, or None
251
checker_command: string; External command which is run to check
252
if client lives. %() expansions are done at
217
checker_initiator_tag: a gobject event source tag, or None
218
disable_initiator_tag: - '' -
219
checker_callback_tag: - '' -
220
checker_command: string; External command which is run to check if
221
client lives. %() expansions are done at
253
222
runtime with vars(self) as dict, so that for
254
223
instance %(name)s can be used in the command.
255
checker_initiator_tag: a gobject event source tag, or None
256
created: datetime.datetime(); (UTC) object creation
257
224
current_checker_command: string; current running checker_command
258
disable_hook: If set, called by disable() as disable_hook(self)
259
disable_initiator_tag: a gobject event source tag, or None
261
fingerprint: string (40 or 32 hexadecimal digits); used to
262
uniquely identify the client
263
host: string; available for use by the checker command
264
interval: datetime.timedelta(); How often to start a new checker
265
last_approval_request: datetime.datetime(); (UTC) or None
266
last_checked_ok: datetime.datetime(); (UTC) or None
267
last_enabled: datetime.datetime(); (UTC)
268
name: string; from the config file, used in log messages and
270
secret: bytestring; sent verbatim (over TLS) to client
271
timeout: datetime.timedelta(); How long from last_checked_ok
272
until this client is disabled
273
runtime_expansions: Allowed attributes for runtime expansion.
276
runtime_expansions = ("approval_delay", "approval_duration",
277
"created", "enabled", "fingerprint",
278
"host", "interval", "last_checked_ok",
279
"last_enabled", "name", "timeout")
282
def _timedelta_to_milliseconds(td):
283
"Convert a datetime.timedelta() to milliseconds"
284
return ((td.days * 24 * 60 * 60 * 1000)
285
+ (td.seconds * 1000)
286
+ (td.microseconds // 1000))
228
def _datetime_to_milliseconds(dt):
229
"Convert a datetime.datetime() to milliseconds"
230
return ((dt.days * 24 * 60 * 60 * 1000)
231
+ (dt.seconds * 1000)
232
+ (dt.microseconds // 1000))
288
234
def timeout_milliseconds(self):
289
235
"Return the 'timeout' attribute in milliseconds"
290
return self._timedelta_to_milliseconds(self.timeout)
236
return self._datetime_to_milliseconds(self.timeout)
292
238
def interval_milliseconds(self):
293
239
"Return the 'interval' attribute in milliseconds"
294
return self._timedelta_to_milliseconds(self.interval)
296
def approval_delay_milliseconds(self):
297
return self._timedelta_to_milliseconds(self.approval_delay)
240
return self._datetime_to_milliseconds(self.interval)
299
242
def __init__(self, name = None, disable_hook=None, config=None):
300
243
"""Note: the 'checker' key in 'config' sets the
304
247
if config is None:
306
logger.debug("Creating client %r", self.name)
249
logger.debug(u"Creating client %r", self.name)
307
250
# Uppercase and remove spaces from fingerprint for later
308
251
# comparison purposes with return value from the fingerprint()
310
self.fingerprint = (config["fingerprint"].upper()
312
logger.debug(" Fingerprint: %s", self.fingerprint)
313
if "secret" in config:
314
self.secret = config["secret"].decode("base64")
315
elif "secfile" in config:
316
with open(os.path.expanduser(os.path.expandvars
317
(config["secfile"])),
253
self.fingerprint = (config[u"fingerprint"].upper()
255
logger.debug(u" Fingerprint: %s", self.fingerprint)
256
if u"secret" in config:
257
self.secret = config[u"secret"].decode(u"base64")
258
elif u"secfile" in config:
259
with closing(open(os.path.expanduser
261
(config[u"secfile"])))) as secfile:
319
262
self.secret = secfile.read()
321
raise TypeError("No secret or secfile for client %s"
264
raise TypeError(u"No secret or secfile for client %s"
323
self.host = config.get("host", "")
266
self.host = config.get(u"host", u"")
324
267
self.created = datetime.datetime.utcnow()
325
268
self.enabled = False
326
self.last_approval_request = None
327
269
self.last_enabled = None
328
270
self.last_checked_ok = None
329
self.timeout = string_to_delta(config["timeout"])
330
self.interval = string_to_delta(config["interval"])
271
self.timeout = string_to_delta(config[u"timeout"])
272
self.interval = string_to_delta(config[u"interval"])
331
273
self.disable_hook = disable_hook
332
274
self.checker = None
333
275
self.checker_initiator_tag = None
334
276
self.disable_initiator_tag = None
335
277
self.checker_callback_tag = None
336
self.checker_command = config["checker"]
278
self.checker_command = config[u"checker"]
337
279
self.current_checker_command = None
338
280
self.last_connect = None
339
self._approved = None
340
self.approved_by_default = config.get("approved_by_default",
342
self.approvals_pending = 0
343
self.approval_delay = string_to_delta(
344
config["approval_delay"])
345
self.approval_duration = string_to_delta(
346
config["approval_duration"])
347
self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
349
def send_changedstate(self):
350
self.changedstate.acquire()
351
self.changedstate.notify_all()
352
self.changedstate.release()
354
282
def enable(self):
355
283
"""Start this client's checker and timeout hooks"""
356
if getattr(self, "enabled", False):
359
self.send_changedstate()
360
284
self.last_enabled = datetime.datetime.utcnow()
361
285
# Schedule a new checker to be started an 'interval' from now,
362
286
# and every interval from then on.
363
287
self.checker_initiator_tag = (gobject.timeout_add
364
288
(self.interval_milliseconds(),
365
289
self.start_checker))
290
# Also start a new checker *right now*.
366
292
# Schedule a disable() when 'timeout' has passed
367
293
self.disable_initiator_tag = (gobject.timeout_add
368
294
(self.timeout_milliseconds(),
370
296
self.enabled = True
371
# Also start a new checker *right now*.
374
def disable(self, quiet=True):
375
299
"""Disable this client."""
376
300
if not getattr(self, "enabled", False):
379
self.send_changedstate()
381
logger.info("Disabling client %s", self.name)
382
if getattr(self, "disable_initiator_tag", False):
302
logger.info(u"Disabling client %s", self.name)
303
if getattr(self, u"disable_initiator_tag", False):
383
304
gobject.source_remove(self.disable_initiator_tag)
384
305
self.disable_initiator_tag = None
385
if getattr(self, "checker_initiator_tag", False):
306
if getattr(self, u"checker_initiator_tag", False):
386
307
gobject.source_remove(self.checker_initiator_tag)
387
308
self.checker_initiator_tag = None
388
309
self.stop_checker()
508
419
if self.checker_callback_tag:
509
420
gobject.source_remove(self.checker_callback_tag)
510
421
self.checker_callback_tag = None
511
if getattr(self, "checker", None) is None:
422
if getattr(self, u"checker", None) is None:
513
logger.debug("Stopping checker for %(name)s", vars(self))
424
logger.debug(u"Stopping checker for %(name)s", vars(self))
515
426
os.kill(self.checker.pid, signal.SIGTERM)
517
428
#if self.checker.poll() is None:
518
429
# os.kill(self.checker.pid, signal.SIGKILL)
519
430
except OSError, error:
520
431
if error.errno != errno.ESRCH: # No such process
522
433
self.checker = None
524
def dbus_service_property(dbus_interface, signature="v",
525
access="readwrite", byte_arrays=False):
526
"""Decorators for marking methods of a DBusObjectWithProperties to
527
become properties on the D-Bus.
529
The decorated method will be called with no arguments by "Get"
530
and with one argument by "Set".
532
The parameters, where they are supported, are the same as
533
dbus.service.method, except there is only "signature", since the
534
type from Get() and the type sent to Set() is the same.
536
# Encoding deeply encoded byte arrays is not supported yet by the
537
# "Set" method, so we fail early here:
538
if byte_arrays and signature != "ay":
539
raise ValueError("Byte arrays not supported for non-'ay'"
540
" signature %r" % signature)
542
func._dbus_is_property = True
543
func._dbus_interface = dbus_interface
544
func._dbus_signature = signature
545
func._dbus_access = access
546
func._dbus_name = func.__name__
547
if func._dbus_name.endswith("_dbus_property"):
548
func._dbus_name = func._dbus_name[:-14]
549
func._dbus_get_args_options = {'byte_arrays': byte_arrays }
554
class DBusPropertyException(dbus.exceptions.DBusException):
555
"""A base class for D-Bus property-related exceptions
557
def __unicode__(self):
558
return unicode(str(self))
561
class DBusPropertyAccessException(DBusPropertyException):
562
"""A property's access permissions disallows an operation.
567
class DBusPropertyNotFound(DBusPropertyException):
568
"""An attempt was made to access a non-existing property.
573
class DBusObjectWithProperties(dbus.service.Object):
574
"""A D-Bus object with properties.
576
Classes inheriting from this can use the dbus_service_property
577
decorator to expose methods as D-Bus properties. It exposes the
578
standard Get(), Set(), and GetAll() methods on the D-Bus.
582
def _is_dbus_property(obj):
583
return getattr(obj, "_dbus_is_property", False)
585
def _get_all_dbus_properties(self):
586
"""Returns a generator of (name, attribute) pairs
588
return ((prop._dbus_name, prop)
590
inspect.getmembers(self, self._is_dbus_property))
592
def _get_dbus_property(self, interface_name, property_name):
593
"""Returns a bound method if one exists which is a D-Bus
594
property with the specified name and interface.
596
for name in (property_name,
597
property_name + "_dbus_property"):
598
prop = getattr(self, name, None)
600
or not self._is_dbus_property(prop)
601
or prop._dbus_name != property_name
602
or (interface_name and prop._dbus_interface
603
and interface_name != prop._dbus_interface)):
607
raise DBusPropertyNotFound(self.dbus_object_path + ":"
608
+ interface_name + "."
611
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
613
def Get(self, interface_name, property_name):
614
"""Standard D-Bus property Get() method, see D-Bus standard.
616
prop = self._get_dbus_property(interface_name, property_name)
617
if prop._dbus_access == "write":
618
raise DBusPropertyAccessException(property_name)
620
if not hasattr(value, "variant_level"):
622
return type(value)(value, variant_level=value.variant_level+1)
624
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ssv")
625
def Set(self, interface_name, property_name, value):
626
"""Standard D-Bus property Set() method, see D-Bus standard.
628
prop = self._get_dbus_property(interface_name, property_name)
629
if prop._dbus_access == "read":
630
raise DBusPropertyAccessException(property_name)
631
if prop._dbus_get_args_options["byte_arrays"]:
632
# The byte_arrays option is not supported yet on
633
# signatures other than "ay".
634
if prop._dbus_signature != "ay":
636
value = dbus.ByteArray(''.join(unichr(byte)
640
@dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="s",
641
out_signature="a{sv}")
642
def GetAll(self, interface_name):
643
"""Standard D-Bus property GetAll() method, see D-Bus
646
Note: Will not include properties with access="write".
649
for name, prop in self._get_all_dbus_properties():
651
and interface_name != prop._dbus_interface):
652
# Interface non-empty but did not match
654
# Ignore write-only properties
655
if prop._dbus_access == "write":
658
if not hasattr(value, "variant_level"):
661
all[name] = type(value)(value, variant_level=
662
value.variant_level+1)
663
return dbus.Dictionary(all, signature="sv")
665
@dbus.service.method(dbus.INTROSPECTABLE_IFACE,
667
path_keyword='object_path',
668
connection_keyword='connection')
669
def Introspect(self, object_path, connection):
670
"""Standard D-Bus method, overloaded to insert property tags.
672
xmlstring = dbus.service.Object.Introspect(self, object_path,
675
document = xml.dom.minidom.parseString(xmlstring)
676
def make_tag(document, name, prop):
677
e = document.createElement("property")
678
e.setAttribute("name", name)
679
e.setAttribute("type", prop._dbus_signature)
680
e.setAttribute("access", prop._dbus_access)
682
for if_tag in document.getElementsByTagName("interface"):
683
for tag in (make_tag(document, name, prop)
685
in self._get_all_dbus_properties()
686
if prop._dbus_interface
687
== if_tag.getAttribute("name")):
688
if_tag.appendChild(tag)
689
# Add the names to the return values for the
690
# "org.freedesktop.DBus.Properties" methods
691
if (if_tag.getAttribute("name")
692
== "org.freedesktop.DBus.Properties"):
693
for cn in if_tag.getElementsByTagName("method"):
694
if cn.getAttribute("name") == "Get":
695
for arg in cn.getElementsByTagName("arg"):
696
if (arg.getAttribute("direction")
698
arg.setAttribute("name", "value")
699
elif cn.getAttribute("name") == "GetAll":
700
for arg in cn.getElementsByTagName("arg"):
701
if (arg.getAttribute("direction")
703
arg.setAttribute("name", "props")
704
xmlstring = document.toxml("utf-8")
706
except (AttributeError, xml.dom.DOMException,
707
xml.parsers.expat.ExpatError), error:
708
logger.error("Failed to override Introspection method",
713
class ClientDBus(Client, DBusObjectWithProperties):
435
def still_valid(self):
436
"""Has the timeout not yet passed for this client?"""
437
if not getattr(self, u"enabled", False):
439
now = datetime.datetime.utcnow()
440
if self.last_checked_ok is None:
441
return now < (self.created + self.timeout)
443
return now < (self.last_checked_ok + self.timeout)
446
class ClientDBus(Client, dbus.service.Object):
714
447
"""A Client class using D-Bus
717
dbus_object_path: dbus.ObjectPath
718
bus: dbus.SystemBus()
450
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
721
runtime_expansions = (Client.runtime_expansions
722
+ ("dbus_object_path",))
724
452
# dbus.service.Object doesn't use super(), so we can't either.
726
def __init__(self, bus = None, *args, **kwargs):
727
self._approvals_pending = 0
454
def __init__(self, *args, **kwargs):
729
455
Client.__init__(self, *args, **kwargs)
730
456
# Only now, when this client is initialized, can it show up on
732
client_object_name = unicode(self.name).translate(
735
458
self.dbus_object_path = (dbus.ObjectPath
736
("/clients/" + client_object_name))
737
DBusObjectWithProperties.__init__(self, self.bus,
738
self.dbus_object_path)
740
def _get_approvals_pending(self):
741
return self._approvals_pending
742
def _set_approvals_pending(self, value):
743
old_value = self._approvals_pending
744
self._approvals_pending = value
746
if (hasattr(self, "dbus_object_path")
747
and bval is not bool(old_value)):
748
dbus_bool = dbus.Boolean(bval, variant_level=1)
749
self.PropertyChanged(dbus.String("ApprovalPending"),
752
approvals_pending = property(_get_approvals_pending,
753
_set_approvals_pending)
754
del _get_approvals_pending, _set_approvals_pending
757
def _datetime_to_dbus(dt, variant_level=0):
758
"""Convert a UTC datetime.datetime() to a D-Bus type."""
759
return dbus.String(dt.isoformat(),
760
variant_level=variant_level)
460
+ self.name.replace(u".", u"_")))
461
dbus.service.Object.__init__(self, bus,
462
self.dbus_object_path)
762
463
def enable(self):
763
oldstate = getattr(self, "enabled", False)
464
oldstate = getattr(self, u"enabled", False)
764
465
r = Client.enable(self)
765
466
if oldstate != self.enabled:
766
467
# Emit D-Bus signals
767
self.PropertyChanged(dbus.String("Enabled"),
468
self.PropertyChanged(dbus.String(u"enabled"),
768
469
dbus.Boolean(True, variant_level=1))
769
self.PropertyChanged(
770
dbus.String("LastEnabled"),
771
self._datetime_to_dbus(self.last_enabled,
470
self.PropertyChanged(dbus.String(u"last_enabled"),
471
(_datetime_to_dbus(self.last_enabled,
775
def disable(self, quiet = False):
776
oldstate = getattr(self, "enabled", False)
777
r = Client.disable(self, quiet=quiet)
778
if not quiet and oldstate != self.enabled:
475
def disable(self, signal = True):
476
oldstate = getattr(self, u"enabled", False)
477
r = Client.disable(self)
478
if signal and oldstate != self.enabled:
779
479
# Emit D-Bus signal
780
self.PropertyChanged(dbus.String("Enabled"),
480
self.PropertyChanged(dbus.String(u"enabled"),
781
481
dbus.Boolean(False, variant_level=1))
843
534
# Emit D-Bus signal
844
535
self.CheckerStarted(self.current_checker_command)
845
536
self.PropertyChanged(
846
dbus.String("CheckerRunning"),
537
dbus.String(u"checker_running"),
847
538
dbus.Boolean(True, variant_level=1))
850
541
def stop_checker(self, *args, **kwargs):
851
old_checker = getattr(self, "checker", None)
542
old_checker = getattr(self, u"checker", None)
852
543
r = Client.stop_checker(self, *args, **kwargs)
853
544
if (old_checker is not None
854
and getattr(self, "checker", None) is None):
855
self.PropertyChanged(dbus.String("CheckerRunning"),
545
and getattr(self, u"checker", None) is None):
546
self.PropertyChanged(dbus.String(u"checker_running"),
856
547
dbus.Boolean(False, variant_level=1))
859
def _reset_approved(self):
860
self._approved = None
863
def approve(self, value=True):
864
self.send_changedstate()
865
self._approved = value
866
gobject.timeout_add(self._timedelta_to_milliseconds
867
(self.approval_duration),
868
self._reset_approved)
871
## D-Bus methods, signals & properties
872
_interface = "se.bsnet.fukt.Mandos.Client"
550
## D-Bus methods & signals
551
_interface = u"se.bsnet.fukt.Mandos.Client"
554
@dbus.service.method(_interface)
556
return self.checked_ok()
876
558
# CheckerCompleted - signal
877
@dbus.service.signal(_interface, signature="nxs")
559
@dbus.service.signal(_interface, signature=u"nxs")
878
560
def CheckerCompleted(self, exitcode, waitstatus, command):
882
564
# CheckerStarted - signal
883
@dbus.service.signal(_interface, signature="s")
565
@dbus.service.signal(_interface, signature=u"s")
884
566
def CheckerStarted(self, command):
570
# GetAllProperties - method
571
@dbus.service.method(_interface, out_signature=u"a{sv}")
572
def GetAllProperties(self):
574
return dbus.Dictionary({
575
dbus.String(u"name"):
576
dbus.String(self.name, variant_level=1),
577
dbus.String(u"fingerprint"):
578
dbus.String(self.fingerprint, variant_level=1),
579
dbus.String(u"host"):
580
dbus.String(self.host, variant_level=1),
581
dbus.String(u"created"):
582
_datetime_to_dbus(self.created, variant_level=1),
583
dbus.String(u"last_enabled"):
584
(_datetime_to_dbus(self.last_enabled,
586
if self.last_enabled is not None
587
else dbus.Boolean(False, variant_level=1)),
588
dbus.String(u"enabled"):
589
dbus.Boolean(self.enabled, variant_level=1),
590
dbus.String(u"last_checked_ok"):
591
(_datetime_to_dbus(self.last_checked_ok,
593
if self.last_checked_ok is not None
594
else dbus.Boolean (False, variant_level=1)),
595
dbus.String(u"timeout"):
596
dbus.UInt64(self.timeout_milliseconds(),
598
dbus.String(u"interval"):
599
dbus.UInt64(self.interval_milliseconds(),
601
dbus.String(u"checker"):
602
dbus.String(self.checker_command,
604
dbus.String(u"checker_running"):
605
dbus.Boolean(self.checker is not None,
607
dbus.String(u"object_path"):
608
dbus.ObjectPath(self.dbus_object_path,
612
# IsStillValid - method
613
@dbus.service.method(_interface, out_signature=u"b")
614
def IsStillValid(self):
615
return self.still_valid()
888
617
# PropertyChanged - signal
889
@dbus.service.signal(_interface, signature="sv")
618
@dbus.service.signal(_interface, signature=u"sv")
890
619
def PropertyChanged(self, property, value):
623
# ReceivedSecret - signal
895
624
@dbus.service.signal(_interface)
898
Is sent after a successful transfer of secret from the Mandos
899
server to mandos-client
625
def ReceivedSecret(self):
903
629
# Rejected - signal
904
@dbus.service.signal(_interface, signature="s")
905
def Rejected(self, reason):
630
@dbus.service.signal(_interface)
909
# NeedApproval - signal
910
@dbus.service.signal(_interface, signature="tb")
911
def NeedApproval(self, timeout, default):
913
return self.need_approval()
918
@dbus.service.method(_interface, in_signature="b")
919
def Approve(self, value):
923
@dbus.service.method(_interface)
925
return self.checked_ok()
635
# SetChecker - method
636
@dbus.service.method(_interface, in_signature=u"s")
637
def SetChecker(self, checker):
638
"D-Bus setter method"
639
self.checker_command = checker
641
self.PropertyChanged(dbus.String(u"checker"),
642
dbus.String(self.checker_command,
646
@dbus.service.method(_interface, in_signature=u"s")
647
def SetHost(self, host):
648
"D-Bus setter method"
651
self.PropertyChanged(dbus.String(u"host"),
652
dbus.String(self.host, variant_level=1))
654
# SetInterval - method
655
@dbus.service.method(_interface, in_signature=u"t")
656
def SetInterval(self, milliseconds):
657
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
659
self.PropertyChanged(dbus.String(u"interval"),
660
(dbus.UInt64(self.interval_milliseconds(),
664
@dbus.service.method(_interface, in_signature=u"ay",
666
def SetSecret(self, secret):
667
"D-Bus setter method"
668
self.secret = str(secret)
670
# SetTimeout - method
671
@dbus.service.method(_interface, in_signature=u"t")
672
def SetTimeout(self, milliseconds):
673
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
675
self.PropertyChanged(dbus.String(u"timeout"),
676
(dbus.UInt64(self.timeout_milliseconds(),
927
679
# Enable - method
928
680
@dbus.service.method(_interface)
947
699
def StopChecker(self):
948
700
self.stop_checker()
952
# ApprovalPending - property
953
@dbus_service_property(_interface, signature="b", access="read")
954
def ApprovalPending_dbus_property(self):
955
return dbus.Boolean(bool(self.approvals_pending))
957
# ApprovedByDefault - property
958
@dbus_service_property(_interface, signature="b",
960
def ApprovedByDefault_dbus_property(self, value=None):
961
if value is None: # get
962
return dbus.Boolean(self.approved_by_default)
963
self.approved_by_default = bool(value)
965
self.PropertyChanged(dbus.String("ApprovedByDefault"),
966
dbus.Boolean(value, variant_level=1))
968
# ApprovalDelay - property
969
@dbus_service_property(_interface, signature="t",
971
def ApprovalDelay_dbus_property(self, value=None):
972
if value is None: # get
973
return dbus.UInt64(self.approval_delay_milliseconds())
974
self.approval_delay = datetime.timedelta(0, 0, 0, value)
976
self.PropertyChanged(dbus.String("ApprovalDelay"),
977
dbus.UInt64(value, variant_level=1))
979
# ApprovalDuration - property
980
@dbus_service_property(_interface, signature="t",
982
def ApprovalDuration_dbus_property(self, value=None):
983
if value is None: # get
984
return dbus.UInt64(self._timedelta_to_milliseconds(
985
self.approval_duration))
986
self.approval_duration = datetime.timedelta(0, 0, 0, value)
988
self.PropertyChanged(dbus.String("ApprovalDuration"),
989
dbus.UInt64(value, variant_level=1))
992
@dbus_service_property(_interface, signature="s", access="read")
993
def Name_dbus_property(self):
994
return dbus.String(self.name)
996
# Fingerprint - property
997
@dbus_service_property(_interface, signature="s", access="read")
998
def Fingerprint_dbus_property(self):
999
return dbus.String(self.fingerprint)
1002
@dbus_service_property(_interface, signature="s",
1004
def Host_dbus_property(self, value=None):
1005
if value is None: # get
1006
return dbus.String(self.host)
1009
self.PropertyChanged(dbus.String("Host"),
1010
dbus.String(value, variant_level=1))
1012
# Created - property
1013
@dbus_service_property(_interface, signature="s", access="read")
1014
def Created_dbus_property(self):
1015
return dbus.String(self._datetime_to_dbus(self.created))
1017
# LastEnabled - property
1018
@dbus_service_property(_interface, signature="s", access="read")
1019
def LastEnabled_dbus_property(self):
1020
if self.last_enabled is None:
1021
return dbus.String("")
1022
return dbus.String(self._datetime_to_dbus(self.last_enabled))
1024
# Enabled - property
1025
@dbus_service_property(_interface, signature="b",
1027
def Enabled_dbus_property(self, value=None):
1028
if value is None: # get
1029
return dbus.Boolean(self.enabled)
1035
# LastCheckedOK - property
1036
@dbus_service_property(_interface, signature="s",
1038
def LastCheckedOK_dbus_property(self, value=None):
1039
if value is not None:
1042
if self.last_checked_ok is None:
1043
return dbus.String("")
1044
return dbus.String(self._datetime_to_dbus(self
1047
# LastApprovalRequest - property
1048
@dbus_service_property(_interface, signature="s", access="read")
1049
def LastApprovalRequest_dbus_property(self):
1050
if self.last_approval_request is None:
1051
return dbus.String("")
1052
return dbus.String(self.
1053
_datetime_to_dbus(self
1054
.last_approval_request))
1056
# Timeout - property
1057
@dbus_service_property(_interface, signature="t",
1059
def Timeout_dbus_property(self, value=None):
1060
if value is None: # get
1061
return dbus.UInt64(self.timeout_milliseconds())
1062
self.timeout = datetime.timedelta(0, 0, 0, value)
1064
self.PropertyChanged(dbus.String("Timeout"),
1065
dbus.UInt64(value, variant_level=1))
1066
if getattr(self, "disable_initiator_tag", None) is None:
1068
# Reschedule timeout
1069
gobject.source_remove(self.disable_initiator_tag)
1070
self.disable_initiator_tag = None
1071
time_to_die = (self.
1072
_timedelta_to_milliseconds((self
1077
if time_to_die <= 0:
1078
# The timeout has passed
1081
self.disable_initiator_tag = (gobject.timeout_add
1082
(time_to_die, self.disable))
1084
# Interval - property
1085
@dbus_service_property(_interface, signature="t",
1087
def Interval_dbus_property(self, value=None):
1088
if value is None: # get
1089
return dbus.UInt64(self.interval_milliseconds())
1090
self.interval = datetime.timedelta(0, 0, 0, value)
1092
self.PropertyChanged(dbus.String("Interval"),
1093
dbus.UInt64(value, variant_level=1))
1094
if getattr(self, "checker_initiator_tag", None) is None:
1096
# Reschedule checker run
1097
gobject.source_remove(self.checker_initiator_tag)
1098
self.checker_initiator_tag = (gobject.timeout_add
1099
(value, self.start_checker))
1100
self.start_checker() # Start one now, too
1102
# Checker - property
1103
@dbus_service_property(_interface, signature="s",
1105
def Checker_dbus_property(self, value=None):
1106
if value is None: # get
1107
return dbus.String(self.checker_command)
1108
self.checker_command = value
1110
self.PropertyChanged(dbus.String("Checker"),
1111
dbus.String(self.checker_command,
1114
# CheckerRunning - property
1115
@dbus_service_property(_interface, signature="b",
1117
def CheckerRunning_dbus_property(self, value=None):
1118
if value is None: # get
1119
return dbus.Boolean(self.checker is not None)
1121
self.start_checker()
1125
# ObjectPath - property
1126
@dbus_service_property(_interface, signature="o", access="read")
1127
def ObjectPath_dbus_property(self):
1128
return self.dbus_object_path # is already a dbus.ObjectPath
1131
@dbus_service_property(_interface, signature="ay",
1132
access="write", byte_arrays=True)
1133
def Secret_dbus_property(self, value):
1134
self.secret = str(value)
1139
class ProxyClient(object):
1140
def __init__(self, child_pipe, fpr, address):
1141
self._pipe = child_pipe
1142
self._pipe.send(('init', fpr, address))
1143
if not self._pipe.recv():
1146
def __getattribute__(self, name):
1147
if(name == '_pipe'):
1148
return super(ProxyClient, self).__getattribute__(name)
1149
self._pipe.send(('getattr', name))
1150
data = self._pipe.recv()
1151
if data[0] == 'data':
1153
if data[0] == 'function':
1154
def func(*args, **kwargs):
1155
self._pipe.send(('funcall', name, args, kwargs))
1156
return self._pipe.recv()[1]
1159
def __setattr__(self, name, value):
1160
if(name == '_pipe'):
1161
return super(ProxyClient, self).__setattr__(name, value)
1162
self._pipe.send(('setattr', name, value))
1165
class ClientHandler(socketserver.BaseRequestHandler, object):
705
class ClientHandler(SocketServer.BaseRequestHandler, object):
1166
706
"""A class to handle client connections.
1168
708
Instantiated once for each connection to handle it.
1169
709
Note: This will run in its own forked process."""
1171
711
def handle(self):
1172
with contextlib.closing(self.server.child_pipe) as child_pipe:
1173
logger.info("TCP connection from: %s",
1174
unicode(self.client_address))
1175
logger.debug("Pipe FD: %d",
1176
self.server.child_pipe.fileno())
712
logger.info(u"TCP connection from: %s",
713
unicode(self.client_address))
714
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
715
# Open IPC pipe to parent process
716
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
1178
717
session = (gnutls.connection
1179
718
.ClientSession(self.request,
1180
719
gnutls.connection
1181
720
.X509Credentials()))
722
line = self.request.makefile().readline()
723
logger.debug(u"Protocol version: %r", line)
725
if int(line.strip().split()[0]) > 1:
727
except (ValueError, IndexError, RuntimeError), error:
728
logger.error(u"Unknown protocol version: %s", error)
1183
731
# Note: gnutls.connection.X509Credentials is really a
1184
732
# generic GnuTLS certificate credentials object so long as
1185
733
# no X.509 keys are added to it. Therefore, we can use it
1186
734
# here despite using OpenPGP certificates.
1188
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
1189
# "+AES-256-CBC", "+SHA1",
1190
# "+COMP-NULL", "+CTYPE-OPENPGP",
736
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
737
# u"+AES-256-CBC", u"+SHA1",
738
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
1192
740
# Use a fallback default, since this MUST be set.
1193
741
priority = self.server.gnutls_priority
1194
742
if priority is None:
1196
744
(gnutls.library.functions
1197
745
.gnutls_priority_set_direct(session._c_object,
1198
746
priority, None))
1200
# Start communication using the Mandos protocol
1201
# Get protocol number
1202
line = self.request.makefile().readline()
1203
logger.debug("Protocol version: %r", line)
1205
if int(line.strip().split()[0]) > 1:
1207
except (ValueError, IndexError, RuntimeError), error:
1208
logger.error("Unknown protocol version: %s", error)
1211
# Start GnuTLS connection
1213
749
session.handshake()
1214
750
except gnutls.errors.GNUTLSError, error:
1215
logger.warning("Handshake failed: %s", error)
751
logger.warning(u"Handshake failed: %s", error)
1216
752
# Do not run session.bye() here: the session is not
1217
753
# established. Just abandon the request.
1219
logger.debug("Handshake succeeded")
1221
approval_required = False
755
logger.debug(u"Handshake succeeded")
1224
fpr = self.fingerprint(self.peer_certificate
1226
except (TypeError, gnutls.errors.GNUTLSError), error:
1227
logger.warning("Bad certificate: %s", error)
1229
logger.debug("Fingerprint: %s", fpr)
1232
client = ProxyClient(child_pipe, fpr,
1233
self.client_address)
1237
if client.approval_delay:
1238
delay = client.approval_delay
1239
client.approvals_pending += 1
1240
approval_required = True
1243
if not client.enabled:
1244
logger.warning("Client %s is disabled",
1246
if self.server.use_dbus:
1248
client.Rejected("Disabled")
1251
if client._approved or not client.approval_delay:
1252
#We are approved or approval is disabled
1254
elif client._approved is None:
1255
logger.info("Client %s needs approval",
1257
if self.server.use_dbus:
1259
client.NeedApproval(
1260
client.approval_delay_milliseconds(),
1261
client.approved_by_default)
1263
logger.warning("Client %s was not approved",
1265
if self.server.use_dbus:
1267
client.Rejected("Denied")
1270
#wait until timeout or approved
1271
#x = float(client._timedelta_to_milliseconds(delay))
1272
time = datetime.datetime.now()
1273
client.changedstate.acquire()
1274
client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1275
client.changedstate.release()
1276
time2 = datetime.datetime.now()
1277
if (time2 - time) >= delay:
1278
if not client.approved_by_default:
1279
logger.warning("Client %s timed out while"
1280
" waiting for approval",
1282
if self.server.use_dbus:
1284
client.Rejected("Approval timed out")
1289
delay -= time2 - time
1292
while sent_size < len(client.secret):
1294
sent = session.send(client.secret[sent_size:])
1295
except (gnutls.errors.GNUTLSError), error:
1296
logger.warning("gnutls send failed")
1298
logger.debug("Sent: %d, remaining: %d",
1299
sent, len(client.secret)
1300
- (sent_size + sent))
1303
logger.info("Sending secret to %s", client.name)
1304
# bump the timeout as if seen
1306
if self.server.use_dbus:
757
fpr = self.fingerprint(self.peer_certificate(session))
758
except (TypeError, gnutls.errors.GNUTLSError), error:
759
logger.warning(u"Bad certificate: %s", error)
762
logger.debug(u"Fingerprint: %s", fpr)
1311
if approval_required:
1312
client.approvals_pending -= 1
1315
except (gnutls.errors.GNUTLSError), error:
1316
logger.warning("GnuTLS bye failed")
764
for c in self.server.clients:
765
if c.fingerprint == fpr:
769
ipc.write(u"NOTFOUND %s\n" % fpr)
772
# Have to check if client.still_valid(), since it is
773
# possible that the client timed out while establishing
774
# the GnuTLS session.
775
if not client.still_valid():
776
ipc.write(u"INVALID %s\n" % client.name)
779
ipc.write(u"SENDING %s\n" % client.name)
781
while sent_size < len(client.secret):
782
sent = session.send(client.secret[sent_size:])
783
logger.debug(u"Sent: %d, remaining: %d",
784
sent, len(client.secret)
785
- (sent_size + sent))
1319
790
def peer_certificate(session):
1375
846
# Convert the buffer to a Python bytestring
1376
847
fpr = ctypes.string_at(buf, buf_len.value)
1377
848
# Convert the bytestring to hexadecimal notation
1378
hex_fpr = ''.join("%02X" % ord(char) for char in fpr)
849
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
1382
class MultiprocessingMixIn(object):
1383
"""Like socketserver.ThreadingMixIn, but with multiprocessing"""
1384
def sub_process_main(self, request, address):
1386
self.finish_request(request, address)
1388
self.handle_error(request, address)
1389
self.close_request(request)
1391
def process_request(self, request, address):
1392
"""Start a new process to process the request."""
1393
multiprocessing.Process(target = self.sub_process_main,
1394
args = (request, address)).start()
1396
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1397
""" adds a pipe to the MixIn """
853
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
854
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
856
Assumes a gobject.MainLoop event loop.
1398
858
def process_request(self, request, client_address):
1399
859
"""Overrides and wraps the original process_request().
1401
This function creates a new pipe in self.pipe
861
This function creates a new pipe in self.pipe
1403
parent_pipe, self.child_pipe = multiprocessing.Pipe()
1405
super(MultiprocessingMixInWithPipe,
863
self.pipe = os.pipe()
864
super(ForkingMixInWithPipe,
1406
865
self).process_request(request, client_address)
1407
self.child_pipe.close()
1408
self.add_pipe(parent_pipe)
1410
def add_pipe(self, parent_pipe):
866
os.close(self.pipe[1]) # close write end
867
# Call "handle_ipc" for both data and EOF events
868
gobject.io_add_watch(self.pipe[0],
869
gobject.IO_IN | gobject.IO_HUP,
871
def handle_ipc(source, condition):
1411
872
"""Dummy function; override as necessary"""
1412
raise NotImplementedError
1414
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1415
socketserver.TCPServer, object):
877
class IPv6_TCPServer(ForkingMixInWithPipe,
878
SocketServer.TCPServer, object):
1416
879
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
1419
882
enabled: Boolean; whether this server is activated yet
1420
883
interface: None or a network interface name (string)
1421
884
use_ipv6: Boolean; to use IPv6 or not
886
clients: Set() of Client objects
887
gnutls_priority GnuTLS priority string
888
use_dbus: Boolean; to emit D-Bus signals or not
1423
890
def __init__(self, server_address, RequestHandlerClass,
1424
interface=None, use_ipv6=True):
891
interface=None, use_ipv6=True, clients=None,
892
gnutls_priority=None, use_dbus=True):
1425
894
self.interface = interface
1427
896
self.address_family = socket.AF_INET6
1428
socketserver.TCPServer.__init__(self, server_address,
897
self.clients = clients
898
self.use_dbus = use_dbus
899
self.gnutls_priority = gnutls_priority
900
SocketServer.TCPServer.__init__(self, server_address,
1429
901
RequestHandlerClass)
1430
902
def server_bind(self):
1431
903
"""This overrides the normal server_bind() function
1432
904
to bind to an interface if one was specified, and also NOT to
1433
905
bind to an address or port if they were not specified."""
1434
906
if self.interface is not None:
1435
if SO_BINDTODEVICE is None:
1436
logger.error("SO_BINDTODEVICE does not exist;"
1437
" cannot bind to interface %s",
1441
self.socket.setsockopt(socket.SOL_SOCKET,
1445
except socket.error, error:
1446
if error[0] == errno.EPERM:
1447
logger.error("No permission to"
1448
" bind to interface %s",
1450
elif error[0] == errno.ENOPROTOOPT:
1451
logger.error("SO_BINDTODEVICE not available;"
1452
" cannot bind to interface %s",
908
self.socket.setsockopt(socket.SOL_SOCKET,
910
str(self.interface + u'\0'))
911
except socket.error, error:
912
if error[0] == errno.EPERM:
913
logger.error(u"No permission to"
914
u" bind to interface %s",
1456
918
# Only bind(2) the socket if we really need to.
1457
919
if self.server_address[0] or self.server_address[1]:
1458
920
if not self.server_address[0]:
1459
921
if self.address_family == socket.AF_INET6:
1460
any_address = "::" # in6addr_any
922
any_address = u"::" # in6addr_any
1462
924
any_address = socket.INADDR_ANY
1463
925
self.server_address = (any_address,
1525
954
for cond, name in
1526
955
condition_names.iteritems()
1527
956
if cond & condition)
1528
# error or the other end of multiprocessing.Pipe has closed
1529
if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1532
# Read a request from the child
1533
request = parent_pipe.recv()
1534
command = request[0]
1536
if command == 'init':
1538
address = request[2]
1540
for c in self.clients:
1541
if c.fingerprint == fpr:
1545
logger.warning("Client not found for fingerprint: %s, ad"
1546
"dress: %s", fpr, address)
1549
mandos_dbus_service.ClientNotFound(fpr, address[0])
1550
parent_pipe.send(False)
1553
gobject.io_add_watch(parent_pipe.fileno(),
1554
gobject.IO_IN | gobject.IO_HUP,
1555
functools.partial(self.handle_ipc,
1556
parent_pipe = parent_pipe,
1557
client_object = client))
1558
parent_pipe.send(True)
1559
# remove the old hook in favor of the new above hook on same fileno
1561
if command == 'funcall':
1562
funcname = request[1]
1566
parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1568
if command == 'getattr':
1569
attrname = request[1]
1570
if callable(client_object.__getattribute__(attrname)):
1571
parent_pipe.send(('function',))
1573
parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1575
if command == 'setattr':
1576
attrname = request[1]
1578
setattr(client_object, attrname, value)
957
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
960
# Turn the pipe file descriptor into a Python file object
961
if source not in file_objects:
962
file_objects[source] = os.fdopen(source, u"r", 1)
964
# Read a line from the file object
965
cmdline = file_objects[source].readline()
966
if not cmdline: # Empty line means end of file
968
file_objects[source].close()
969
del file_objects[source]
971
# Stop calling this function
974
logger.debug(u"IPC command: %r", cmdline)
976
# Parse and act on command
977
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
979
if cmd == u"NOTFOUND":
980
logger.warning(u"Client not found for fingerprint: %s",
984
mandos_dbus_service.ClientNotFound(args)
985
elif cmd == u"INVALID":
986
for client in self.clients:
987
if client.name == args:
988
logger.warning(u"Client %s is invalid", args)
994
logger.error(u"Unknown client %s is invalid", args)
995
elif cmd == u"SENDING":
996
for client in self.clients:
997
if client.name == args:
998
logger.info(u"Sending secret to %s", client.name)
1002
client.ReceivedSecret()
1005
logger.error(u"Sending secret to unknown client %s",
1008
logger.error(u"Unknown IPC command: %r", cmdline)
1010
# Keep calling this function
1583
1014
def string_to_delta(interval):
1584
1015
"""Parse a string and return a datetime.timedelta
1586
>>> string_to_delta('7d')
1017
>>> string_to_delta(u'7d')
1587
1018
datetime.timedelta(7)
1588
>>> string_to_delta('60s')
1019
>>> string_to_delta(u'60s')
1589
1020
datetime.timedelta(0, 60)
1590
>>> string_to_delta('60m')
1021
>>> string_to_delta(u'60m')
1591
1022
datetime.timedelta(0, 3600)
1592
>>> string_to_delta('24h')
1023
>>> string_to_delta(u'24h')
1593
1024
datetime.timedelta(1)
1594
>>> string_to_delta('1w')
1025
>>> string_to_delta(u'1w')
1595
1026
datetime.timedelta(7)
1596
>>> string_to_delta('5m 30s')
1027
>>> string_to_delta(u'5m 30s')
1597
1028
datetime.timedelta(0, 330)
1599
1030
timevalue = datetime.timedelta(0)
1673
##################################################################
1126
######################################################################
1674
1127
# Parsing of options, both command line and config file
1676
1129
parser = optparse.OptionParser(version = "%%prog %s" % version)
1677
parser.add_option("-i", "--interface", type="string",
1678
metavar="IF", help="Bind to interface IF")
1679
parser.add_option("-a", "--address", type="string",
1680
help="Address to listen for requests on")
1681
parser.add_option("-p", "--port", type="int",
1682
help="Port number to receive requests on")
1683
parser.add_option("--check", action="store_true",
1684
help="Run self-test")
1685
parser.add_option("--debug", action="store_true",
1686
help="Debug mode; run in foreground and log to"
1688
parser.add_option("--debuglevel", type="string", metavar="LEVEL",
1689
help="Debug level for stdout output")
1690
parser.add_option("--priority", type="string", help="GnuTLS"
1691
" priority string (see GnuTLS documentation)")
1692
parser.add_option("--servicename", type="string",
1693
metavar="NAME", help="Zeroconf service name")
1694
parser.add_option("--configdir", type="string",
1695
default="/etc/mandos", metavar="DIR",
1696
help="Directory to search for configuration"
1698
parser.add_option("--no-dbus", action="store_false",
1699
dest="use_dbus", help="Do not provide D-Bus"
1700
" system bus interface")
1701
parser.add_option("--no-ipv6", action="store_false",
1702
dest="use_ipv6", help="Do not use IPv6")
1130
parser.add_option("-i", u"--interface", type=u"string",
1131
metavar="IF", help=u"Bind to interface IF")
1132
parser.add_option("-a", u"--address", type=u"string",
1133
help=u"Address to listen for requests on")
1134
parser.add_option("-p", u"--port", type=u"int",
1135
help=u"Port number to receive requests on")
1136
parser.add_option("--check", action=u"store_true",
1137
help=u"Run self-test")
1138
parser.add_option("--debug", action=u"store_true",
1139
help=u"Debug mode; run in foreground and log to"
1141
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1142
u" priority string (see GnuTLS documentation)")
1143
parser.add_option("--servicename", type=u"string",
1144
metavar=u"NAME", help=u"Zeroconf service name")
1145
parser.add_option("--configdir", type=u"string",
1146
default=u"/etc/mandos", metavar=u"DIR",
1147
help=u"Directory to search for configuration"
1149
parser.add_option("--no-dbus", action=u"store_false",
1150
dest=u"use_dbus", help=u"Do not provide D-Bus"
1151
u" system bus interface")
1152
parser.add_option("--no-ipv6", action=u"store_false",
1153
dest=u"use_ipv6", help=u"Do not use IPv6")
1703
1154
options = parser.parse_args()[0]
1705
1156
if options.check:
1754
1204
##################################################################
1756
1206
# For convenience
1757
debug = server_settings["debug"]
1758
debuglevel = server_settings["debuglevel"]
1759
use_dbus = server_settings["use_dbus"]
1760
use_ipv6 = server_settings["use_ipv6"]
1762
if server_settings["servicename"] != "Mandos":
1207
debug = server_settings[u"debug"]
1208
use_dbus = server_settings[u"use_dbus"]
1209
use_ipv6 = server_settings[u"use_ipv6"]
1212
syslogger.setLevel(logging.WARNING)
1213
console.setLevel(logging.WARNING)
1215
if server_settings[u"servicename"] != u"Mandos":
1763
1216
syslogger.setFormatter(logging.Formatter
1764
('Mandos (%s) [%%(process)d]:'
1765
' %%(levelname)s: %%(message)s'
1766
% server_settings["servicename"]))
1217
(u'Mandos (%s) [%%(process)d]:'
1218
u' %%(levelname)s: %%(message)s'
1219
% server_settings[u"servicename"]))
1768
1221
# Parse config file with clients
1769
client_defaults = { "timeout": "1h",
1771
"checker": "fping -q -- %%(host)s",
1773
"approval_delay": "0s",
1774
"approval_duration": "1s",
1222
client_defaults = { u"timeout": u"1h",
1224
u"checker": u"fping -q -- %%(host)s",
1776
client_config = configparser.SafeConfigParser(client_defaults)
1777
client_config.read(os.path.join(server_settings["configdir"],
1227
client_config = ConfigParser.SafeConfigParser(client_defaults)
1228
client_config.read(os.path.join(server_settings[u"configdir"],
1780
1231
global mandos_dbus_service
1781
1232
mandos_dbus_service = None
1783
tcp_server = MandosServer((server_settings["address"],
1784
server_settings["port"]),
1786
interface=(server_settings["interface"]
1790
server_settings["priority"],
1793
pidfilename = "/var/run/mandos.pid"
1795
pidfile = open(pidfilename, "w")
1797
logger.error("Could not open file %r", pidfilename)
1235
tcp_server = IPv6_TCPServer((server_settings[u"address"],
1236
server_settings[u"port"]),
1239
server_settings[u"interface"],
1243
server_settings[u"priority"],
1245
pidfilename = u"/var/run/mandos.pid"
1247
pidfile = open(pidfilename, u"w")
1249
logger.error(u"Could not open file %r", pidfilename)
1800
uid = pwd.getpwnam("_mandos").pw_uid
1801
gid = pwd.getpwnam("_mandos").pw_gid
1252
uid = pwd.getpwnam(u"_mandos").pw_uid
1253
gid = pwd.getpwnam(u"_mandos").pw_gid
1802
1254
except KeyError:
1804
uid = pwd.getpwnam("mandos").pw_uid
1805
gid = pwd.getpwnam("mandos").pw_gid
1256
uid = pwd.getpwnam(u"mandos").pw_uid
1257
gid = pwd.getpwnam(u"mandos").pw_gid
1806
1258
except KeyError:
1808
uid = pwd.getpwnam("nobody").pw_uid
1809
gid = pwd.getpwnam("nobody").pw_gid
1260
uid = pwd.getpwnam(u"nobody").pw_uid
1261
gid = pwd.getpwnam(u"nobody").pw_gid
1810
1262
except KeyError:
1817
1269
if error[0] != errno.EPERM:
1820
if not debug and not debuglevel:
1821
syslogger.setLevel(logging.WARNING)
1822
console.setLevel(logging.WARNING)
1824
level = getattr(logging, debuglevel.upper())
1825
syslogger.setLevel(level)
1826
console.setLevel(level)
1272
# Enable all possible GnuTLS debugging
1829
# Enable all possible GnuTLS debugging
1831
1274
# "Use a log level over 10 to enable all debugging options."
1832
1275
# - GnuTLS manual
1833
1276
gnutls.library.functions.gnutls_global_set_log_level(11)
1835
1278
@gnutls.library.types.gnutls_log_func
1836
1279
def debug_gnutls(level, string):
1837
logger.debug("GnuTLS: %s", string[:-1])
1280
logger.debug(u"GnuTLS: %s", string[:-1])
1839
1282
(gnutls.library.functions
1840
1283
.gnutls_global_set_log_function(debug_gnutls))
1842
# Redirect stdin so all checkers get /dev/null
1843
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1844
os.dup2(null, sys.stdin.fileno())
1848
# No console logging
1849
logger.removeHandler(console)
1851
# Need to fork before connecting to D-Bus
1853
# Close all input and output, do double fork, etc.
1286
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1287
service = AvahiService(name = server_settings[u"servicename"],
1288
servicetype = u"_mandos._tcp",
1289
protocol = protocol)
1290
if server_settings["interface"]:
1291
service.interface = (if_nametoindex
1292
(str(server_settings[u"interface"])))
1856
1294
global main_loop
1857
1297
# From the Avahi example code
1858
1298
DBusGMainLoop(set_as_default=True )
1859
1299
main_loop = gobject.MainLoop()
1860
1300
bus = dbus.SystemBus()
1301
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1302
avahi.DBUS_PATH_SERVER),
1303
avahi.DBUS_INTERFACE_SERVER)
1861
1304
# End of Avahi example code
1864
bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1865
bus, do_not_queue=True)
1866
except dbus.exceptions.NameExistsException, e:
1867
logger.error(unicode(e) + ", disabling D-Bus")
1869
server_settings["use_dbus"] = False
1870
tcp_server.use_dbus = False
1871
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1872
service = AvahiService(name = server_settings["servicename"],
1873
servicetype = "_mandos._tcp",
1874
protocol = protocol, bus = bus)
1875
if server_settings["interface"]:
1876
service.interface = (if_nametoindex
1877
(str(server_settings["interface"])))
1879
global multiprocessing_manager
1880
multiprocessing_manager = multiprocessing.Manager()
1306
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1882
1308
client_class = Client
1884
client_class = functools.partial(ClientDBus, bus = bus)
1885
def client_config_items(config, section):
1886
special_settings = {
1887
"approved_by_default":
1888
lambda: config.getboolean(section,
1889
"approved_by_default"),
1891
for name, value in config.items(section):
1893
yield (name, special_settings[name]())
1897
tcp_server.clients.update(set(
1310
client_class = ClientDBus
1898
1312
client_class(name = section,
1899
config= dict(client_config_items(
1900
client_config, section)))
1313
config= dict(client_config.items(section)))
1901
1314
for section in client_config.sections()))
1902
if not tcp_server.clients:
1903
logger.warning("No clients defined")
1316
logger.warning(u"No clients defined")
1319
# Redirect stdin so all checkers get /dev/null
1320
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1321
os.dup2(null, sys.stdin.fileno())
1325
# No console logging
1326
logger.removeHandler(console)
1327
# Close all input and output, do double fork, etc.
1331
with closing(pidfile):
1333
pidfile.write(str(pid) + "\n")
1336
logger.error(u"Could not write to file %r with PID %d",
1339
# "pidfile" was never created
1344
"Cleanup function; run on exit"
1346
# From the Avahi example code
1347
if not group is None:
1350
# End of Avahi example code
1353
client = clients.pop()
1354
client.disable_hook = None
1357
atexit.register(cleanup)
1909
pidfile.write(str(pid) + "\n".encode("utf-8"))
1912
logger.error("Could not write to file %r with PID %d",
1915
# "pidfile" was never created
1919
1360
signal.signal(signal.SIGINT, signal.SIG_IGN)
1921
1361
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1922
1362
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1925
1365
class MandosDBusService(dbus.service.Object):
1926
1366
"""A D-Bus proxy object"""
1927
1367
def __init__(self):
1928
dbus.service.Object.__init__(self, bus, "/")
1929
_interface = "se.bsnet.fukt.Mandos"
1931
@dbus.service.signal(_interface, signature="o")
1932
def ClientAdded(self, objpath):
1936
@dbus.service.signal(_interface, signature="ss")
1937
def ClientNotFound(self, fingerprint, address):
1941
@dbus.service.signal(_interface, signature="os")
1368
dbus.service.Object.__init__(self, bus, u"/")
1369
_interface = u"se.bsnet.fukt.Mandos"
1371
@dbus.service.signal(_interface, signature=u"oa{sv}")
1372
def ClientAdded(self, objpath, properties):
1376
@dbus.service.signal(_interface, signature=u"s")
1377
def ClientNotFound(self, fingerprint):
1381
@dbus.service.signal(_interface, signature=u"os")
1942
1382
def ClientRemoved(self, objpath, name):
1946
@dbus.service.method(_interface, out_signature="ao")
1386
@dbus.service.method(_interface, out_signature=u"ao")
1947
1387
def GetAllClients(self):
1949
return dbus.Array(c.dbus_object_path
1950
for c in tcp_server.clients)
1389
return dbus.Array(c.dbus_object_path for c in clients)
1952
1391
@dbus.service.method(_interface,
1953
out_signature="a{oa{sv}}")
1392
out_signature=u"a{oa{sv}}")
1954
1393
def GetAllClientsWithProperties(self):
1956
1395
return dbus.Dictionary(
1957
((c.dbus_object_path, c.GetAll(""))
1958
for c in tcp_server.clients),
1396
((c.dbus_object_path, c.GetAllProperties())
1398
signature=u"oa{sv}")
1961
@dbus.service.method(_interface, in_signature="o")
1400
@dbus.service.method(_interface, in_signature=u"o")
1962
1401
def RemoveClient(self, object_path):
1964
for c in tcp_server.clients:
1965
1404
if c.dbus_object_path == object_path:
1966
tcp_server.clients.remove(c)
1967
1406
c.remove_from_connection()
1968
1407
# Don't signal anything except ClientRemoved
1969
c.disable(quiet=True)
1408
c.disable(signal=False)
1970
1409
# Emit D-Bus signal
1971
1410
self.ClientRemoved(object_path, c.name)
1973
raise KeyError(object_path)
1977
1416
mandos_dbus_service = MandosDBusService()
1980
"Cleanup function; run on exit"
1983
while tcp_server.clients:
1984
client = tcp_server.clients.pop()
1986
client.remove_from_connection()
1987
client.disable_hook = None
1988
# Don't signal anything except ClientRemoved
1989
client.disable(quiet=True)
1992
mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1995
atexit.register(cleanup)
1997
for client in tcp_server.clients:
1418
for client in clients:
1999
1420
# Emit D-Bus signal
2000
mandos_dbus_service.ClientAdded(client.dbus_object_path)
1421
mandos_dbus_service.ClientAdded(client.dbus_object_path,
1422
client.GetAllProperties())
2001
1423
client.enable()
2003
1425
tcp_server.enable()