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
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
503
573
old_checker_pid = None
504
574
r = Client.start_checker(self, *args, **kwargs)
505
# Only emit D-Bus signal if new checker process was started
506
if ((self.checker is not None)
507
and not (old_checker is not None
508
and old_checker_pid == self.checker.pid)):
575
# Only if new checker process was started
576
if (self.checker is not None
577
and old_checker_pid != self.checker.pid):
509
579
self.CheckerStarted(self.current_checker_command)
510
580
self.PropertyChanged(
511
dbus.String("checker_running"),
581
dbus.String(u"checker_running"),
512
582
dbus.Boolean(True, variant_level=1))
515
585
def stop_checker(self, *args, **kwargs):
516
old_checker = getattr(self, "checker", None)
586
old_checker = getattr(self, u"checker", None)
517
587
r = Client.stop_checker(self, *args, **kwargs)
518
588
if (old_checker is not None
519
and getattr(self, "checker", None) is None):
589
and getattr(self, u"checker", None) is None):
520
590
self.PropertyChanged(dbus.String(u"checker_running"),
521
591
dbus.Boolean(False, variant_level=1))
525
595
_interface = u"se.bsnet.fukt.Mandos.Client"
527
597
# CheckedOK - method
528
CheckedOK = dbus.service.method(_interface)(checked_ok)
529
CheckedOK.__name__ = "CheckedOK"
598
@dbus.service.method(_interface)
600
return self.checked_ok()
531
602
# CheckerCompleted - signal
532
@dbus.service.signal(_interface, signature="nxs")
603
@dbus.service.signal(_interface, signature=u"nxs")
533
604
def CheckerCompleted(self, exitcode, waitstatus, command):
537
608
# CheckerStarted - signal
538
@dbus.service.signal(_interface, signature="s")
609
@dbus.service.signal(_interface, signature=u"s")
539
610
def CheckerStarted(self, command):
543
614
# GetAllProperties - method
544
@dbus.service.method(_interface, out_signature="a{sv}")
615
@dbus.service.method(_interface, out_signature=u"a{sv}")
545
616
def GetAllProperties(self):
547
618
return dbus.Dictionary({
619
dbus.String(u"name"):
549
620
dbus.String(self.name, variant_level=1),
550
dbus.String("fingerprint"):
621
dbus.String(u"fingerprint"):
551
622
dbus.String(self.fingerprint, variant_level=1),
623
dbus.String(u"host"):
553
624
dbus.String(self.host, variant_level=1),
554
dbus.String("created"):
555
_datetime_to_dbus(self.created, variant_level=1),
556
dbus.String("last_enabled"):
557
(_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,
559
631
if self.last_enabled is not None
560
632
else dbus.Boolean(False, variant_level=1)),
561
dbus.String("enabled"):
633
dbus.String(u"enabled"):
562
634
dbus.Boolean(self.enabled, variant_level=1),
563
dbus.String("last_checked_ok"):
564
(_datetime_to_dbus(self.last_checked_ok,
635
dbus.String(u"last_checked_ok"):
636
(self._datetime_to_dbus(self.last_checked_ok,
566
638
if self.last_checked_ok is not None
567
639
else dbus.Boolean (False, variant_level=1)),
568
dbus.String("timeout"):
640
dbus.String(u"timeout"):
569
641
dbus.UInt64(self.timeout_milliseconds(),
570
642
variant_level=1),
571
dbus.String("interval"):
643
dbus.String(u"interval"):
572
644
dbus.UInt64(self.interval_milliseconds(),
573
645
variant_level=1),
574
dbus.String("checker"):
646
dbus.String(u"checker"):
575
647
dbus.String(self.checker_command,
576
648
variant_level=1),
577
dbus.String("checker_running"):
649
dbus.String(u"checker_running"):
578
650
dbus.Boolean(self.checker is not None,
579
651
variant_level=1),
580
dbus.String("object_path"):
652
dbus.String(u"object_path"):
581
653
dbus.ObjectPath(self.dbus_object_path,
585
657
# IsStillValid - method
586
@dbus.service.method(_interface, out_signature="b")
658
@dbus.service.method(_interface, out_signature=u"b")
587
659
def IsStillValid(self):
588
660
return self.still_valid()
590
662
# PropertyChanged - signal
591
@dbus.service.signal(_interface, signature="sv")
663
@dbus.service.signal(_interface, signature=u"sv")
592
664
def PropertyChanged(self, property, value):
668
742
# StopChecker - method
669
StopChecker = dbus.service.method(_interface)(stop_checker)
670
StopChecker.__name__ = "StopChecker"
743
@dbus.service.method(_interface)
744
def StopChecker(self):
675
def peer_certificate(session):
676
"Return the peer's OpenPGP certificate as a bytestring"
677
# If not an OpenPGP certificate...
678
if (gnutls.library.functions
679
.gnutls_certificate_type_get(session._c_object)
680
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
681
# ...do the normal thing
682
return session.peer_certificate
683
list_size = ctypes.c_uint(1)
684
cert_list = (gnutls.library.functions
685
.gnutls_certificate_get_peers
686
(session._c_object, ctypes.byref(list_size)))
687
if not bool(cert_list) and list_size.value != 0:
688
raise gnutls.errors.GNUTLSError("error getting peer"
690
if list_size.value == 0:
693
return ctypes.string_at(cert.data, cert.size)
696
def fingerprint(openpgp):
697
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
698
# New GnuTLS "datum" with the OpenPGP public key
699
datum = (gnutls.library.types
700
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
703
ctypes.c_uint(len(openpgp))))
704
# New empty GnuTLS certificate
705
crt = gnutls.library.types.gnutls_openpgp_crt_t()
706
(gnutls.library.functions
707
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
708
# Import the OpenPGP public key into the certificate
709
(gnutls.library.functions
710
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
711
gnutls.library.constants
712
.GNUTLS_OPENPGP_FMT_RAW))
713
# Verify the self signature in the key
714
crtverify = ctypes.c_uint()
715
(gnutls.library.functions
716
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
717
if crtverify.value != 0:
718
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
719
raise gnutls.errors.CertificateSecurityError("Verify failed")
720
# New buffer for the fingerprint
721
buf = ctypes.create_string_buffer(20)
722
buf_len = ctypes.c_size_t()
723
# Get the fingerprint from the certificate into the buffer
724
(gnutls.library.functions
725
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
726
ctypes.byref(buf_len)))
727
# Deinit the certificate
728
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
729
# Convert the buffer to a Python bytestring
730
fpr = ctypes.string_at(buf, buf_len.value)
731
# Convert the bytestring to hexadecimal notation
732
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
736
class TCP_handler(SocketServer.BaseRequestHandler, object):
737
"""A TCP request handler class.
738
Instantiated by IPv6_TCPServer for each request to handle it.
750
class ClientHandler(socketserver.BaseRequestHandler, object):
751
"""A class to handle client connections.
753
Instantiated once for each connection to handle it.
739
754
Note: This will run in its own forked process."""
741
756
def handle(self):
817
830
- (sent_size + sent))
818
831
sent_size += sent
822
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
823
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
824
Assumes a gobject.MainLoop event loop.
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."""
826
900
def process_request(self, request, client_address):
827
"""This overrides and wraps the original process_request().
901
"""Overrides and wraps the original process_request().
828
903
This function creates a new pipe in self.pipe
830
905
self.pipe = os.pipe()
831
906
super(ForkingMixInWithPipe,
832
907
self).process_request(request, client_address)
833
908
os.close(self.pipe[1]) # close write end
834
# Call "handle_ipc" for both data and EOF events
835
gobject.io_add_watch(self.pipe[0],
836
gobject.IO_IN | gobject.IO_HUP,
838
def handle_ipc(source, condition):
909
self.add_pipe(self.pipe[0])
910
def add_pipe(self, pipe):
839
911
"""Dummy function; override as necessary"""
844
915
class IPv6_TCPServer(ForkingMixInWithPipe,
845
SocketServer.TCPServer, object):
916
socketserver.TCPServer, object):
846
917
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
848
settings: Server settings
849
clients: Set() of Client objects
850
920
enabled: Boolean; whether this server is activated yet
921
interface: None or a network interface name (string)
922
use_ipv6: Boolean; to use IPv6 or not
852
address_family = socket.AF_INET6
853
def __init__(self, *args, **kwargs):
854
if "settings" in kwargs:
855
self.settings = kwargs["settings"]
856
del kwargs["settings"]
857
if "clients" in kwargs:
858
self.clients = kwargs["clients"]
859
del kwargs["clients"]
860
if "use_ipv6" in kwargs:
861
if not kwargs["use_ipv6"]:
862
self.address_family = socket.AF_INET
863
del kwargs["use_ipv6"]
865
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
924
def __init__(self, server_address, RequestHandlerClass,
925
interface=None, use_ipv6=True):
926
self.interface = interface
928
self.address_family = socket.AF_INET6
929
socketserver.TCPServer.__init__(self, server_address,
866
931
def server_bind(self):
867
932
"""This overrides the normal server_bind() function
868
933
to bind to an interface if one was specified, and also NOT to
869
934
bind to an address or port if they were not specified."""
870
if self.settings["interface"]:
871
# 25 is from /usr/include/asm-i486/socket.h
872
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
874
self.socket.setsockopt(socket.SOL_SOCKET,
876
self.settings["interface"])
877
except socket.error, error:
878
if error[0] == errno.EPERM:
879
logger.error(u"No permission to"
880
u" bind to interface %s",
881
self.settings["interface"])
935
if self.interface is not None:
936
if SO_BINDTODEVICE is None:
937
logger.error(u"SO_BINDTODEVICE does not exist;"
938
u" cannot bind to interface %s",
942
self.socket.setsockopt(socket.SOL_SOCKET,
946
except socket.error, error:
947
if error[0] == errno.EPERM:
948
logger.error(u"No permission to"
949
u" bind to interface %s",
951
elif error[0] == errno.ENOPROTOOPT:
952
logger.error(u"SO_BINDTODEVICE not available;"
953
u" cannot bind to interface %s",
884
957
# Only bind(2) the socket if we really need to.
885
958
if self.server_address[0] or self.server_address[1]:
886
959
if not self.server_address[0]:
887
960
if self.address_family == socket.AF_INET6:
888
any_address = "::" # in6addr_any
961
any_address = u"::" # in6addr_any
890
963
any_address = socket.INADDR_ANY
891
964
self.server_address = (any_address,
893
966
elif not self.server_address[1]:
894
967
self.server_address = (self.server_address[0],
896
# if self.settings["interface"]:
897
970
# self.server_address = (self.server_address[0],
903
return super(IPv6_TCPServer, self).server_bind()
975
return socketserver.TCPServer.server_bind(self)
978
class MandosServer(IPv6_TCPServer):
982
clients: set of Client objects
983
gnutls_priority GnuTLS priority string
984
use_dbus: Boolean; to emit D-Bus signals or not
985
clients: set of Client objects
986
gnutls_priority GnuTLS priority string
987
use_dbus: Boolean; to emit D-Bus signals or not
989
Assumes a gobject.MainLoop event loop.
991
def __init__(self, server_address, RequestHandlerClass,
992
interface=None, use_ipv6=True, clients=None,
993
gnutls_priority=None, use_dbus=True):
995
self.clients = clients
996
if self.clients is None:
998
self.use_dbus = use_dbus
999
self.gnutls_priority = gnutls_priority
1000
IPv6_TCPServer.__init__(self, server_address,
1001
RequestHandlerClass,
1002
interface = interface,
1003
use_ipv6 = use_ipv6)
904
1004
def server_activate(self):
905
1005
if self.enabled:
906
return super(IPv6_TCPServer, self).server_activate()
1006
return socketserver.TCPServer.server_activate(self)
907
1007
def enable(self):
908
1008
self.enabled = True
1009
def add_pipe(self, pipe):
1010
# Call "handle_ipc" for both data and EOF events
1011
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
909
1013
def handle_ipc(self, source, condition, file_objects={}):
910
1014
condition_names = {
911
gobject.IO_IN: "IN", # There is data to read.
912
gobject.IO_OUT: "OUT", # Data can be written (without
914
gobject.IO_PRI: "PRI", # There is urgent data to read.
915
gobject.IO_ERR: "ERR", # Error condition.
916
gobject.IO_HUP: "HUP" # Hung up (the connection has been
917
# broken, usually for pipes and
1015
gobject.IO_IN: u"IN", # There is data to read.
1016
gobject.IO_OUT: u"OUT", # Data can be written (without
1018
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1019
gobject.IO_ERR: u"ERR", # Error condition.
1020
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1021
# broken, usually for pipes and
920
1024
conditions_string = ' | '.join(name
921
1025
for cond, name in
922
1026
condition_names.iteritems()
923
1027
if cond & condition)
924
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1028
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
925
1029
conditions_string)
927
1031
# Turn the pipe file descriptor into a Python file object
928
1032
if source not in file_objects:
929
file_objects[source] = os.fdopen(source, "r", 1)
1033
file_objects[source] = os.fdopen(source, u"r", 1)
931
1035
# Read a line from the file object
932
1036
cmdline = file_objects[source].readline()
938
1042
# Stop calling this function
941
logger.debug("IPC command: %r\n" % cmdline)
1045
logger.debug(u"IPC command: %r", cmdline)
943
1047
# Parse and act on command
944
cmd, args = cmdline.split(None, 1)
945
if cmd == "NOTFOUND":
946
if self.settings["use_dbus"]:
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",
947
1054
# Emit D-Bus signal
948
1055
mandos_dbus_service.ClientNotFound(args)
949
elif cmd == "INVALID":
950
if self.settings["use_dbus"]:
951
for client in self.clients:
952
if client.name == 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)
953
1061
# Emit D-Bus signal
954
1062
client.Rejected()
956
elif cmd == "SENDING":
1065
logger.error(u"Unknown client %s is invalid", args)
1066
elif cmd == u"SENDING":
957
1067
for client in self.clients:
958
1068
if client.name == args:
1069
logger.info(u"Sending secret to %s", client.name)
959
1070
client.checked_ok()
960
if self.settings["use_dbus"]:
961
1072
# Emit D-Bus signal
962
1073
client.ReceivedSecret()
1076
logger.error(u"Sending secret to unknown client %s",
965
logger.error("Unknown IPC command: %r", cmdline)
1079
logger.error(u"Unknown IPC command: %r", cmdline)
967
1081
# Keep calling this function
1007
1121
return timevalue
1010
def server_state_changed(state):
1011
"""Derived from the Avahi example code"""
1012
if state == avahi.SERVER_COLLISION:
1013
logger.error(u"Zeroconf server name collision")
1015
elif state == avahi.SERVER_RUNNING:
1019
def entry_group_state_changed(state, error):
1020
"""Derived from the Avahi example code"""
1021
logger.debug(u"Avahi state change: %i", state)
1023
if state == avahi.ENTRY_GROUP_ESTABLISHED:
1024
logger.debug(u"Zeroconf service established.")
1025
elif state == avahi.ENTRY_GROUP_COLLISION:
1026
logger.warning(u"Zeroconf service name collision.")
1028
elif state == avahi.ENTRY_GROUP_FAILURE:
1029
logger.critical(u"Avahi: Error in group state changed %s",
1031
raise AvahiGroupError(u"State changed: %s" % unicode(error))
1033
1124
def if_nametoindex(interface):
1034
"""Call the C function if_nametoindex(), or equivalent"""
1125
"""Call the C function if_nametoindex(), or equivalent
1127
Note: This function cannot accept a unicode string."""
1035
1128
global if_nametoindex
1037
1130
if_nametoindex = (ctypes.cdll.LoadLibrary
1038
(ctypes.util.find_library("c"))
1131
(ctypes.util.find_library(u"c"))
1039
1132
.if_nametoindex)
1040
1133
except (OSError, AttributeError):
1041
if "struct" not in sys.modules:
1043
if "fcntl" not in sys.modules:
1134
logger.warning(u"Doing if_nametoindex the hard way")
1045
1135
def if_nametoindex(interface):
1046
1136
"Get an interface index the hard way, i.e. using fcntl()"
1047
1137
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1048
1138
with closing(socket.socket()) as s:
1049
1139
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1050
struct.pack("16s16x", interface))
1051
interface_index = struct.unpack("I", ifreq[16:20])[0]
1140
struct.pack(str(u"16s16x"),
1142
interface_index = struct.unpack(str(u"I"),
1052
1144
return interface_index
1053
1145
return if_nametoindex(interface)
1056
1148
def daemon(nochdir = False, noclose = False):
1057
1149
"""See daemon(3). Standard BSD Unix function.
1058
1151
This should really exist as os.daemon, but it doesn't (yet)."""
1062
1155
if not nochdir:
1066
1159
if not noclose:
1082
1175
# Parsing of options, both command line and config file
1084
1177
parser = optparse.OptionParser(version = "%%prog %s" % version)
1085
parser.add_option("-i", "--interface", type="string",
1086
metavar="IF", help="Bind to interface IF")
1087
parser.add_option("-a", "--address", type="string",
1088
help="Address to listen for requests on")
1089
parser.add_option("-p", "--port", type="int",
1090
help="Port number to receive requests on")
1091
parser.add_option("--check", action="store_true",
1092
help="Run self-test")
1093
parser.add_option("--debug", action="store_true",
1094
help="Debug mode; run in foreground and log to"
1096
parser.add_option("--priority", type="string", help="GnuTLS"
1097
" priority string (see GnuTLS documentation)")
1098
parser.add_option("--servicename", type="string", metavar="NAME",
1099
help="Zeroconf service name")
1100
parser.add_option("--configdir", type="string",
1101
default="/etc/mandos", metavar="DIR",
1102
help="Directory to search for configuration"
1104
parser.add_option("--no-dbus", action="store_false",
1106
help="Do not provide D-Bus system bus"
1108
parser.add_option("--no-ipv6", action="store_false",
1109
dest="use_ipv6", help="Do not use IPv6")
1178
parser.add_option("-i", u"--interface", type=u"string",
1179
metavar="IF", help=u"Bind to interface IF")
1180
parser.add_option("-a", u"--address", type=u"string",
1181
help=u"Address to listen for requests on")
1182
parser.add_option("-p", u"--port", type=u"int",
1183
help=u"Port number to receive requests on")
1184
parser.add_option("--check", action=u"store_true",
1185
help=u"Run self-test")
1186
parser.add_option("--debug", action=u"store_true",
1187
help=u"Debug mode; run in foreground and log to"
1189
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1190
u" priority string (see GnuTLS documentation)")
1191
parser.add_option("--servicename", type=u"string",
1192
metavar=u"NAME", help=u"Zeroconf service name")
1193
parser.add_option("--configdir", type=u"string",
1194
default=u"/etc/mandos", metavar=u"DIR",
1195
help=u"Directory to search for configuration"
1197
parser.add_option("--no-dbus", action=u"store_false",
1198
dest=u"use_dbus", help=u"Do not provide D-Bus"
1199
u" system bus interface")
1200
parser.add_option("--no-ipv6", action=u"store_false",
1201
dest=u"use_ipv6", help=u"Do not use IPv6")
1110
1202
options = parser.parse_args()[0]
1112
1204
if options.check:
1117
1209
# Default values for config file for server-global settings
1118
server_defaults = { "interface": "",
1123
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1124
"servicename": "Mandos",
1210
server_defaults = { u"interface": u"",
1215
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1216
u"servicename": u"Mandos",
1217
u"use_dbus": u"True",
1218
u"use_ipv6": u"True",
1129
1221
# Parse config file for server-global settings
1130
server_config = ConfigParser.SafeConfigParser(server_defaults)
1222
server_config = configparser.SafeConfigParser(server_defaults)
1131
1223
del server_defaults
1132
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1224
server_config.read(os.path.join(options.configdir,
1133
1226
# Convert the SafeConfigParser object to a dict
1134
1227
server_settings = server_config.defaults()
1135
1228
# Use the appropriate methods on the non-string config options
1136
server_settings["debug"] = server_config.getboolean("DEFAULT",
1138
server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1140
server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1229
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1230
server_settings[option] = server_config.getboolean(u"DEFAULT",
1142
1232
if server_settings["port"]:
1143
server_settings["port"] = server_config.getint("DEFAULT",
1233
server_settings["port"] = server_config.getint(u"DEFAULT",
1145
1235
del server_config
1147
1237
# Override the settings from the config file with command line
1148
1238
# options, if set.
1149
for option in ("interface", "address", "port", "debug",
1150
"priority", "servicename", "configdir",
1151
"use_dbus", "use_ipv6"):
1239
for option in (u"interface", u"address", u"port", u"debug",
1240
u"priority", u"servicename", u"configdir",
1241
u"use_dbus", u"use_ipv6"):
1152
1242
value = getattr(options, option)
1153
1243
if value is not None:
1154
1244
server_settings[option] = value
1246
# Force all strings to be unicode
1247
for option in server_settings.keys():
1248
if type(server_settings[option]) is str:
1249
server_settings[option] = unicode(server_settings[option])
1156
1250
# Now we have our good server settings in "server_settings"
1158
1252
##################################################################
1160
1254
# For convenience
1161
debug = server_settings["debug"]
1162
use_dbus = server_settings["use_dbus"]
1163
use_ipv6 = server_settings["use_ipv6"]
1255
debug = server_settings[u"debug"]
1256
use_dbus = server_settings[u"use_dbus"]
1257
use_ipv6 = server_settings[u"use_ipv6"]
1166
1260
syslogger.setLevel(logging.WARNING)
1167
1261
console.setLevel(logging.WARNING)
1169
if server_settings["servicename"] != "Mandos":
1263
if server_settings[u"servicename"] != u"Mandos":
1170
1264
syslogger.setFormatter(logging.Formatter
1171
('Mandos (%s) [%%(process)d]:'
1172
' %%(levelname)s: %%(message)s'
1173
% server_settings["servicename"]))
1265
(u'Mandos (%s) [%%(process)d]:'
1266
u' %%(levelname)s: %%(message)s'
1267
% server_settings[u"servicename"]))
1175
1269
# Parse config file with clients
1176
client_defaults = { "timeout": "1h",
1178
"checker": "fping -q -- %%(host)s",
1270
client_defaults = { u"timeout": u"1h",
1272
u"checker": u"fping -q -- %%(host)s",
1181
client_config = ConfigParser.SafeConfigParser(client_defaults)
1182
client_config.read(os.path.join(server_settings["configdir"],
1275
client_config = configparser.SafeConfigParser(client_defaults)
1276
client_config.read(os.path.join(server_settings[u"configdir"],
1185
1279
global mandos_dbus_service
1186
1280
mandos_dbus_service = None
1189
tcp_server = IPv6_TCPServer((server_settings["address"],
1190
server_settings["port"]),
1192
settings=server_settings,
1193
clients=clients, use_ipv6=use_ipv6)
1194
pidfilename = "/var/run/mandos.pid"
1282
tcp_server = MandosServer((server_settings[u"address"],
1283
server_settings[u"port"]),
1285
interface=server_settings[u"interface"],
1288
server_settings[u"priority"],
1290
pidfilename = u"/var/run/mandos.pid"
1196
pidfile = open(pidfilename, "w")
1292
pidfile = open(pidfilename, u"w")
1197
1293
except IOError:
1198
logger.error("Could not open file %r", pidfilename)
1294
logger.error(u"Could not open file %r", pidfilename)
1201
uid = pwd.getpwnam("_mandos").pw_uid
1202
gid = pwd.getpwnam("_mandos").pw_gid
1297
uid = pwd.getpwnam(u"_mandos").pw_uid
1298
gid = pwd.getpwnam(u"_mandos").pw_gid
1203
1299
except KeyError:
1205
uid = pwd.getpwnam("mandos").pw_uid
1206
gid = pwd.getpwnam("mandos").pw_gid
1301
uid = pwd.getpwnam(u"mandos").pw_uid
1302
gid = pwd.getpwnam(u"mandos").pw_gid
1207
1303
except KeyError:
1209
uid = pwd.getpwnam("nobody").pw_uid
1210
gid = pwd.getpwnam("nogroup").pw_gid
1305
uid = pwd.getpwnam(u"nobody").pw_uid
1306
gid = pwd.getpwnam(u"nobody").pw_gid
1211
1307
except KeyError:
1227
1323
@gnutls.library.types.gnutls_log_func
1228
1324
def debug_gnutls(level, string):
1229
logger.debug("GnuTLS: %s", string[:-1])
1325
logger.debug(u"GnuTLS: %s", string[:-1])
1231
1327
(gnutls.library.functions
1232
1328
.gnutls_global_set_log_function(debug_gnutls))
1235
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1236
service = AvahiService(name = server_settings["servicename"],
1237
servicetype = "_mandos._tcp",
1238
protocol = protocol)
1239
if server_settings["interface"]:
1240
service.interface = (if_nametoindex
1241
(server_settings["interface"]))
1243
1330
global main_loop
1246
1331
# From the Avahi example code
1247
1332
DBusGMainLoop(set_as_default=True )
1248
1333
main_loop = gobject.MainLoop()
1249
1334
bus = dbus.SystemBus()
1250
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1251
avahi.DBUS_PATH_SERVER),
1252
avahi.DBUS_INTERFACE_SERVER)
1253
1335
# End of Avahi example code
1255
1337
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1338
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1339
service = AvahiService(name = server_settings[u"servicename"],
1340
servicetype = u"_mandos._tcp",
1341
protocol = protocol, bus = bus)
1342
if server_settings["interface"]:
1343
service.interface = (if_nametoindex
1344
(str(server_settings[u"interface"])))
1257
1346
client_class = Client
1259
client_class = ClientDBus
1348
client_class = functools.partial(ClientDBus, bus = bus)
1349
tcp_server.clients.update(set(
1261
1350
client_class(name = section,
1262
1351
config= dict(client_config.items(section)))
1263
1352
for section in client_config.sections()))
1353
if not tcp_server.clients:
1265
1354
logger.warning(u"No clients defined")
1314
1398
class MandosDBusService(dbus.service.Object):
1315
1399
"""A D-Bus proxy object"""
1316
1400
def __init__(self):
1317
dbus.service.Object.__init__(self, bus, "/")
1401
dbus.service.Object.__init__(self, bus, u"/")
1318
1402
_interface = u"se.bsnet.fukt.Mandos"
1320
@dbus.service.signal(_interface, signature="oa{sv}")
1404
@dbus.service.signal(_interface, signature=u"oa{sv}")
1321
1405
def ClientAdded(self, objpath, properties):
1325
@dbus.service.signal(_interface, signature="s")
1409
@dbus.service.signal(_interface, signature=u"s")
1326
1410
def ClientNotFound(self, fingerprint):
1330
@dbus.service.signal(_interface, signature="os")
1414
@dbus.service.signal(_interface, signature=u"os")
1331
1415
def ClientRemoved(self, objpath, name):
1335
@dbus.service.method(_interface, out_signature="ao")
1419
@dbus.service.method(_interface, out_signature=u"ao")
1336
1420
def GetAllClients(self):
1338
return dbus.Array(c.dbus_object_path for c in clients)
1422
return dbus.Array(c.dbus_object_path
1423
for c in tcp_server.clients)
1340
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1425
@dbus.service.method(_interface,
1426
out_signature=u"a{oa{sv}}")
1341
1427
def GetAllClientsWithProperties(self):
1343
1429
return dbus.Dictionary(
1344
1430
((c.dbus_object_path, c.GetAllProperties())
1431
for c in tcp_server.clients),
1432
signature=u"oa{sv}")
1348
@dbus.service.method(_interface, in_signature="o")
1434
@dbus.service.method(_interface, in_signature=u"o")
1349
1435
def RemoveClient(self, object_path):
1437
for c in tcp_server.clients:
1352
1438
if c.dbus_object_path == object_path:
1439
tcp_server.clients.remove(c)
1354
1440
c.remove_from_connection()
1355
1441
# Don't signal anything except ClientRemoved
1356
1442
c.disable(signal=False)