133
152
u" after %i retries, exiting.",
134
153
self.rename_count)
135
154
raise AvahiServiceError(u"Too many renames")
136
self.name = server.GetAlternativeServiceName(self.name)
155
self.name = self.server.GetAlternativeServiceName(self.name)
137
156
logger.info(u"Changing Zeroconf service name to %r ...",
139
158
syslogger.setFormatter(logging.Formatter
140
('Mandos (%s) [%%(process)d]:'
141
' %%(levelname)s: %%(message)s'
159
(u'Mandos (%s) [%%(process)d]:'
160
u' %%(levelname)s: %%(message)s'
145
164
self.rename_count += 1
146
165
def remove(self):
147
166
"""Derived from the Avahi example code"""
148
if group is not None:
167
if self.group is not None:
151
170
"""Derived from the Avahi example code"""
154
group = dbus.Interface(bus.get_object
156
server.EntryGroupNew()),
157
avahi.DBUS_INTERFACE_ENTRY_GROUP)
158
group.connect_to_signal('StateChanged',
159
entry_group_state_changed)
171
if self.group is None:
172
self.group = dbus.Interface(
173
self.bus.get_object(avahi.DBUS_NAME,
174
self.server.EntryGroupNew()),
175
avahi.DBUS_INTERFACE_ENTRY_GROUP)
176
self.group.connect_to_signal('StateChanged',
177
self.entry_group_state_changed)
160
178
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
161
service.name, service.type)
163
self.interface, # interface
164
self.protocol, # protocol
165
dbus.UInt32(0), # flags
166
self.name, self.type,
167
self.domain, self.host,
168
dbus.UInt16(self.port),
169
avahi.string_array_to_txt_array(self.TXT))
172
# From the Avahi example code:
173
group = None # our entry group
174
# End of Avahi example code
177
def _datetime_to_dbus(dt, variant_level=0):
178
"""Convert a UTC datetime.datetime() to a D-Bus type."""
179
return dbus.String(dt.isoformat(), variant_level=variant_level)
179
self.name, self.type)
180
self.group.AddService(
183
dbus.UInt32(0), # flags
184
self.name, self.type,
185
self.domain, self.host,
186
dbus.UInt16(self.port),
187
avahi.string_array_to_txt_array(self.TXT))
189
def entry_group_state_changed(self, state, error):
190
"""Derived from the Avahi example code"""
191
logger.debug(u"Avahi state change: %i", state)
193
if state == avahi.ENTRY_GROUP_ESTABLISHED:
194
logger.debug(u"Zeroconf service established.")
195
elif state == avahi.ENTRY_GROUP_COLLISION:
196
logger.warning(u"Zeroconf service name collision.")
198
elif state == avahi.ENTRY_GROUP_FAILURE:
199
logger.critical(u"Avahi: Error in group state changed %s",
201
raise AvahiGroupError(u"State changed: %s"
204
"""Derived from the Avahi example code"""
205
if self.group is not None:
208
def server_state_changed(self, state):
209
"""Derived from the Avahi example code"""
210
if state == avahi.SERVER_COLLISION:
211
logger.error(u"Zeroconf server name collision")
213
elif state == avahi.SERVER_RUNNING:
216
"""Derived from the Avahi example code"""
217
if self.server is None:
218
self.server = dbus.Interface(
219
self.bus.get_object(avahi.DBUS_NAME,
220
avahi.DBUS_PATH_SERVER),
221
avahi.DBUS_INTERFACE_SERVER)
222
self.server.connect_to_signal(u"StateChanged",
223
self.server_state_changed)
224
self.server_state_changed(self.server.GetState())
182
227
class Client(object):
183
228
"""A representation of a client host served by this server.
185
231
name: string; from the config file, used in log messages and
186
232
D-Bus identifiers
208
254
instance %(name)s can be used in the command.
209
255
current_checker_command: string; current running checker_command
259
def _datetime_to_milliseconds(dt):
260
"Convert a datetime.datetime() to milliseconds"
261
return ((dt.days * 24 * 60 * 60 * 1000)
262
+ (dt.seconds * 1000)
263
+ (dt.microseconds // 1000))
211
265
def timeout_milliseconds(self):
212
266
"Return the 'timeout' attribute in milliseconds"
213
return ((self.timeout.days * 24 * 60 * 60 * 1000)
214
+ (self.timeout.seconds * 1000)
215
+ (self.timeout.microseconds // 1000))
267
return self._datetime_to_milliseconds(self.timeout)
217
269
def interval_milliseconds(self):
218
270
"Return the 'interval' attribute in milliseconds"
219
return ((self.interval.days * 24 * 60 * 60 * 1000)
220
+ (self.interval.seconds * 1000)
221
+ (self.interval.microseconds // 1000))
271
return self._datetime_to_milliseconds(self.interval)
223
273
def __init__(self, name = None, disable_hook=None, config=None):
224
274
"""Note: the 'checker' key in 'config' sets the
231
281
# Uppercase and remove spaces from fingerprint for later
232
282
# comparison purposes with return value from the fingerprint()
234
self.fingerprint = (config["fingerprint"].upper()
284
self.fingerprint = (config[u"fingerprint"].upper()
235
285
.replace(u" ", u""))
236
286
logger.debug(u" Fingerprint: %s", self.fingerprint)
237
if "secret" in config:
238
self.secret = config["secret"].decode(u"base64")
239
elif "secfile" in config:
287
if u"secret" in config:
288
self.secret = config[u"secret"].decode(u"base64")
289
elif u"secfile" in config:
240
290
with closing(open(os.path.expanduser
241
291
(os.path.expandvars
242
(config["secfile"])))) as secfile:
292
(config[u"secfile"])))) as secfile:
243
293
self.secret = secfile.read()
245
295
raise TypeError(u"No secret or secfile for client %s"
247
self.host = config.get("host", "")
297
self.host = config.get(u"host", u"")
248
298
self.created = datetime.datetime.utcnow()
249
299
self.enabled = False
250
300
self.last_enabled = None
251
301
self.last_checked_ok = None
252
self.timeout = string_to_delta(config["timeout"])
253
self.interval = string_to_delta(config["interval"])
302
self.timeout = string_to_delta(config[u"timeout"])
303
self.interval = string_to_delta(config[u"interval"])
254
304
self.disable_hook = disable_hook
255
305
self.checker = None
256
306
self.checker_initiator_tag = None
257
307
self.disable_initiator_tag = None
258
308
self.checker_callback_tag = None
259
self.checker_command = config["checker"]
309
self.checker_command = config[u"checker"]
260
310
self.current_checker_command = None
261
311
self.last_connect = None
263
313
def enable(self):
264
314
"""Start this client's checker and timeout hooks"""
315
if getattr(self, u"enabled", False):
265
318
self.last_enabled = datetime.datetime.utcnow()
266
319
# Schedule a new checker to be started an 'interval' from now,
267
320
# and every interval from then on.
422
480
class ClientDBus(Client, dbus.service.Object):
423
481
"""A Client class using D-Bus
425
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
484
dbus_object_path: dbus.ObjectPath
485
bus: dbus.SystemBus()
427
487
# dbus.service.Object doesn't use super(), so we can't either.
429
def __init__(self, *args, **kwargs):
489
def __init__(self, bus = None, *args, **kwargs):
430
491
Client.__init__(self, *args, **kwargs)
431
492
# Only now, when this client is initialized, can it show up on
433
494
self.dbus_object_path = (dbus.ObjectPath
435
+ self.name.replace(".", "_")))
436
dbus.service.Object.__init__(self, bus,
496
+ self.name.replace(u".", u"_")))
497
dbus.service.Object.__init__(self, self.bus,
437
498
self.dbus_object_path)
501
def _datetime_to_dbus(dt, variant_level=0):
502
"""Convert a UTC datetime.datetime() to a D-Bus type."""
503
return dbus.String(dt.isoformat(),
504
variant_level=variant_level)
438
506
def enable(self):
439
oldstate = getattr(self, "enabled", False)
507
oldstate = getattr(self, u"enabled", False)
440
508
r = Client.enable(self)
441
509
if oldstate != self.enabled:
442
510
# Emit D-Bus signals
443
511
self.PropertyChanged(dbus.String(u"enabled"),
444
512
dbus.Boolean(True, variant_level=1))
445
self.PropertyChanged(dbus.String(u"last_enabled"),
446
(_datetime_to_dbus(self.last_enabled,
513
self.PropertyChanged(
514
dbus.String(u"last_enabled"),
515
self._datetime_to_dbus(self.last_enabled,
450
519
def disable(self, signal = True):
451
oldstate = getattr(self, "enabled", False)
520
oldstate = getattr(self, u"enabled", False)
452
521
r = Client.disable(self)
453
522
if signal and oldstate != self.enabled:
454
523
# Emit D-Bus signal
509
578
# Emit D-Bus signal
510
579
self.CheckerStarted(self.current_checker_command)
511
580
self.PropertyChanged(
512
dbus.String("checker_running"),
581
dbus.String(u"checker_running"),
513
582
dbus.Boolean(True, variant_level=1))
516
585
def stop_checker(self, *args, **kwargs):
517
old_checker = getattr(self, "checker", None)
586
old_checker = getattr(self, u"checker", None)
518
587
r = Client.stop_checker(self, *args, **kwargs)
519
588
if (old_checker is not None
520
and getattr(self, "checker", None) is None):
589
and getattr(self, u"checker", None) is None):
521
590
self.PropertyChanged(dbus.String(u"checker_running"),
522
591
dbus.Boolean(False, variant_level=1))
526
595
_interface = u"se.bsnet.fukt.Mandos.Client"
528
597
# CheckedOK - method
529
CheckedOK = dbus.service.method(_interface)(checked_ok)
530
CheckedOK.__name__ = "CheckedOK"
598
@dbus.service.method(_interface)
600
return self.checked_ok()
532
602
# CheckerCompleted - signal
533
@dbus.service.signal(_interface, signature="nxs")
603
@dbus.service.signal(_interface, signature=u"nxs")
534
604
def CheckerCompleted(self, exitcode, waitstatus, command):
538
608
# CheckerStarted - signal
539
@dbus.service.signal(_interface, signature="s")
609
@dbus.service.signal(_interface, signature=u"s")
540
610
def CheckerStarted(self, command):
544
614
# GetAllProperties - method
545
@dbus.service.method(_interface, out_signature="a{sv}")
615
@dbus.service.method(_interface, out_signature=u"a{sv}")
546
616
def GetAllProperties(self):
548
618
return dbus.Dictionary({
619
dbus.String(u"name"):
550
620
dbus.String(self.name, variant_level=1),
551
dbus.String("fingerprint"):
621
dbus.String(u"fingerprint"):
552
622
dbus.String(self.fingerprint, variant_level=1),
623
dbus.String(u"host"):
554
624
dbus.String(self.host, variant_level=1),
555
dbus.String("created"):
556
_datetime_to_dbus(self.created, variant_level=1),
557
dbus.String("last_enabled"):
558
(_datetime_to_dbus(self.last_enabled,
625
dbus.String(u"created"):
626
self._datetime_to_dbus(self.created,
628
dbus.String(u"last_enabled"):
629
(self._datetime_to_dbus(self.last_enabled,
560
631
if self.last_enabled is not None
561
632
else dbus.Boolean(False, variant_level=1)),
562
dbus.String("enabled"):
633
dbus.String(u"enabled"):
563
634
dbus.Boolean(self.enabled, variant_level=1),
564
dbus.String("last_checked_ok"):
565
(_datetime_to_dbus(self.last_checked_ok,
635
dbus.String(u"last_checked_ok"):
636
(self._datetime_to_dbus(self.last_checked_ok,
567
638
if self.last_checked_ok is not None
568
639
else dbus.Boolean (False, variant_level=1)),
569
dbus.String("timeout"):
640
dbus.String(u"timeout"):
570
641
dbus.UInt64(self.timeout_milliseconds(),
571
642
variant_level=1),
572
dbus.String("interval"):
643
dbus.String(u"interval"):
573
644
dbus.UInt64(self.interval_milliseconds(),
574
645
variant_level=1),
575
dbus.String("checker"):
646
dbus.String(u"checker"):
576
647
dbus.String(self.checker_command,
577
648
variant_level=1),
578
dbus.String("checker_running"):
649
dbus.String(u"checker_running"):
579
650
dbus.Boolean(self.checker is not None,
580
651
variant_level=1),
581
dbus.String("object_path"):
652
dbus.String(u"object_path"):
582
653
dbus.ObjectPath(self.dbus_object_path,
586
657
# IsStillValid - method
587
@dbus.service.method(_interface, out_signature="b")
658
@dbus.service.method(_interface, out_signature=u"b")
588
659
def IsStillValid(self):
589
660
return self.still_valid()
591
662
# PropertyChanged - signal
592
@dbus.service.signal(_interface, signature="sv")
663
@dbus.service.signal(_interface, signature=u"sv")
593
664
def PropertyChanged(self, property, value):
703
778
# no X.509 keys are added to it. Therefore, we can use it
704
779
# here despite using OpenPGP certificates.
706
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
707
# "+AES-256-CBC", "+SHA1",
708
# "+COMP-NULL", "+CTYPE-OPENPGP",
781
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
782
# u"+AES-256-CBC", u"+SHA1",
783
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
710
785
# Use a fallback default, since this MUST be set.
711
priority = self.server.settings.get("priority", "NORMAL")
786
priority = self.server.gnutls_priority
712
789
(gnutls.library.functions
713
790
.gnutls_priority_set_direct(session._c_object,
821
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
822
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
823
Assumes a gobject.MainLoop event loop.
898
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
899
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
825
900
def process_request(self, request, client_address):
826
"""This overrides and wraps the original process_request().
901
"""Overrides and wraps the original process_request().
827
903
This function creates a new pipe in self.pipe
829
905
self.pipe = os.pipe()
830
906
super(ForkingMixInWithPipe,
831
907
self).process_request(request, client_address)
832
908
os.close(self.pipe[1]) # close write end
833
# Call "handle_ipc" for both data and EOF events
834
gobject.io_add_watch(self.pipe[0],
835
gobject.IO_IN | gobject.IO_HUP,
837
def handle_ipc(source, condition):
909
self.add_pipe(self.pipe[0])
910
def add_pipe(self, pipe):
838
911
"""Dummy function; override as necessary"""
843
915
class IPv6_TCPServer(ForkingMixInWithPipe,
844
SocketServer.TCPServer, object):
916
socketserver.TCPServer, object):
845
917
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
847
settings: Server settings
848
clients: Set() of Client objects
849
920
enabled: Boolean; whether this server is activated yet
921
interface: None or a network interface name (string)
922
use_ipv6: Boolean; to use IPv6 or not
851
address_family = socket.AF_INET6
852
def __init__(self, *args, **kwargs):
853
if "settings" in kwargs:
854
self.settings = kwargs["settings"]
855
del kwargs["settings"]
856
if "clients" in kwargs:
857
self.clients = kwargs["clients"]
858
del kwargs["clients"]
859
if "use_ipv6" in kwargs:
860
if not kwargs["use_ipv6"]:
861
self.address_family = socket.AF_INET
862
del kwargs["use_ipv6"]
864
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
924
def __init__(self, server_address, RequestHandlerClass,
925
interface=None, use_ipv6=True):
926
self.interface = interface
928
self.address_family = socket.AF_INET6
929
socketserver.TCPServer.__init__(self, server_address,
865
931
def server_bind(self):
866
932
"""This overrides the normal server_bind() function
867
933
to bind to an interface if one was specified, and also NOT to
868
934
bind to an address or port if they were not specified."""
869
if self.settings["interface"]:
870
# 25 is from /usr/include/asm-i486/socket.h
871
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
873
self.socket.setsockopt(socket.SOL_SOCKET,
875
self.settings["interface"])
876
except socket.error, error:
877
if error[0] == errno.EPERM:
878
logger.error(u"No permission to"
879
u" bind to interface %s",
880
self.settings["interface"])
935
if self.interface is not None:
936
if SO_BINDTODEVICE is None:
937
logger.error(u"SO_BINDTODEVICE does not exist;"
938
u" cannot bind to interface %s",
942
self.socket.setsockopt(socket.SOL_SOCKET,
946
except socket.error, error:
947
if error[0] == errno.EPERM:
948
logger.error(u"No permission to"
949
u" bind to interface %s",
951
elif error[0] == errno.ENOPROTOOPT:
952
logger.error(u"SO_BINDTODEVICE not available;"
953
u" cannot bind to interface %s",
883
957
# Only bind(2) the socket if we really need to.
884
958
if self.server_address[0] or self.server_address[1]:
885
959
if not self.server_address[0]:
886
960
if self.address_family == socket.AF_INET6:
887
any_address = "::" # in6addr_any
961
any_address = u"::" # in6addr_any
889
963
any_address = socket.INADDR_ANY
890
964
self.server_address = (any_address,
892
966
elif not self.server_address[1]:
893
967
self.server_address = (self.server_address[0],
895
# if self.settings["interface"]:
896
970
# self.server_address = (self.server_address[0],
902
return super(IPv6_TCPServer, self).server_bind()
975
return socketserver.TCPServer.server_bind(self)
978
class MandosServer(IPv6_TCPServer):
982
clients: set of Client objects
983
gnutls_priority GnuTLS priority string
984
use_dbus: Boolean; to emit D-Bus signals or not
985
clients: set of Client objects
986
gnutls_priority GnuTLS priority string
987
use_dbus: Boolean; to emit D-Bus signals or not
989
Assumes a gobject.MainLoop event loop.
991
def __init__(self, server_address, RequestHandlerClass,
992
interface=None, use_ipv6=True, clients=None,
993
gnutls_priority=None, use_dbus=True):
995
self.clients = clients
996
if self.clients is None:
998
self.use_dbus = use_dbus
999
self.gnutls_priority = gnutls_priority
1000
IPv6_TCPServer.__init__(self, server_address,
1001
RequestHandlerClass,
1002
interface = interface,
1003
use_ipv6 = use_ipv6)
903
1004
def server_activate(self):
904
1005
if self.enabled:
905
return super(IPv6_TCPServer, self).server_activate()
1006
return socketserver.TCPServer.server_activate(self)
906
1007
def enable(self):
907
1008
self.enabled = True
1009
def add_pipe(self, pipe):
1010
# Call "handle_ipc" for both data and EOF events
1011
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
908
1013
def handle_ipc(self, source, condition, file_objects={}):
909
1014
condition_names = {
910
gobject.IO_IN: "IN", # There is data to read.
911
gobject.IO_OUT: "OUT", # Data can be written (without
913
gobject.IO_PRI: "PRI", # There is urgent data to read.
914
gobject.IO_ERR: "ERR", # Error condition.
915
gobject.IO_HUP: "HUP" # Hung up (the connection has been
916
# broken, usually for pipes and
1015
gobject.IO_IN: u"IN", # There is data to read.
1016
gobject.IO_OUT: u"OUT", # Data can be written (without
1018
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1019
gobject.IO_ERR: u"ERR", # Error condition.
1020
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1021
# broken, usually for pipes and
919
1024
conditions_string = ' | '.join(name
920
1025
for cond, name in
921
1026
condition_names.iteritems()
922
1027
if cond & condition)
923
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1028
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
924
1029
conditions_string)
926
1031
# Turn the pipe file descriptor into a Python file object
927
1032
if source not in file_objects:
928
file_objects[source] = os.fdopen(source, "r", 1)
1033
file_objects[source] = os.fdopen(source, u"r", 1)
930
1035
# Read a line from the file object
931
1036
cmdline = file_objects[source].readline()
937
1042
# Stop calling this function
940
logger.debug("IPC command: %r", cmdline)
1045
logger.debug(u"IPC command: %r", cmdline)
942
1047
# Parse and act on command
943
cmd, args = cmdline.rstrip("\r\n").split(None, 1)
1048
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
945
if cmd == "NOTFOUND":
1050
if cmd == u"NOTFOUND":
946
1051
logger.warning(u"Client not found for fingerprint: %s",
948
if self.settings["use_dbus"]:
949
1054
# Emit D-Bus signal
950
1055
mandos_dbus_service.ClientNotFound(args)
951
elif cmd == "INVALID":
1056
elif cmd == u"INVALID":
952
1057
for client in self.clients:
953
1058
if client.name == args:
954
1059
logger.warning(u"Client %s is invalid", args)
955
if self.settings["use_dbus"]:
956
1061
# Emit D-Bus signal
957
1062
client.Rejected()
960
1065
logger.error(u"Unknown client %s is invalid", args)
961
elif cmd == "SENDING":
1066
elif cmd == u"SENDING":
962
1067
for client in self.clients:
963
1068
if client.name == args:
964
1069
logger.info(u"Sending secret to %s", client.name)
965
1070
client.checked_ok()
966
if self.settings["use_dbus"]:
967
1072
# Emit D-Bus signal
968
1073
client.ReceivedSecret()
1016
1121
return timevalue
1019
def server_state_changed(state):
1020
"""Derived from the Avahi example code"""
1021
if state == avahi.SERVER_COLLISION:
1022
logger.error(u"Zeroconf server name collision")
1024
elif state == avahi.SERVER_RUNNING:
1028
def entry_group_state_changed(state, error):
1029
"""Derived from the Avahi example code"""
1030
logger.debug(u"Avahi state change: %i", state)
1032
if state == avahi.ENTRY_GROUP_ESTABLISHED:
1033
logger.debug(u"Zeroconf service established.")
1034
elif state == avahi.ENTRY_GROUP_COLLISION:
1035
logger.warning(u"Zeroconf service name collision.")
1037
elif state == avahi.ENTRY_GROUP_FAILURE:
1038
logger.critical(u"Avahi: Error in group state changed %s",
1040
raise AvahiGroupError(u"State changed: %s" % unicode(error))
1042
1124
def if_nametoindex(interface):
1043
"""Call the C function if_nametoindex(), or equivalent"""
1125
"""Call the C function if_nametoindex(), or equivalent
1127
Note: This function cannot accept a unicode string."""
1044
1128
global if_nametoindex
1046
1130
if_nametoindex = (ctypes.cdll.LoadLibrary
1047
(ctypes.util.find_library("c"))
1131
(ctypes.util.find_library(u"c"))
1048
1132
.if_nametoindex)
1049
1133
except (OSError, AttributeError):
1050
if "struct" not in sys.modules:
1052
if "fcntl" not in sys.modules:
1134
logger.warning(u"Doing if_nametoindex the hard way")
1054
1135
def if_nametoindex(interface):
1055
1136
"Get an interface index the hard way, i.e. using fcntl()"
1056
1137
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1057
1138
with closing(socket.socket()) as s:
1058
1139
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1059
struct.pack("16s16x", interface))
1060
interface_index = struct.unpack("I", ifreq[16:20])[0]
1140
struct.pack(str(u"16s16x"),
1142
interface_index = struct.unpack(str(u"I"),
1061
1144
return interface_index
1062
1145
return if_nametoindex(interface)
1065
1148
def daemon(nochdir = False, noclose = False):
1066
1149
"""See daemon(3). Standard BSD Unix function.
1067
1151
This should really exist as os.daemon, but it doesn't (yet)."""
1071
1155
if not nochdir:
1075
1159
if not noclose:
1091
1175
# Parsing of options, both command line and config file
1093
1177
parser = optparse.OptionParser(version = "%%prog %s" % version)
1094
parser.add_option("-i", "--interface", type="string",
1095
metavar="IF", help="Bind to interface IF")
1096
parser.add_option("-a", "--address", type="string",
1097
help="Address to listen for requests on")
1098
parser.add_option("-p", "--port", type="int",
1099
help="Port number to receive requests on")
1100
parser.add_option("--check", action="store_true",
1101
help="Run self-test")
1102
parser.add_option("--debug", action="store_true",
1103
help="Debug mode; run in foreground and log to"
1105
parser.add_option("--priority", type="string", help="GnuTLS"
1106
" priority string (see GnuTLS documentation)")
1107
parser.add_option("--servicename", type="string", metavar="NAME",
1108
help="Zeroconf service name")
1109
parser.add_option("--configdir", type="string",
1110
default="/etc/mandos", metavar="DIR",
1111
help="Directory to search for configuration"
1113
parser.add_option("--no-dbus", action="store_false",
1115
help="Do not provide D-Bus system bus"
1117
parser.add_option("--no-ipv6", action="store_false",
1118
dest="use_ipv6", help="Do not use IPv6")
1178
parser.add_option("-i", u"--interface", type=u"string",
1179
metavar="IF", help=u"Bind to interface IF")
1180
parser.add_option("-a", u"--address", type=u"string",
1181
help=u"Address to listen for requests on")
1182
parser.add_option("-p", u"--port", type=u"int",
1183
help=u"Port number to receive requests on")
1184
parser.add_option("--check", action=u"store_true",
1185
help=u"Run self-test")
1186
parser.add_option("--debug", action=u"store_true",
1187
help=u"Debug mode; run in foreground and log to"
1189
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1190
u" priority string (see GnuTLS documentation)")
1191
parser.add_option("--servicename", type=u"string",
1192
metavar=u"NAME", help=u"Zeroconf service name")
1193
parser.add_option("--configdir", type=u"string",
1194
default=u"/etc/mandos", metavar=u"DIR",
1195
help=u"Directory to search for configuration"
1197
parser.add_option("--no-dbus", action=u"store_false",
1198
dest=u"use_dbus", help=u"Do not provide D-Bus"
1199
u" system bus interface")
1200
parser.add_option("--no-ipv6", action=u"store_false",
1201
dest=u"use_ipv6", help=u"Do not use IPv6")
1119
1202
options = parser.parse_args()[0]
1121
1204
if options.check:
1126
1209
# Default values for config file for server-global settings
1127
server_defaults = { "interface": "",
1132
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1133
"servicename": "Mandos",
1210
server_defaults = { u"interface": u"",
1215
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1216
u"servicename": u"Mandos",
1217
u"use_dbus": u"True",
1218
u"use_ipv6": u"True",
1138
1221
# Parse config file for server-global settings
1139
server_config = ConfigParser.SafeConfigParser(server_defaults)
1222
server_config = configparser.SafeConfigParser(server_defaults)
1140
1223
del server_defaults
1141
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1224
server_config.read(os.path.join(options.configdir,
1142
1226
# Convert the SafeConfigParser object to a dict
1143
1227
server_settings = server_config.defaults()
1144
1228
# Use the appropriate methods on the non-string config options
1145
server_settings["debug"] = server_config.getboolean("DEFAULT",
1147
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1149
server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1229
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1230
server_settings[option] = server_config.getboolean(u"DEFAULT",
1151
1232
if server_settings["port"]:
1152
server_settings["port"] = server_config.getint("DEFAULT",
1233
server_settings["port"] = server_config.getint(u"DEFAULT",
1154
1235
del server_config
1156
1237
# Override the settings from the config file with command line
1157
1238
# options, if set.
1158
for option in ("interface", "address", "port", "debug",
1159
"priority", "servicename", "configdir",
1160
"use_dbus", "use_ipv6"):
1239
for option in (u"interface", u"address", u"port", u"debug",
1240
u"priority", u"servicename", u"configdir",
1241
u"use_dbus", u"use_ipv6"):
1161
1242
value = getattr(options, option)
1162
1243
if value is not None:
1163
1244
server_settings[option] = value
1246
# Force all strings to be unicode
1247
for option in server_settings.keys():
1248
if type(server_settings[option]) is str:
1249
server_settings[option] = unicode(server_settings[option])
1165
1250
# Now we have our good server settings in "server_settings"
1167
1252
##################################################################
1169
1254
# For convenience
1170
debug = server_settings["debug"]
1171
use_dbus = server_settings["use_dbus"]
1172
use_ipv6 = server_settings["use_ipv6"]
1255
debug = server_settings[u"debug"]
1256
use_dbus = server_settings[u"use_dbus"]
1257
use_ipv6 = server_settings[u"use_ipv6"]
1175
1260
syslogger.setLevel(logging.WARNING)
1176
1261
console.setLevel(logging.WARNING)
1178
if server_settings["servicename"] != "Mandos":
1263
if server_settings[u"servicename"] != u"Mandos":
1179
1264
syslogger.setFormatter(logging.Formatter
1180
('Mandos (%s) [%%(process)d]:'
1181
' %%(levelname)s: %%(message)s'
1182
% server_settings["servicename"]))
1265
(u'Mandos (%s) [%%(process)d]:'
1266
u' %%(levelname)s: %%(message)s'
1267
% server_settings[u"servicename"]))
1184
1269
# Parse config file with clients
1185
client_defaults = { "timeout": "1h",
1187
"checker": "fping -q -- %%(host)s",
1270
client_defaults = { u"timeout": u"1h",
1272
u"checker": u"fping -q -- %%(host)s",
1190
client_config = ConfigParser.SafeConfigParser(client_defaults)
1191
client_config.read(os.path.join(server_settings["configdir"],
1275
client_config = configparser.SafeConfigParser(client_defaults)
1276
client_config.read(os.path.join(server_settings[u"configdir"],
1194
1279
global mandos_dbus_service
1195
1280
mandos_dbus_service = None
1198
tcp_server = IPv6_TCPServer((server_settings["address"],
1199
server_settings["port"]),
1201
settings=server_settings,
1202
clients=clients, use_ipv6=use_ipv6)
1203
pidfilename = "/var/run/mandos.pid"
1282
tcp_server = MandosServer((server_settings[u"address"],
1283
server_settings[u"port"]),
1285
interface=server_settings[u"interface"],
1288
server_settings[u"priority"],
1290
pidfilename = u"/var/run/mandos.pid"
1205
pidfile = open(pidfilename, "w")
1292
pidfile = open(pidfilename, u"w")
1206
1293
except IOError:
1207
logger.error("Could not open file %r", pidfilename)
1294
logger.error(u"Could not open file %r", pidfilename)
1210
uid = pwd.getpwnam("_mandos").pw_uid
1211
gid = pwd.getpwnam("_mandos").pw_gid
1297
uid = pwd.getpwnam(u"_mandos").pw_uid
1298
gid = pwd.getpwnam(u"_mandos").pw_gid
1212
1299
except KeyError:
1214
uid = pwd.getpwnam("mandos").pw_uid
1215
gid = pwd.getpwnam("mandos").pw_gid
1301
uid = pwd.getpwnam(u"mandos").pw_uid
1302
gid = pwd.getpwnam(u"mandos").pw_gid
1216
1303
except KeyError:
1218
uid = pwd.getpwnam("nobody").pw_uid
1219
gid = pwd.getpwnam("nogroup").pw_gid
1305
uid = pwd.getpwnam(u"nobody").pw_uid
1306
gid = pwd.getpwnam(u"nobody").pw_gid
1220
1307
except KeyError:
1236
1323
@gnutls.library.types.gnutls_log_func
1237
1324
def debug_gnutls(level, string):
1238
logger.debug("GnuTLS: %s", string[:-1])
1325
logger.debug(u"GnuTLS: %s", string[:-1])
1240
1327
(gnutls.library.functions
1241
1328
.gnutls_global_set_log_function(debug_gnutls))
1244
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1245
service = AvahiService(name = server_settings["servicename"],
1246
servicetype = "_mandos._tcp",
1247
protocol = protocol)
1248
if server_settings["interface"]:
1249
service.interface = (if_nametoindex
1250
(server_settings["interface"]))
1252
1330
global main_loop
1255
1331
# From the Avahi example code
1256
1332
DBusGMainLoop(set_as_default=True )
1257
1333
main_loop = gobject.MainLoop()
1258
1334
bus = dbus.SystemBus()
1259
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1260
avahi.DBUS_PATH_SERVER),
1261
avahi.DBUS_INTERFACE_SERVER)
1262
1335
# End of Avahi example code
1264
1337
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1338
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1339
service = AvahiService(name = server_settings[u"servicename"],
1340
servicetype = u"_mandos._tcp",
1341
protocol = protocol, bus = bus)
1342
if server_settings["interface"]:
1343
service.interface = (if_nametoindex
1344
(str(server_settings[u"interface"])))
1266
1346
client_class = Client
1268
client_class = ClientDBus
1348
client_class = functools.partial(ClientDBus, bus = bus)
1349
tcp_server.clients.update(set(
1270
1350
client_class(name = section,
1271
1351
config= dict(client_config.items(section)))
1272
1352
for section in client_config.sections()))
1353
if not tcp_server.clients:
1274
1354
logger.warning(u"No clients defined")
1323
1398
class MandosDBusService(dbus.service.Object):
1324
1399
"""A D-Bus proxy object"""
1325
1400
def __init__(self):
1326
dbus.service.Object.__init__(self, bus, "/")
1401
dbus.service.Object.__init__(self, bus, u"/")
1327
1402
_interface = u"se.bsnet.fukt.Mandos"
1329
@dbus.service.signal(_interface, signature="oa{sv}")
1404
@dbus.service.signal(_interface, signature=u"oa{sv}")
1330
1405
def ClientAdded(self, objpath, properties):
1334
@dbus.service.signal(_interface, signature="s")
1409
@dbus.service.signal(_interface, signature=u"s")
1335
1410
def ClientNotFound(self, fingerprint):
1339
@dbus.service.signal(_interface, signature="os")
1414
@dbus.service.signal(_interface, signature=u"os")
1340
1415
def ClientRemoved(self, objpath, name):
1344
@dbus.service.method(_interface, out_signature="ao")
1419
@dbus.service.method(_interface, out_signature=u"ao")
1345
1420
def GetAllClients(self):
1347
return dbus.Array(c.dbus_object_path for c in clients)
1422
return dbus.Array(c.dbus_object_path
1423
for c in tcp_server.clients)
1349
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1425
@dbus.service.method(_interface,
1426
out_signature=u"a{oa{sv}}")
1350
1427
def GetAllClientsWithProperties(self):
1352
1429
return dbus.Dictionary(
1353
1430
((c.dbus_object_path, c.GetAllProperties())
1431
for c in tcp_server.clients),
1432
signature=u"oa{sv}")
1357
@dbus.service.method(_interface, in_signature="o")
1434
@dbus.service.method(_interface, in_signature=u"o")
1358
1435
def RemoveClient(self, object_path):
1437
for c in tcp_server.clients:
1361
1438
if c.dbus_object_path == object_path:
1439
tcp_server.clients.remove(c)
1363
1440
c.remove_from_connection()
1364
1441
# Don't signal anything except ClientRemoved
1365
1442
c.disable(signal=False)