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.
899
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
900
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
825
901
def process_request(self, request, client_address):
826
"""This overrides and wraps the original process_request().
827
This function creates a new pipe in self.pipe
902
"""Overrides and wraps the original process_request().
904
This function creates a new pipe in self.pipe
829
906
self.pipe = os.pipe()
830
907
super(ForkingMixInWithPipe,
831
908
self).process_request(request, client_address)
832
909
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):
910
self.add_pipe(self.pipe[0])
911
def add_pipe(self, pipe):
838
912
"""Dummy function; override as necessary"""
843
916
class IPv6_TCPServer(ForkingMixInWithPipe,
844
SocketServer.TCPServer, object):
917
socketserver.TCPServer, object):
845
918
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
847
settings: Server settings
848
clients: Set() of Client objects
849
921
enabled: Boolean; whether this server is activated yet
922
interface: None or a network interface name (string)
923
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)
925
def __init__(self, server_address, RequestHandlerClass,
926
interface=None, use_ipv6=True):
927
self.interface = interface
929
self.address_family = socket.AF_INET6
930
socketserver.TCPServer.__init__(self, server_address,
865
932
def server_bind(self):
866
933
"""This overrides the normal server_bind() function
867
934
to bind to an interface if one was specified, and also NOT to
868
935
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"])
936
if self.interface is not None:
937
if SO_BINDTODEVICE is None:
938
logger.error(u"SO_BINDTODEVICE does not exist;"
939
u" cannot bind to interface %s",
943
self.socket.setsockopt(socket.SOL_SOCKET,
947
except socket.error, error:
948
if error[0] == errno.EPERM:
949
logger.error(u"No permission to"
950
u" bind to interface %s",
952
elif error[0] == errno.ENOPROTOOPT:
953
logger.error(u"SO_BINDTODEVICE not available;"
954
u" cannot bind to interface %s",
883
958
# Only bind(2) the socket if we really need to.
884
959
if self.server_address[0] or self.server_address[1]:
885
960
if not self.server_address[0]:
886
961
if self.address_family == socket.AF_INET6:
887
any_address = "::" # in6addr_any
962
any_address = u"::" # in6addr_any
889
964
any_address = socket.INADDR_ANY
890
965
self.server_address = (any_address,
892
967
elif not self.server_address[1]:
893
968
self.server_address = (self.server_address[0],
895
# if self.settings["interface"]:
896
971
# self.server_address = (self.server_address[0],
902
return super(IPv6_TCPServer, self).server_bind()
976
return socketserver.TCPServer.server_bind(self)
979
class MandosServer(IPv6_TCPServer):
983
clients: set of Client objects
984
gnutls_priority GnuTLS priority string
985
use_dbus: Boolean; to emit D-Bus signals or not
986
clients: set of Client objects
987
gnutls_priority GnuTLS priority string
988
use_dbus: Boolean; to emit D-Bus signals or not
990
Assumes a gobject.MainLoop event loop.
992
def __init__(self, server_address, RequestHandlerClass,
993
interface=None, use_ipv6=True, clients=None,
994
gnutls_priority=None, use_dbus=True):
996
self.clients = clients
997
if self.clients is None:
999
self.use_dbus = use_dbus
1000
self.gnutls_priority = gnutls_priority
1001
IPv6_TCPServer.__init__(self, server_address,
1002
RequestHandlerClass,
1003
interface = interface,
1004
use_ipv6 = use_ipv6)
903
1005
def server_activate(self):
904
1006
if self.enabled:
905
return super(IPv6_TCPServer, self).server_activate()
1007
return socketserver.TCPServer.server_activate(self)
906
1008
def enable(self):
907
1009
self.enabled = True
1010
def add_pipe(self, pipe):
1011
# Call "handle_ipc" for both data and EOF events
1012
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
908
1014
def handle_ipc(self, source, condition, file_objects={}):
909
1015
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
1016
gobject.IO_IN: u"IN", # There is data to read.
1017
gobject.IO_OUT: u"OUT", # Data can be written (without
1019
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1020
gobject.IO_ERR: u"ERR", # Error condition.
1021
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1022
# broken, usually for pipes and
919
1025
conditions_string = ' | '.join(name
920
1026
for cond, name in
921
1027
condition_names.iteritems()
922
1028
if cond & condition)
923
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1029
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
924
1030
conditions_string)
926
1032
# Turn the pipe file descriptor into a Python file object
927
1033
if source not in file_objects:
928
file_objects[source] = os.fdopen(source, "r", 1)
1034
file_objects[source] = os.fdopen(source, u"r", 1)
930
1036
# Read a line from the file object
931
1037
cmdline = file_objects[source].readline()
937
1043
# Stop calling this function
940
logger.debug("IPC command: %r", cmdline)
1046
logger.debug(u"IPC command: %r", cmdline)
942
1048
# Parse and act on command
943
cmd, args = cmdline.rstrip("\r\n").split(None, 1)
1049
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
945
if cmd == "NOTFOUND":
1051
if cmd == u"NOTFOUND":
946
1052
logger.warning(u"Client not found for fingerprint: %s",
948
if self.settings["use_dbus"]:
949
1055
# Emit D-Bus signal
950
1056
mandos_dbus_service.ClientNotFound(args)
951
elif cmd == "INVALID":
1057
elif cmd == u"INVALID":
952
1058
for client in self.clients:
953
1059
if client.name == args:
954
1060
logger.warning(u"Client %s is invalid", args)
955
if self.settings["use_dbus"]:
956
1062
# Emit D-Bus signal
957
1063
client.Rejected()
960
1066
logger.error(u"Unknown client %s is invalid", args)
961
elif cmd == "SENDING":
1067
elif cmd == u"SENDING":
962
1068
for client in self.clients:
963
1069
if client.name == args:
964
1070
logger.info(u"Sending secret to %s", client.name)
965
1071
client.checked_ok()
966
if self.settings["use_dbus"]:
967
1073
# Emit D-Bus signal
968
1074
client.ReceivedSecret()
1016
1122
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
1125
def if_nametoindex(interface):
1043
"""Call the C function if_nametoindex(), or equivalent"""
1126
"""Call the C function if_nametoindex(), or equivalent
1128
Note: This function cannot accept a unicode string."""
1044
1129
global if_nametoindex
1046
1131
if_nametoindex = (ctypes.cdll.LoadLibrary
1047
(ctypes.util.find_library("c"))
1132
(ctypes.util.find_library(u"c"))
1048
1133
.if_nametoindex)
1049
1134
except (OSError, AttributeError):
1050
if "struct" not in sys.modules:
1052
if "fcntl" not in sys.modules:
1135
logger.warning(u"Doing if_nametoindex the hard way")
1054
1136
def if_nametoindex(interface):
1055
1137
"Get an interface index the hard way, i.e. using fcntl()"
1056
1138
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1057
1139
with closing(socket.socket()) as s:
1058
1140
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1059
struct.pack("16s16x", interface))
1060
interface_index = struct.unpack("I", ifreq[16:20])[0]
1141
struct.pack(str(u"16s16x"),
1143
interface_index = struct.unpack(str(u"I"),
1061
1145
return interface_index
1062
1146
return if_nametoindex(interface)
1065
1149
def daemon(nochdir = False, noclose = False):
1066
1150
"""See daemon(3). Standard BSD Unix function.
1067
1152
This should really exist as os.daemon, but it doesn't (yet)."""
1071
1156
if not nochdir:
1075
1160
if not noclose:
1091
1176
# Parsing of options, both command line and config file
1093
1178
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")
1179
parser.add_option("-i", u"--interface", type=u"string",
1180
metavar="IF", help=u"Bind to interface IF")
1181
parser.add_option("-a", u"--address", type=u"string",
1182
help=u"Address to listen for requests on")
1183
parser.add_option("-p", u"--port", type=u"int",
1184
help=u"Port number to receive requests on")
1185
parser.add_option("--check", action=u"store_true",
1186
help=u"Run self-test")
1187
parser.add_option("--debug", action=u"store_true",
1188
help=u"Debug mode; run in foreground and log to"
1190
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1191
u" priority string (see GnuTLS documentation)")
1192
parser.add_option("--servicename", type=u"string",
1193
metavar=u"NAME", help=u"Zeroconf service name")
1194
parser.add_option("--configdir", type=u"string",
1195
default=u"/etc/mandos", metavar=u"DIR",
1196
help=u"Directory to search for configuration"
1198
parser.add_option("--no-dbus", action=u"store_false",
1199
dest=u"use_dbus", help=u"Do not provide D-Bus"
1200
u" system bus interface")
1201
parser.add_option("--no-ipv6", action=u"store_false",
1202
dest=u"use_ipv6", help=u"Do not use IPv6")
1119
1203
options = parser.parse_args()[0]
1121
1205
if options.check:
1126
1210
# Default values for config file for server-global settings
1127
server_defaults = { "interface": "",
1132
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1133
"servicename": "Mandos",
1211
server_defaults = { u"interface": u"",
1216
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1217
u"servicename": u"Mandos",
1218
u"use_dbus": u"True",
1219
u"use_ipv6": u"True",
1138
1222
# Parse config file for server-global settings
1139
server_config = ConfigParser.SafeConfigParser(server_defaults)
1223
server_config = configparser.SafeConfigParser(server_defaults)
1140
1224
del server_defaults
1141
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1225
server_config.read(os.path.join(options.configdir,
1142
1227
# Convert the SafeConfigParser object to a dict
1143
1228
server_settings = server_config.defaults()
1144
1229
# 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",
1230
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1231
server_settings[option] = server_config.getboolean(u"DEFAULT",
1151
1233
if server_settings["port"]:
1152
server_settings["port"] = server_config.getint("DEFAULT",
1234
server_settings["port"] = server_config.getint(u"DEFAULT",
1154
1236
del server_config
1156
1238
# Override the settings from the config file with command line
1157
1239
# options, if set.
1158
for option in ("interface", "address", "port", "debug",
1159
"priority", "servicename", "configdir",
1160
"use_dbus", "use_ipv6"):
1240
for option in (u"interface", u"address", u"port", u"debug",
1241
u"priority", u"servicename", u"configdir",
1242
u"use_dbus", u"use_ipv6"):
1161
1243
value = getattr(options, option)
1162
1244
if value is not None:
1163
1245
server_settings[option] = value
1247
# Force all strings to be unicode
1248
for option in server_settings.keys():
1249
if type(server_settings[option]) is str:
1250
server_settings[option] = unicode(server_settings[option])
1165
1251
# Now we have our good server settings in "server_settings"
1167
1253
##################################################################
1169
1255
# For convenience
1170
debug = server_settings["debug"]
1171
use_dbus = server_settings["use_dbus"]
1172
use_ipv6 = server_settings["use_ipv6"]
1256
debug = server_settings[u"debug"]
1257
use_dbus = server_settings[u"use_dbus"]
1258
use_ipv6 = server_settings[u"use_ipv6"]
1175
1261
syslogger.setLevel(logging.WARNING)
1176
1262
console.setLevel(logging.WARNING)
1178
if server_settings["servicename"] != "Mandos":
1264
if server_settings[u"servicename"] != u"Mandos":
1179
1265
syslogger.setFormatter(logging.Formatter
1180
('Mandos (%s) [%%(process)d]:'
1181
' %%(levelname)s: %%(message)s'
1182
% server_settings["servicename"]))
1266
(u'Mandos (%s) [%%(process)d]:'
1267
u' %%(levelname)s: %%(message)s'
1268
% server_settings[u"servicename"]))
1184
1270
# Parse config file with clients
1185
client_defaults = { "timeout": "1h",
1187
"checker": "fping -q -- %%(host)s",
1271
client_defaults = { u"timeout": u"1h",
1273
u"checker": u"fping -q -- %%(host)s",
1190
client_config = ConfigParser.SafeConfigParser(client_defaults)
1191
client_config.read(os.path.join(server_settings["configdir"],
1276
client_config = configparser.SafeConfigParser(client_defaults)
1277
client_config.read(os.path.join(server_settings[u"configdir"],
1194
1280
global mandos_dbus_service
1195
1281
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"
1283
tcp_server = MandosServer((server_settings[u"address"],
1284
server_settings[u"port"]),
1286
interface=server_settings[u"interface"],
1289
server_settings[u"priority"],
1291
pidfilename = u"/var/run/mandos.pid"
1205
pidfile = open(pidfilename, "w")
1293
pidfile = open(pidfilename, u"w")
1206
1294
except IOError:
1207
logger.error("Could not open file %r", pidfilename)
1295
logger.error(u"Could not open file %r", pidfilename)
1210
uid = pwd.getpwnam("_mandos").pw_uid
1211
gid = pwd.getpwnam("_mandos").pw_gid
1298
uid = pwd.getpwnam(u"_mandos").pw_uid
1299
gid = pwd.getpwnam(u"_mandos").pw_gid
1212
1300
except KeyError:
1214
uid = pwd.getpwnam("mandos").pw_uid
1215
gid = pwd.getpwnam("mandos").pw_gid
1302
uid = pwd.getpwnam(u"mandos").pw_uid
1303
gid = pwd.getpwnam(u"mandos").pw_gid
1216
1304
except KeyError:
1218
uid = pwd.getpwnam("nobody").pw_uid
1219
gid = pwd.getpwnam("nogroup").pw_gid
1306
uid = pwd.getpwnam(u"nobody").pw_uid
1307
gid = pwd.getpwnam(u"nobody").pw_gid
1220
1308
except KeyError:
1236
1324
@gnutls.library.types.gnutls_log_func
1237
1325
def debug_gnutls(level, string):
1238
logger.debug("GnuTLS: %s", string[:-1])
1326
logger.debug(u"GnuTLS: %s", string[:-1])
1240
1328
(gnutls.library.functions
1241
1329
.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
1331
global main_loop
1255
1332
# From the Avahi example code
1256
1333
DBusGMainLoop(set_as_default=True )
1257
1334
main_loop = gobject.MainLoop()
1258
1335
bus = dbus.SystemBus()
1259
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1260
avahi.DBUS_PATH_SERVER),
1261
avahi.DBUS_INTERFACE_SERVER)
1262
1336
# End of Avahi example code
1264
1338
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1339
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1340
service = AvahiService(name = server_settings[u"servicename"],
1341
servicetype = u"_mandos._tcp",
1342
protocol = protocol, bus = bus)
1343
if server_settings["interface"]:
1344
service.interface = (if_nametoindex
1345
(str(server_settings[u"interface"])))
1266
1347
client_class = Client
1268
client_class = ClientDBus
1349
client_class = functools.partial(ClientDBus, bus = bus)
1350
tcp_server.clients.update(set(
1270
1351
client_class(name = section,
1271
1352
config= dict(client_config.items(section)))
1272
1353
for section in client_config.sections()))
1354
if not tcp_server.clients:
1274
1355
logger.warning(u"No clients defined")
1323
1399
class MandosDBusService(dbus.service.Object):
1324
1400
"""A D-Bus proxy object"""
1325
1401
def __init__(self):
1326
dbus.service.Object.__init__(self, bus, "/")
1402
dbus.service.Object.__init__(self, bus, u"/")
1327
1403
_interface = u"se.bsnet.fukt.Mandos"
1329
@dbus.service.signal(_interface, signature="oa{sv}")
1405
@dbus.service.signal(_interface, signature=u"oa{sv}")
1330
1406
def ClientAdded(self, objpath, properties):
1334
@dbus.service.signal(_interface, signature="s")
1410
@dbus.service.signal(_interface, signature=u"s")
1335
1411
def ClientNotFound(self, fingerprint):
1339
@dbus.service.signal(_interface, signature="os")
1415
@dbus.service.signal(_interface, signature=u"os")
1340
1416
def ClientRemoved(self, objpath, name):
1344
@dbus.service.method(_interface, out_signature="ao")
1420
@dbus.service.method(_interface, out_signature=u"ao")
1345
1421
def GetAllClients(self):
1347
return dbus.Array(c.dbus_object_path for c in clients)
1423
return dbus.Array(c.dbus_object_path
1424
for c in tcp_server.clients)
1349
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1426
@dbus.service.method(_interface,
1427
out_signature=u"a{oa{sv}}")
1350
1428
def GetAllClientsWithProperties(self):
1352
1430
return dbus.Dictionary(
1353
1431
((c.dbus_object_path, c.GetAllProperties())
1432
for c in tcp_server.clients),
1433
signature=u"oa{sv}")
1357
@dbus.service.method(_interface, in_signature="o")
1435
@dbus.service.method(_interface, in_signature=u"o")
1358
1436
def RemoveClient(self, object_path):
1438
for c in tcp_server.clients:
1361
1439
if c.dbus_object_path == object_path:
1440
tcp_server.clients.remove(c)
1363
1441
c.remove_from_connection()
1364
1442
# Don't signal anything except ClientRemoved
1365
1443
c.disable(signal=False)