152
133
u" after %i retries, exiting.",
153
134
self.rename_count)
154
135
raise AvahiServiceError(u"Too many renames")
155
self.name = self.server.GetAlternativeServiceName(self.name)
136
self.name = server.GetAlternativeServiceName(self.name)
156
137
logger.info(u"Changing Zeroconf service name to %r ...",
158
139
syslogger.setFormatter(logging.Formatter
159
(u'Mandos (%s) [%%(process)d]:'
160
u' %%(levelname)s: %%(message)s'
140
('Mandos (%s) [%%(process)d]:'
141
' %%(levelname)s: %%(message)s'
164
145
self.rename_count += 1
165
146
def remove(self):
166
147
"""Derived from the Avahi example code"""
167
if self.group is not None:
148
if group is not None:
170
151
"""Derived from the Avahi example code"""
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)
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)
178
160
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
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())
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)
227
182
class Client(object):
228
183
"""A representation of a client host served by this server.
231
185
name: string; from the config file, used in log messages and
232
186
D-Bus identifiers
254
208
instance %(name)s can be used in the command.
255
209
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))
265
211
def timeout_milliseconds(self):
266
212
"Return the 'timeout' attribute in milliseconds"
267
return self._datetime_to_milliseconds(self.timeout)
213
return ((self.timeout.days * 24 * 60 * 60 * 1000)
214
+ (self.timeout.seconds * 1000)
215
+ (self.timeout.microseconds // 1000))
269
217
def interval_milliseconds(self):
270
218
"Return the 'interval' attribute in milliseconds"
271
return self._datetime_to_milliseconds(self.interval)
219
return ((self.interval.days * 24 * 60 * 60 * 1000)
220
+ (self.interval.seconds * 1000)
221
+ (self.interval.microseconds // 1000))
273
223
def __init__(self, name = None, disable_hook=None, config=None):
274
224
"""Note: the 'checker' key in 'config' sets the
281
231
# Uppercase and remove spaces from fingerprint for later
282
232
# comparison purposes with return value from the fingerprint()
284
self.fingerprint = (config[u"fingerprint"].upper()
234
self.fingerprint = (config["fingerprint"].upper()
285
235
.replace(u" ", u""))
286
236
logger.debug(u" Fingerprint: %s", self.fingerprint)
287
if u"secret" in config:
288
self.secret = config[u"secret"].decode(u"base64")
289
elif u"secfile" in config:
237
if "secret" in config:
238
self.secret = config["secret"].decode(u"base64")
239
elif "secfile" in config:
290
240
with closing(open(os.path.expanduser
291
241
(os.path.expandvars
292
(config[u"secfile"])))) as secfile:
242
(config["secfile"])))) as secfile:
293
243
self.secret = secfile.read()
295
245
raise TypeError(u"No secret or secfile for client %s"
297
self.host = config.get(u"host", u"")
247
self.host = config.get("host", "")
298
248
self.created = datetime.datetime.utcnow()
299
249
self.enabled = False
300
250
self.last_enabled = None
301
251
self.last_checked_ok = None
302
self.timeout = string_to_delta(config[u"timeout"])
303
self.interval = string_to_delta(config[u"interval"])
252
self.timeout = string_to_delta(config["timeout"])
253
self.interval = string_to_delta(config["interval"])
304
254
self.disable_hook = disable_hook
305
255
self.checker = None
306
256
self.checker_initiator_tag = None
307
257
self.disable_initiator_tag = None
308
258
self.checker_callback_tag = None
309
self.checker_command = config[u"checker"]
259
self.checker_command = config["checker"]
310
260
self.current_checker_command = None
311
261
self.last_connect = None
313
263
def enable(self):
314
264
"""Start this client's checker and timeout hooks"""
315
if getattr(self, u"enabled", False):
318
265
self.last_enabled = datetime.datetime.utcnow()
319
266
# Schedule a new checker to be started an 'interval' from now,
320
267
# and every interval from then on.
480
422
class ClientDBus(Client, dbus.service.Object):
481
423
"""A Client class using D-Bus
484
dbus_object_path: dbus.ObjectPath
485
bus: dbus.SystemBus()
425
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
487
427
# dbus.service.Object doesn't use super(), so we can't either.
489
def __init__(self, bus = None, *args, **kwargs):
429
def __init__(self, *args, **kwargs):
491
430
Client.__init__(self, *args, **kwargs)
492
431
# Only now, when this client is initialized, can it show up on
494
433
self.dbus_object_path = (dbus.ObjectPath
496
+ self.name.replace(u".", u"_")))
497
dbus.service.Object.__init__(self, self.bus,
435
+ self.name.replace(".", "_")))
436
dbus.service.Object.__init__(self, bus,
498
437
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)
506
438
def enable(self):
507
oldstate = getattr(self, u"enabled", False)
439
oldstate = getattr(self, "enabled", False)
508
440
r = Client.enable(self)
509
441
if oldstate != self.enabled:
510
442
# Emit D-Bus signals
511
443
self.PropertyChanged(dbus.String(u"enabled"),
512
444
dbus.Boolean(True, variant_level=1))
513
self.PropertyChanged(
514
dbus.String(u"last_enabled"),
515
self._datetime_to_dbus(self.last_enabled,
445
self.PropertyChanged(dbus.String(u"last_enabled"),
446
(_datetime_to_dbus(self.last_enabled,
519
450
def disable(self, signal = True):
520
oldstate = getattr(self, u"enabled", False)
451
oldstate = getattr(self, "enabled", False)
521
452
r = Client.disable(self)
522
453
if signal and oldstate != self.enabled:
523
454
# Emit D-Bus signal
578
509
# Emit D-Bus signal
579
510
self.CheckerStarted(self.current_checker_command)
580
511
self.PropertyChanged(
581
dbus.String(u"checker_running"),
512
dbus.String("checker_running"),
582
513
dbus.Boolean(True, variant_level=1))
585
516
def stop_checker(self, *args, **kwargs):
586
old_checker = getattr(self, u"checker", None)
517
old_checker = getattr(self, "checker", None)
587
518
r = Client.stop_checker(self, *args, **kwargs)
588
519
if (old_checker is not None
589
and getattr(self, u"checker", None) is None):
520
and getattr(self, "checker", None) is None):
590
521
self.PropertyChanged(dbus.String(u"checker_running"),
591
522
dbus.Boolean(False, variant_level=1))
595
526
_interface = u"se.bsnet.fukt.Mandos.Client"
597
528
# CheckedOK - method
598
@dbus.service.method(_interface)
600
return self.checked_ok()
529
CheckedOK = dbus.service.method(_interface)(checked_ok)
530
CheckedOK.__name__ = "CheckedOK"
602
532
# CheckerCompleted - signal
603
@dbus.service.signal(_interface, signature=u"nxs")
533
@dbus.service.signal(_interface, signature="nxs")
604
534
def CheckerCompleted(self, exitcode, waitstatus, command):
608
538
# CheckerStarted - signal
609
@dbus.service.signal(_interface, signature=u"s")
539
@dbus.service.signal(_interface, signature="s")
610
540
def CheckerStarted(self, command):
614
544
# GetAllProperties - method
615
@dbus.service.method(_interface, out_signature=u"a{sv}")
545
@dbus.service.method(_interface, out_signature="a{sv}")
616
546
def GetAllProperties(self):
618
548
return dbus.Dictionary({
619
dbus.String(u"name"):
620
550
dbus.String(self.name, variant_level=1),
621
dbus.String(u"fingerprint"):
551
dbus.String("fingerprint"):
622
552
dbus.String(self.fingerprint, variant_level=1),
623
dbus.String(u"host"):
624
554
dbus.String(self.host, variant_level=1),
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,
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,
631
560
if self.last_enabled is not None
632
561
else dbus.Boolean(False, variant_level=1)),
633
dbus.String(u"enabled"):
562
dbus.String("enabled"):
634
563
dbus.Boolean(self.enabled, variant_level=1),
635
dbus.String(u"last_checked_ok"):
636
(self._datetime_to_dbus(self.last_checked_ok,
564
dbus.String("last_checked_ok"):
565
(_datetime_to_dbus(self.last_checked_ok,
638
567
if self.last_checked_ok is not None
639
568
else dbus.Boolean (False, variant_level=1)),
640
dbus.String(u"timeout"):
569
dbus.String("timeout"):
641
570
dbus.UInt64(self.timeout_milliseconds(),
642
571
variant_level=1),
643
dbus.String(u"interval"):
572
dbus.String("interval"):
644
573
dbus.UInt64(self.interval_milliseconds(),
645
574
variant_level=1),
646
dbus.String(u"checker"):
575
dbus.String("checker"):
647
576
dbus.String(self.checker_command,
648
577
variant_level=1),
649
dbus.String(u"checker_running"):
578
dbus.String("checker_running"):
650
579
dbus.Boolean(self.checker is not None,
651
580
variant_level=1),
652
dbus.String(u"object_path"):
581
dbus.String("object_path"):
653
582
dbus.ObjectPath(self.dbus_object_path,
657
586
# IsStillValid - method
658
@dbus.service.method(_interface, out_signature=u"b")
587
@dbus.service.method(_interface, out_signature="b")
659
588
def IsStillValid(self):
660
589
return self.still_valid()
662
591
# PropertyChanged - signal
663
@dbus.service.signal(_interface, signature=u"sv")
592
@dbus.service.signal(_interface, signature="sv")
664
593
def PropertyChanged(self, property, value):
778
703
# no X.509 keys are added to it. Therefore, we can use it
779
704
# here despite using OpenPGP certificates.
781
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
782
# u"+AES-256-CBC", u"+SHA1",
783
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
706
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
707
# "+AES-256-CBC", "+SHA1",
708
# "+COMP-NULL", "+CTYPE-OPENPGP",
785
710
# Use a fallback default, since this MUST be set.
786
priority = self.server.gnutls_priority
711
priority = self.server.settings.get("priority", "NORMAL")
789
712
(gnutls.library.functions
790
713
.gnutls_priority_set_direct(session._c_object,
898
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
899
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
821
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
822
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
823
Assumes a gobject.MainLoop event loop.
900
825
def process_request(self, request, client_address):
901
"""Overrides and wraps the original process_request().
826
"""This overrides and wraps the original process_request().
903
827
This function creates a new pipe in self.pipe
905
829
self.pipe = os.pipe()
906
830
super(ForkingMixInWithPipe,
907
831
self).process_request(request, client_address)
908
832
os.close(self.pipe[1]) # close write end
909
self.add_pipe(self.pipe[0])
910
def add_pipe(self, pipe):
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):
911
838
"""Dummy function; override as necessary"""
915
843
class IPv6_TCPServer(ForkingMixInWithPipe,
916
socketserver.TCPServer, object):
844
SocketServer.TCPServer, object):
917
845
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
847
settings: Server settings
848
clients: Set() of Client objects
920
849
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
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,
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)
931
865
def server_bind(self):
932
866
"""This overrides the normal server_bind() function
933
867
to bind to an interface if one was specified, and also NOT to
934
868
bind to an address or port if they were not specified."""
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",
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"])
957
883
# Only bind(2) the socket if we really need to.
958
884
if self.server_address[0] or self.server_address[1]:
959
885
if not self.server_address[0]:
960
886
if self.address_family == socket.AF_INET6:
961
any_address = u"::" # in6addr_any
887
any_address = "::" # in6addr_any
963
889
any_address = socket.INADDR_ANY
964
890
self.server_address = (any_address,
966
892
elif not self.server_address[1]:
967
893
self.server_address = (self.server_address[0],
895
# if self.settings["interface"]:
970
896
# self.server_address = (self.server_address[0],
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)
902
return super(IPv6_TCPServer, self).server_bind()
1004
903
def server_activate(self):
1005
904
if self.enabled:
1006
return socketserver.TCPServer.server_activate(self)
905
return super(IPv6_TCPServer, self).server_activate()
1007
906
def enable(self):
1008
907
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,
1013
908
def handle_ipc(self, source, condition, file_objects={}):
1014
909
condition_names = {
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
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
1024
919
conditions_string = ' | '.join(name
1025
920
for cond, name in
1026
921
condition_names.iteritems()
1027
922
if cond & condition)
1028
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
923
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1029
924
conditions_string)
1031
926
# Turn the pipe file descriptor into a Python file object
1032
927
if source not in file_objects:
1033
file_objects[source] = os.fdopen(source, u"r", 1)
928
file_objects[source] = os.fdopen(source, "r", 1)
1035
930
# Read a line from the file object
1036
931
cmdline = file_objects[source].readline()
1042
937
# Stop calling this function
1045
logger.debug(u"IPC command: %r", cmdline)
940
logger.debug("IPC command: %r", cmdline)
1047
942
# Parse and act on command
1048
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
943
cmd, args = cmdline.rstrip("\r\n").split(None, 1)
1050
if cmd == u"NOTFOUND":
945
if cmd == "NOTFOUND":
1051
946
logger.warning(u"Client not found for fingerprint: %s",
948
if self.settings["use_dbus"]:
1054
949
# Emit D-Bus signal
1055
950
mandos_dbus_service.ClientNotFound(args)
1056
elif cmd == u"INVALID":
951
elif cmd == "INVALID":
1057
952
for client in self.clients:
1058
953
if client.name == args:
1059
954
logger.warning(u"Client %s is invalid", args)
955
if self.settings["use_dbus"]:
1061
956
# Emit D-Bus signal
1062
957
client.Rejected()
1065
960
logger.error(u"Unknown client %s is invalid", args)
1066
elif cmd == u"SENDING":
961
elif cmd == "SENDING":
1067
962
for client in self.clients:
1068
963
if client.name == args:
1069
964
logger.info(u"Sending secret to %s", client.name)
1070
965
client.checked_ok()
966
if self.settings["use_dbus"]:
1072
967
# Emit D-Bus signal
1073
968
client.ReceivedSecret()
1121
1016
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))
1124
1042
def if_nametoindex(interface):
1125
"""Call the C function if_nametoindex(), or equivalent
1127
Note: This function cannot accept a unicode string."""
1043
"""Call the C function if_nametoindex(), or equivalent"""
1128
1044
global if_nametoindex
1130
1046
if_nametoindex = (ctypes.cdll.LoadLibrary
1131
(ctypes.util.find_library(u"c"))
1047
(ctypes.util.find_library("c"))
1132
1048
.if_nametoindex)
1133
1049
except (OSError, AttributeError):
1134
logger.warning(u"Doing if_nametoindex the hard way")
1050
if "struct" not in sys.modules:
1052
if "fcntl" not in sys.modules:
1135
1054
def if_nametoindex(interface):
1136
1055
"Get an interface index the hard way, i.e. using fcntl()"
1137
1056
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1138
1057
with closing(socket.socket()) as s:
1139
1058
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1140
struct.pack(str(u"16s16x"),
1142
interface_index = struct.unpack(str(u"I"),
1059
struct.pack("16s16x", interface))
1060
interface_index = struct.unpack("I", ifreq[16:20])[0]
1144
1061
return interface_index
1145
1062
return if_nametoindex(interface)
1148
1065
def daemon(nochdir = False, noclose = False):
1149
1066
"""See daemon(3). Standard BSD Unix function.
1151
1067
This should really exist as os.daemon, but it doesn't (yet)."""
1155
1071
if not nochdir:
1159
1075
if not noclose:
1175
1091
# Parsing of options, both command line and config file
1177
1093
parser = optparse.OptionParser(version = "%%prog %s" % version)
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")
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")
1202
1119
options = parser.parse_args()[0]
1204
1121
if options.check:
1209
1126
# Default values for config file for server-global settings
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",
1127
server_defaults = { "interface": "",
1132
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1133
"servicename": "Mandos",
1221
1138
# Parse config file for server-global settings
1222
server_config = configparser.SafeConfigParser(server_defaults)
1139
server_config = ConfigParser.SafeConfigParser(server_defaults)
1223
1140
del server_defaults
1224
server_config.read(os.path.join(options.configdir,
1141
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1226
1142
# Convert the SafeConfigParser object to a dict
1227
1143
server_settings = server_config.defaults()
1228
1144
# Use the appropriate methods on the non-string config options
1229
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1230
server_settings[option] = server_config.getboolean(u"DEFAULT",
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",
1232
1151
if server_settings["port"]:
1233
server_settings["port"] = server_config.getint(u"DEFAULT",
1152
server_settings["port"] = server_config.getint("DEFAULT",
1235
1154
del server_config
1237
1156
# Override the settings from the config file with command line
1238
1157
# options, if set.
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"):
1158
for option in ("interface", "address", "port", "debug",
1159
"priority", "servicename", "configdir",
1160
"use_dbus", "use_ipv6"):
1242
1161
value = getattr(options, option)
1243
1162
if value is not None:
1244
1163
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])
1250
1165
# Now we have our good server settings in "server_settings"
1252
1167
##################################################################
1254
1169
# For convenience
1255
debug = server_settings[u"debug"]
1256
use_dbus = server_settings[u"use_dbus"]
1257
use_ipv6 = server_settings[u"use_ipv6"]
1170
debug = server_settings["debug"]
1171
use_dbus = server_settings["use_dbus"]
1172
use_ipv6 = server_settings["use_ipv6"]
1260
1175
syslogger.setLevel(logging.WARNING)
1261
1176
console.setLevel(logging.WARNING)
1263
if server_settings[u"servicename"] != u"Mandos":
1178
if server_settings["servicename"] != "Mandos":
1264
1179
syslogger.setFormatter(logging.Formatter
1265
(u'Mandos (%s) [%%(process)d]:'
1266
u' %%(levelname)s: %%(message)s'
1267
% server_settings[u"servicename"]))
1180
('Mandos (%s) [%%(process)d]:'
1181
' %%(levelname)s: %%(message)s'
1182
% server_settings["servicename"]))
1269
1184
# Parse config file with clients
1270
client_defaults = { u"timeout": u"1h",
1272
u"checker": u"fping -q -- %%(host)s",
1185
client_defaults = { "timeout": "1h",
1187
"checker": "fping -q -- %%(host)s",
1275
client_config = configparser.SafeConfigParser(client_defaults)
1276
client_config.read(os.path.join(server_settings[u"configdir"],
1190
client_config = ConfigParser.SafeConfigParser(client_defaults)
1191
client_config.read(os.path.join(server_settings["configdir"],
1279
1194
global mandos_dbus_service
1280
1195
mandos_dbus_service = None
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"
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"
1292
pidfile = open(pidfilename, u"w")
1205
pidfile = open(pidfilename, "w")
1293
1206
except IOError:
1294
logger.error(u"Could not open file %r", pidfilename)
1207
logger.error("Could not open file %r", pidfilename)
1297
uid = pwd.getpwnam(u"_mandos").pw_uid
1298
gid = pwd.getpwnam(u"_mandos").pw_gid
1210
uid = pwd.getpwnam("_mandos").pw_uid
1211
gid = pwd.getpwnam("_mandos").pw_gid
1299
1212
except KeyError:
1301
uid = pwd.getpwnam(u"mandos").pw_uid
1302
gid = pwd.getpwnam(u"mandos").pw_gid
1214
uid = pwd.getpwnam("mandos").pw_uid
1215
gid = pwd.getpwnam("mandos").pw_gid
1303
1216
except KeyError:
1305
uid = pwd.getpwnam(u"nobody").pw_uid
1306
gid = pwd.getpwnam(u"nobody").pw_gid
1218
uid = pwd.getpwnam("nobody").pw_uid
1219
gid = pwd.getpwnam("nogroup").pw_gid
1307
1220
except KeyError:
1323
1236
@gnutls.library.types.gnutls_log_func
1324
1237
def debug_gnutls(level, string):
1325
logger.debug(u"GnuTLS: %s", string[:-1])
1238
logger.debug("GnuTLS: %s", string[:-1])
1327
1240
(gnutls.library.functions
1328
1241
.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"]))
1330
1252
global main_loop
1331
1255
# From the Avahi example code
1332
1256
DBusGMainLoop(set_as_default=True )
1333
1257
main_loop = gobject.MainLoop()
1334
1258
bus = dbus.SystemBus()
1259
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1260
avahi.DBUS_PATH_SERVER),
1261
avahi.DBUS_INTERFACE_SERVER)
1335
1262
# End of Avahi example code
1337
1264
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"])))
1346
1266
client_class = Client
1348
client_class = functools.partial(ClientDBus, bus = bus)
1349
tcp_server.clients.update(set(
1268
client_class = ClientDBus
1350
1270
client_class(name = section,
1351
1271
config= dict(client_config.items(section)))
1352
1272
for section in client_config.sections()))
1353
if not tcp_server.clients:
1354
1274
logger.warning(u"No clients defined")
1398
1323
class MandosDBusService(dbus.service.Object):
1399
1324
"""A D-Bus proxy object"""
1400
1325
def __init__(self):
1401
dbus.service.Object.__init__(self, bus, u"/")
1326
dbus.service.Object.__init__(self, bus, "/")
1402
1327
_interface = u"se.bsnet.fukt.Mandos"
1404
@dbus.service.signal(_interface, signature=u"oa{sv}")
1329
@dbus.service.signal(_interface, signature="oa{sv}")
1405
1330
def ClientAdded(self, objpath, properties):
1409
@dbus.service.signal(_interface, signature=u"s")
1334
@dbus.service.signal(_interface, signature="s")
1410
1335
def ClientNotFound(self, fingerprint):
1414
@dbus.service.signal(_interface, signature=u"os")
1339
@dbus.service.signal(_interface, signature="os")
1415
1340
def ClientRemoved(self, objpath, name):
1419
@dbus.service.method(_interface, out_signature=u"ao")
1344
@dbus.service.method(_interface, out_signature="ao")
1420
1345
def GetAllClients(self):
1422
return dbus.Array(c.dbus_object_path
1423
for c in tcp_server.clients)
1347
return dbus.Array(c.dbus_object_path for c in clients)
1425
@dbus.service.method(_interface,
1426
out_signature=u"a{oa{sv}}")
1349
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1427
1350
def GetAllClientsWithProperties(self):
1429
1352
return dbus.Dictionary(
1430
1353
((c.dbus_object_path, c.GetAllProperties())
1431
for c in tcp_server.clients),
1432
signature=u"oa{sv}")
1434
@dbus.service.method(_interface, in_signature=u"o")
1357
@dbus.service.method(_interface, in_signature="o")
1435
1358
def RemoveClient(self, object_path):
1437
for c in tcp_server.clients:
1438
1361
if c.dbus_object_path == object_path:
1439
tcp_server.clients.remove(c)
1440
1363
c.remove_from_connection()
1441
1364
# Don't signal anything except ClientRemoved
1442
1365
c.disable(signal=False)