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
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
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):
742
669
# StopChecker - method
743
@dbus.service.method(_interface)
744
def StopChecker(self):
670
StopChecker = dbus.service.method(_interface)(stop_checker)
671
StopChecker.__name__ = "StopChecker"
750
class ClientHandler(socketserver.BaseRequestHandler, object):
751
"""A class to handle client connections.
753
Instantiated once for each connection to handle it.
676
def peer_certificate(session):
677
"Return the peer's OpenPGP certificate as a bytestring"
678
# If not an OpenPGP certificate...
679
if (gnutls.library.functions
680
.gnutls_certificate_type_get(session._c_object)
681
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
682
# ...do the normal thing
683
return session.peer_certificate
684
list_size = ctypes.c_uint(1)
685
cert_list = (gnutls.library.functions
686
.gnutls_certificate_get_peers
687
(session._c_object, ctypes.byref(list_size)))
688
if not bool(cert_list) and list_size.value != 0:
689
raise gnutls.errors.GNUTLSError("error getting peer"
691
if list_size.value == 0:
694
return ctypes.string_at(cert.data, cert.size)
697
def fingerprint(openpgp):
698
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
699
# New GnuTLS "datum" with the OpenPGP public key
700
datum = (gnutls.library.types
701
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
704
ctypes.c_uint(len(openpgp))))
705
# New empty GnuTLS certificate
706
crt = gnutls.library.types.gnutls_openpgp_crt_t()
707
(gnutls.library.functions
708
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
709
# Import the OpenPGP public key into the certificate
710
(gnutls.library.functions
711
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
712
gnutls.library.constants
713
.GNUTLS_OPENPGP_FMT_RAW))
714
# Verify the self signature in the key
715
crtverify = ctypes.c_uint()
716
(gnutls.library.functions
717
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
718
if crtverify.value != 0:
719
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
720
raise gnutls.errors.CertificateSecurityError("Verify failed")
721
# New buffer for the fingerprint
722
buf = ctypes.create_string_buffer(20)
723
buf_len = ctypes.c_size_t()
724
# Get the fingerprint from the certificate into the buffer
725
(gnutls.library.functions
726
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
727
ctypes.byref(buf_len)))
728
# Deinit the certificate
729
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
730
# Convert the buffer to a Python bytestring
731
fpr = ctypes.string_at(buf, buf_len.value)
732
# Convert the bytestring to hexadecimal notation
733
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
737
class TCP_handler(SocketServer.BaseRequestHandler, object):
738
"""A TCP request handler class.
739
Instantiated by IPv6_TCPServer for each request to handle it.
754
740
Note: This will run in its own forked process."""
756
742
def handle(self):
830
818
- (sent_size + sent))
831
819
sent_size += sent
835
def peer_certificate(session):
836
"Return the peer's OpenPGP certificate as a bytestring"
837
# If not an OpenPGP certificate...
838
if (gnutls.library.functions
839
.gnutls_certificate_type_get(session._c_object)
840
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
841
# ...do the normal thing
842
return session.peer_certificate
843
list_size = ctypes.c_uint(1)
844
cert_list = (gnutls.library.functions
845
.gnutls_certificate_get_peers
846
(session._c_object, ctypes.byref(list_size)))
847
if not bool(cert_list) and list_size.value != 0:
848
raise gnutls.errors.GNUTLSError(u"error getting peer"
850
if list_size.value == 0:
853
return ctypes.string_at(cert.data, cert.size)
856
def fingerprint(openpgp):
857
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
858
# New GnuTLS "datum" with the OpenPGP public key
859
datum = (gnutls.library.types
860
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
863
ctypes.c_uint(len(openpgp))))
864
# New empty GnuTLS certificate
865
crt = gnutls.library.types.gnutls_openpgp_crt_t()
866
(gnutls.library.functions
867
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
868
# Import the OpenPGP public key into the certificate
869
(gnutls.library.functions
870
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
871
gnutls.library.constants
872
.GNUTLS_OPENPGP_FMT_RAW))
873
# Verify the self signature in the key
874
crtverify = ctypes.c_uint()
875
(gnutls.library.functions
876
.gnutls_openpgp_crt_verify_self(crt, 0,
877
ctypes.byref(crtverify)))
878
if crtverify.value != 0:
879
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
880
raise (gnutls.errors.CertificateSecurityError
882
# New buffer for the fingerprint
883
buf = ctypes.create_string_buffer(20)
884
buf_len = ctypes.c_size_t()
885
# Get the fingerprint from the certificate into the buffer
886
(gnutls.library.functions
887
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
888
ctypes.byref(buf_len)))
889
# Deinit the certificate
890
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
891
# Convert the buffer to a Python bytestring
892
fpr = ctypes.string_at(buf, buf_len.value)
893
# Convert the bytestring to hexadecimal notation
894
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
898
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
899
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
823
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
824
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
825
Assumes a gobject.MainLoop event loop.
900
827
def process_request(self, request, client_address):
901
"""Overrides and wraps the original process_request().
828
"""This overrides and wraps the original process_request().
903
829
This function creates a new pipe in self.pipe
905
831
self.pipe = os.pipe()
906
832
super(ForkingMixInWithPipe,
907
833
self).process_request(request, client_address)
908
834
os.close(self.pipe[1]) # close write end
909
self.add_pipe(self.pipe[0])
910
def add_pipe(self, pipe):
835
# Call "handle_ipc" for both data and EOF events
836
gobject.io_add_watch(self.pipe[0],
837
gobject.IO_IN | gobject.IO_HUP,
839
def handle_ipc(source, condition):
911
840
"""Dummy function; override as necessary"""
915
845
class IPv6_TCPServer(ForkingMixInWithPipe,
916
socketserver.TCPServer, object):
846
SocketServer.TCPServer, object):
917
847
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
849
settings: Server settings
850
clients: Set() of Client objects
920
851
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,
853
address_family = socket.AF_INET6
854
def __init__(self, *args, **kwargs):
855
if "settings" in kwargs:
856
self.settings = kwargs["settings"]
857
del kwargs["settings"]
858
if "clients" in kwargs:
859
self.clients = kwargs["clients"]
860
del kwargs["clients"]
861
if "use_ipv6" in kwargs:
862
if not kwargs["use_ipv6"]:
863
self.address_family = socket.AF_INET
864
del kwargs["use_ipv6"]
866
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
931
867
def server_bind(self):
932
868
"""This overrides the normal server_bind() function
933
869
to bind to an interface if one was specified, and also NOT to
934
870
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",
871
if self.settings["interface"]:
872
# 25 is from /usr/include/asm-i486/socket.h
873
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
875
self.socket.setsockopt(socket.SOL_SOCKET,
877
self.settings["interface"])
878
except socket.error, error:
879
if error[0] == errno.EPERM:
880
logger.error(u"No permission to"
881
u" bind to interface %s",
882
self.settings["interface"])
957
885
# Only bind(2) the socket if we really need to.
958
886
if self.server_address[0] or self.server_address[1]:
959
887
if not self.server_address[0]:
960
888
if self.address_family == socket.AF_INET6:
961
any_address = u"::" # in6addr_any
889
any_address = "::" # in6addr_any
963
891
any_address = socket.INADDR_ANY
964
892
self.server_address = (any_address,
966
894
elif not self.server_address[1]:
967
895
self.server_address = (self.server_address[0],
897
# if self.settings["interface"]:
970
898
# 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)
904
return super(IPv6_TCPServer, self).server_bind()
1004
905
def server_activate(self):
1005
906
if self.enabled:
1006
return socketserver.TCPServer.server_activate(self)
907
return super(IPv6_TCPServer, self).server_activate()
1007
908
def enable(self):
1008
909
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
910
def handle_ipc(self, source, condition, file_objects={}):
1014
911
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
912
gobject.IO_IN: "IN", # There is data to read.
913
gobject.IO_OUT: "OUT", # Data can be written (without
915
gobject.IO_PRI: "PRI", # There is urgent data to read.
916
gobject.IO_ERR: "ERR", # Error condition.
917
gobject.IO_HUP: "HUP" # Hung up (the connection has been
918
# broken, usually for pipes and
1024
921
conditions_string = ' | '.join(name
1025
922
for cond, name in
1026
923
condition_names.iteritems()
1027
924
if cond & condition)
1028
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
925
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1029
926
conditions_string)
1031
928
# Turn the pipe file descriptor into a Python file object
1032
929
if source not in file_objects:
1033
file_objects[source] = os.fdopen(source, u"r", 1)
930
file_objects[source] = os.fdopen(source, "r", 1)
1035
932
# Read a line from the file object
1036
933
cmdline = file_objects[source].readline()
1042
939
# Stop calling this function
1045
logger.debug(u"IPC command: %r", cmdline)
942
logger.debug("IPC command: %r\n" % cmdline)
1047
944
# Parse and act on command
1048
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1050
if cmd == u"NOTFOUND":
1051
logger.warning(u"Client not found for fingerprint: %s",
945
cmd, args = cmdline.split(None, 1)
946
if cmd == "NOTFOUND":
947
if self.settings["use_dbus"]:
1054
948
# Emit D-Bus signal
1055
949
mandos_dbus_service.ClientNotFound(args)
1056
elif cmd == u"INVALID":
1057
for client in self.clients:
1058
if client.name == args:
1059
logger.warning(u"Client %s is invalid", args)
950
elif cmd == "INVALID":
951
if self.settings["use_dbus"]:
952
for client in self.clients:
953
if client.name == args:
1061
954
# Emit D-Bus signal
1062
955
client.Rejected()
1065
logger.error(u"Unknown client %s is invalid", args)
1066
elif cmd == u"SENDING":
957
elif cmd == "SENDING":
1067
958
for client in self.clients:
1068
959
if client.name == args:
1069
logger.info(u"Sending secret to %s", client.name)
1070
960
client.checked_ok()
961
if self.settings["use_dbus"]:
1072
962
# Emit D-Bus signal
1073
963
client.ReceivedSecret()
1076
logger.error(u"Sending secret to unknown client %s",
1079
logger.error(u"Unknown IPC command: %r", cmdline)
966
logger.error("Unknown IPC command: %r", cmdline)
1081
968
# Keep calling this function
1121
1008
return timevalue
1011
def server_state_changed(state):
1012
"""Derived from the Avahi example code"""
1013
if state == avahi.SERVER_COLLISION:
1014
logger.error(u"Zeroconf server name collision")
1016
elif state == avahi.SERVER_RUNNING:
1020
def entry_group_state_changed(state, error):
1021
"""Derived from the Avahi example code"""
1022
logger.debug(u"Avahi state change: %i", state)
1024
if state == avahi.ENTRY_GROUP_ESTABLISHED:
1025
logger.debug(u"Zeroconf service established.")
1026
elif state == avahi.ENTRY_GROUP_COLLISION:
1027
logger.warning(u"Zeroconf service name collision.")
1029
elif state == avahi.ENTRY_GROUP_FAILURE:
1030
logger.critical(u"Avahi: Error in group state changed %s",
1032
raise AvahiGroupError(u"State changed: %s" % unicode(error))
1124
1034
def if_nametoindex(interface):
1125
"""Call the C function if_nametoindex(), or equivalent
1127
Note: This function cannot accept a unicode string."""
1035
"""Call the C function if_nametoindex(), or equivalent"""
1128
1036
global if_nametoindex
1130
1038
if_nametoindex = (ctypes.cdll.LoadLibrary
1131
(ctypes.util.find_library(u"c"))
1039
(ctypes.util.find_library("c"))
1132
1040
.if_nametoindex)
1133
1041
except (OSError, AttributeError):
1134
logger.warning(u"Doing if_nametoindex the hard way")
1042
if "struct" not in sys.modules:
1044
if "fcntl" not in sys.modules:
1135
1046
def if_nametoindex(interface):
1136
1047
"Get an interface index the hard way, i.e. using fcntl()"
1137
1048
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1138
1049
with closing(socket.socket()) as s:
1139
1050
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1140
struct.pack(str(u"16s16x"),
1142
interface_index = struct.unpack(str(u"I"),
1051
struct.pack("16s16x", interface))
1052
interface_index = struct.unpack("I", ifreq[16:20])[0]
1144
1053
return interface_index
1145
1054
return if_nametoindex(interface)
1148
1057
def daemon(nochdir = False, noclose = False):
1149
1058
"""See daemon(3). Standard BSD Unix function.
1151
1059
This should really exist as os.daemon, but it doesn't (yet)."""
1155
1063
if not nochdir:
1159
1067
if not noclose:
1175
1083
# Parsing of options, both command line and config file
1177
1085
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")
1086
parser.add_option("-i", "--interface", type="string",
1087
metavar="IF", help="Bind to interface IF")
1088
parser.add_option("-a", "--address", type="string",
1089
help="Address to listen for requests on")
1090
parser.add_option("-p", "--port", type="int",
1091
help="Port number to receive requests on")
1092
parser.add_option("--check", action="store_true",
1093
help="Run self-test")
1094
parser.add_option("--debug", action="store_true",
1095
help="Debug mode; run in foreground and log to"
1097
parser.add_option("--priority", type="string", help="GnuTLS"
1098
" priority string (see GnuTLS documentation)")
1099
parser.add_option("--servicename", type="string", metavar="NAME",
1100
help="Zeroconf service name")
1101
parser.add_option("--configdir", type="string",
1102
default="/etc/mandos", metavar="DIR",
1103
help="Directory to search for configuration"
1105
parser.add_option("--no-dbus", action="store_false",
1107
help="Do not provide D-Bus system bus"
1109
parser.add_option("--no-ipv6", action="store_false",
1110
dest="use_ipv6", help="Do not use IPv6")
1202
1111
options = parser.parse_args()[0]
1204
1113
if options.check:
1209
1118
# 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",
1119
server_defaults = { "interface": "",
1124
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1125
"servicename": "Mandos",
1221
1130
# Parse config file for server-global settings
1222
server_config = configparser.SafeConfigParser(server_defaults)
1131
server_config = ConfigParser.SafeConfigParser(server_defaults)
1223
1132
del server_defaults
1224
server_config.read(os.path.join(options.configdir,
1133
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1226
1134
# Convert the SafeConfigParser object to a dict
1227
1135
server_settings = server_config.defaults()
1228
1136
# 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",
1137
server_settings["debug"] = server_config.getboolean("DEFAULT",
1139
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1141
server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1232
1143
if server_settings["port"]:
1233
server_settings["port"] = server_config.getint(u"DEFAULT",
1144
server_settings["port"] = server_config.getint("DEFAULT",
1235
1146
del server_config
1237
1148
# Override the settings from the config file with command line
1238
1149
# 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"):
1150
for option in ("interface", "address", "port", "debug",
1151
"priority", "servicename", "configdir",
1152
"use_dbus", "use_ipv6"):
1242
1153
value = getattr(options, option)
1243
1154
if value is not None:
1244
1155
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
1157
# Now we have our good server settings in "server_settings"
1252
1159
##################################################################
1254
1161
# For convenience
1255
debug = server_settings[u"debug"]
1256
use_dbus = server_settings[u"use_dbus"]
1257
use_ipv6 = server_settings[u"use_ipv6"]
1162
debug = server_settings["debug"]
1163
use_dbus = server_settings["use_dbus"]
1164
use_ipv6 = server_settings["use_ipv6"]
1260
1167
syslogger.setLevel(logging.WARNING)
1261
1168
console.setLevel(logging.WARNING)
1263
if server_settings[u"servicename"] != u"Mandos":
1170
if server_settings["servicename"] != "Mandos":
1264
1171
syslogger.setFormatter(logging.Formatter
1265
(u'Mandos (%s) [%%(process)d]:'
1266
u' %%(levelname)s: %%(message)s'
1267
% server_settings[u"servicename"]))
1172
('Mandos (%s) [%%(process)d]:'
1173
' %%(levelname)s: %%(message)s'
1174
% server_settings["servicename"]))
1269
1176
# Parse config file with clients
1270
client_defaults = { u"timeout": u"1h",
1272
u"checker": u"fping -q -- %%(host)s",
1177
client_defaults = { "timeout": "1h",
1179
"checker": "fping -q -- %%(host)s",
1275
client_config = configparser.SafeConfigParser(client_defaults)
1276
client_config.read(os.path.join(server_settings[u"configdir"],
1182
client_config = ConfigParser.SafeConfigParser(client_defaults)
1183
client_config.read(os.path.join(server_settings["configdir"],
1279
1186
global mandos_dbus_service
1280
1187
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"
1190
tcp_server = IPv6_TCPServer((server_settings["address"],
1191
server_settings["port"]),
1193
settings=server_settings,
1194
clients=clients, use_ipv6=use_ipv6)
1195
pidfilename = "/var/run/mandos.pid"
1292
pidfile = open(pidfilename, u"w")
1197
pidfile = open(pidfilename, "w")
1293
1198
except IOError:
1294
logger.error(u"Could not open file %r", pidfilename)
1199
logger.error("Could not open file %r", pidfilename)
1297
uid = pwd.getpwnam(u"_mandos").pw_uid
1298
gid = pwd.getpwnam(u"_mandos").pw_gid
1202
uid = pwd.getpwnam("_mandos").pw_uid
1203
gid = pwd.getpwnam("_mandos").pw_gid
1299
1204
except KeyError:
1301
uid = pwd.getpwnam(u"mandos").pw_uid
1302
gid = pwd.getpwnam(u"mandos").pw_gid
1206
uid = pwd.getpwnam("mandos").pw_uid
1207
gid = pwd.getpwnam("mandos").pw_gid
1303
1208
except KeyError:
1305
uid = pwd.getpwnam(u"nobody").pw_uid
1306
gid = pwd.getpwnam(u"nobody").pw_gid
1210
uid = pwd.getpwnam("nobody").pw_uid
1211
gid = pwd.getpwnam("nogroup").pw_gid
1307
1212
except KeyError:
1323
1228
@gnutls.library.types.gnutls_log_func
1324
1229
def debug_gnutls(level, string):
1325
logger.debug(u"GnuTLS: %s", string[:-1])
1230
logger.debug("GnuTLS: %s", string[:-1])
1327
1232
(gnutls.library.functions
1328
1233
.gnutls_global_set_log_function(debug_gnutls))
1236
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1237
service = AvahiService(name = server_settings["servicename"],
1238
servicetype = "_mandos._tcp",
1239
protocol = protocol)
1240
if server_settings["interface"]:
1241
service.interface = (if_nametoindex
1242
(server_settings["interface"]))
1330
1244
global main_loop
1331
1247
# From the Avahi example code
1332
1248
DBusGMainLoop(set_as_default=True )
1333
1249
main_loop = gobject.MainLoop()
1334
1250
bus = dbus.SystemBus()
1251
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1252
avahi.DBUS_PATH_SERVER),
1253
avahi.DBUS_INTERFACE_SERVER)
1335
1254
# End of Avahi example code
1337
1256
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
1258
client_class = Client
1348
client_class = functools.partial(ClientDBus, bus = bus)
1349
tcp_server.clients.update(set(
1260
client_class = ClientDBus
1350
1262
client_class(name = section,
1351
1263
config= dict(client_config.items(section)))
1352
1264
for section in client_config.sections()))
1353
if not tcp_server.clients:
1354
1266
logger.warning(u"No clients defined")
1398
1315
class MandosDBusService(dbus.service.Object):
1399
1316
"""A D-Bus proxy object"""
1400
1317
def __init__(self):
1401
dbus.service.Object.__init__(self, bus, u"/")
1318
dbus.service.Object.__init__(self, bus, "/")
1402
1319
_interface = u"se.bsnet.fukt.Mandos"
1404
@dbus.service.signal(_interface, signature=u"oa{sv}")
1321
@dbus.service.signal(_interface, signature="oa{sv}")
1405
1322
def ClientAdded(self, objpath, properties):
1409
@dbus.service.signal(_interface, signature=u"s")
1326
@dbus.service.signal(_interface, signature="s")
1410
1327
def ClientNotFound(self, fingerprint):
1414
@dbus.service.signal(_interface, signature=u"os")
1331
@dbus.service.signal(_interface, signature="os")
1415
1332
def ClientRemoved(self, objpath, name):
1419
@dbus.service.method(_interface, out_signature=u"ao")
1336
@dbus.service.method(_interface, out_signature="ao")
1420
1337
def GetAllClients(self):
1422
return dbus.Array(c.dbus_object_path
1423
for c in tcp_server.clients)
1339
return dbus.Array(c.dbus_object_path for c in clients)
1425
@dbus.service.method(_interface,
1426
out_signature=u"a{oa{sv}}")
1341
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1427
1342
def GetAllClientsWithProperties(self):
1429
1344
return dbus.Dictionary(
1430
1345
((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")
1349
@dbus.service.method(_interface, in_signature="o")
1435
1350
def RemoveClient(self, object_path):
1437
for c in tcp_server.clients:
1438
1353
if c.dbus_object_path == object_path:
1439
tcp_server.clients.remove(c)
1440
1355
c.remove_from_connection()
1441
1356
# Don't signal anything except ClientRemoved
1442
1357
c.disable(signal=False)