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
573
503
old_checker_pid = None
574
504
r = Client.start_checker(self, *args, **kwargs)
575
# Only if new checker process was started
576
if (self.checker is not None
577
and old_checker_pid != self.checker.pid):
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)):
579
509
self.CheckerStarted(self.current_checker_command)
580
510
self.PropertyChanged(
581
dbus.String(u"checker_running"),
511
dbus.String("checker_running"),
582
512
dbus.Boolean(True, variant_level=1))
585
515
def stop_checker(self, *args, **kwargs):
586
old_checker = getattr(self, u"checker", None)
516
old_checker = getattr(self, "checker", None)
587
517
r = Client.stop_checker(self, *args, **kwargs)
588
518
if (old_checker is not None
589
and getattr(self, u"checker", None) is None):
519
and getattr(self, "checker", None) is None):
590
520
self.PropertyChanged(dbus.String(u"checker_running"),
591
521
dbus.Boolean(False, variant_level=1))
595
525
_interface = u"se.bsnet.fukt.Mandos.Client"
597
527
# CheckedOK - method
598
@dbus.service.method(_interface)
600
return self.checked_ok()
528
CheckedOK = dbus.service.method(_interface)(checked_ok)
529
CheckedOK.__name__ = "CheckedOK"
602
531
# CheckerCompleted - signal
603
@dbus.service.signal(_interface, signature=u"nxs")
532
@dbus.service.signal(_interface, signature="nxs")
604
533
def CheckerCompleted(self, exitcode, waitstatus, command):
608
537
# CheckerStarted - signal
609
@dbus.service.signal(_interface, signature=u"s")
538
@dbus.service.signal(_interface, signature="s")
610
539
def CheckerStarted(self, command):
614
543
# GetAllProperties - method
615
@dbus.service.method(_interface, out_signature=u"a{sv}")
544
@dbus.service.method(_interface, out_signature="a{sv}")
616
545
def GetAllProperties(self):
618
547
return dbus.Dictionary({
619
dbus.String(u"name"):
620
549
dbus.String(self.name, variant_level=1),
621
dbus.String(u"fingerprint"):
550
dbus.String("fingerprint"):
622
551
dbus.String(self.fingerprint, variant_level=1),
623
dbus.String(u"host"):
624
553
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,
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,
631
559
if self.last_enabled is not None
632
560
else dbus.Boolean(False, variant_level=1)),
633
dbus.String(u"enabled"):
561
dbus.String("enabled"):
634
562
dbus.Boolean(self.enabled, variant_level=1),
635
dbus.String(u"last_checked_ok"):
636
(self._datetime_to_dbus(self.last_checked_ok,
563
dbus.String("last_checked_ok"):
564
(_datetime_to_dbus(self.last_checked_ok,
638
566
if self.last_checked_ok is not None
639
567
else dbus.Boolean (False, variant_level=1)),
640
dbus.String(u"timeout"):
568
dbus.String("timeout"):
641
569
dbus.UInt64(self.timeout_milliseconds(),
642
570
variant_level=1),
643
dbus.String(u"interval"):
571
dbus.String("interval"):
644
572
dbus.UInt64(self.interval_milliseconds(),
645
573
variant_level=1),
646
dbus.String(u"checker"):
574
dbus.String("checker"):
647
575
dbus.String(self.checker_command,
648
576
variant_level=1),
649
dbus.String(u"checker_running"):
577
dbus.String("checker_running"):
650
578
dbus.Boolean(self.checker is not None,
651
579
variant_level=1),
652
dbus.String(u"object_path"):
580
dbus.String("object_path"):
653
581
dbus.ObjectPath(self.dbus_object_path,
657
585
# IsStillValid - method
658
@dbus.service.method(_interface, out_signature=u"b")
586
@dbus.service.method(_interface, out_signature="b")
659
587
def IsStillValid(self):
660
588
return self.still_valid()
662
590
# PropertyChanged - signal
663
@dbus.service.signal(_interface, signature=u"sv")
591
@dbus.service.signal(_interface, signature="sv")
664
592
def PropertyChanged(self, property, value):
742
668
# StopChecker - method
743
@dbus.service.method(_interface)
744
def StopChecker(self):
669
StopChecker = dbus.service.method(_interface)(stop_checker)
670
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.
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.
754
739
Note: This will run in its own forked process."""
756
741
def handle(self):
830
817
- (sent_size + sent))
831
818
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."""
822
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
823
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
824
Assumes a gobject.MainLoop event loop.
900
826
def process_request(self, request, client_address):
901
"""Overrides and wraps the original process_request().
827
"""This overrides and wraps the original process_request().
903
828
This function creates a new pipe in self.pipe
905
830
self.pipe = os.pipe()
906
831
super(ForkingMixInWithPipe,
907
832
self).process_request(request, client_address)
908
833
os.close(self.pipe[1]) # close write end
909
self.add_pipe(self.pipe[0])
910
def add_pipe(self, pipe):
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):
911
839
"""Dummy function; override as necessary"""
915
844
class IPv6_TCPServer(ForkingMixInWithPipe,
916
socketserver.TCPServer, object):
845
SocketServer.TCPServer, object):
917
846
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
848
settings: Server settings
849
clients: Set() of Client objects
920
850
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,
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)
931
866
def server_bind(self):
932
867
"""This overrides the normal server_bind() function
933
868
to bind to an interface if one was specified, and also NOT to
934
869
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",
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"])
957
884
# Only bind(2) the socket if we really need to.
958
885
if self.server_address[0] or self.server_address[1]:
959
886
if not self.server_address[0]:
960
887
if self.address_family == socket.AF_INET6:
961
any_address = u"::" # in6addr_any
888
any_address = "::" # in6addr_any
963
890
any_address = socket.INADDR_ANY
964
891
self.server_address = (any_address,
966
893
elif not self.server_address[1]:
967
894
self.server_address = (self.server_address[0],
896
# if self.settings["interface"]:
970
897
# 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)
903
return super(IPv6_TCPServer, self).server_bind()
1004
904
def server_activate(self):
1005
905
if self.enabled:
1006
return socketserver.TCPServer.server_activate(self)
906
return super(IPv6_TCPServer, self).server_activate()
1007
907
def enable(self):
1008
908
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
909
def handle_ipc(self, source, condition, file_objects={}):
1014
910
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
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
1024
920
conditions_string = ' | '.join(name
1025
921
for cond, name in
1026
922
condition_names.iteritems()
1027
923
if cond & condition)
1028
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
924
logger.debug("Handling IPC: FD = %d, condition = %s", source,
1029
925
conditions_string)
1031
927
# Turn the pipe file descriptor into a Python file object
1032
928
if source not in file_objects:
1033
file_objects[source] = os.fdopen(source, u"r", 1)
929
file_objects[source] = os.fdopen(source, "r", 1)
1035
931
# Read a line from the file object
1036
932
cmdline = file_objects[source].readline()
1042
938
# Stop calling this function
1045
logger.debug(u"IPC command: %r", cmdline)
941
logger.debug("IPC command: %r\n" % cmdline)
1047
943
# 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",
944
cmd, args = cmdline.split(None, 1)
945
if cmd == "NOTFOUND":
946
if self.settings["use_dbus"]:
1054
947
# Emit D-Bus signal
1055
948
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)
949
elif cmd == "INVALID":
950
if self.settings["use_dbus"]:
951
for client in self.clients:
952
if client.name == args:
1061
953
# Emit D-Bus signal
1062
954
client.Rejected()
1065
logger.error(u"Unknown client %s is invalid", args)
1066
elif cmd == u"SENDING":
956
elif cmd == "SENDING":
1067
957
for client in self.clients:
1068
958
if client.name == args:
1069
logger.info(u"Sending secret to %s", client.name)
1070
959
client.checked_ok()
960
if self.settings["use_dbus"]:
1072
961
# Emit D-Bus signal
1073
962
client.ReceivedSecret()
1076
logger.error(u"Sending secret to unknown client %s",
1079
logger.error(u"Unknown IPC command: %r", cmdline)
965
logger.error("Unknown IPC command: %r", cmdline)
1081
967
# Keep calling this function
1121
1007
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))
1124
1033
def if_nametoindex(interface):
1125
"""Call the C function if_nametoindex(), or equivalent
1127
Note: This function cannot accept a unicode string."""
1034
"""Call the C function if_nametoindex(), or equivalent"""
1128
1035
global if_nametoindex
1130
1037
if_nametoindex = (ctypes.cdll.LoadLibrary
1131
(ctypes.util.find_library(u"c"))
1038
(ctypes.util.find_library("c"))
1132
1039
.if_nametoindex)
1133
1040
except (OSError, AttributeError):
1134
logger.warning(u"Doing if_nametoindex the hard way")
1041
if "struct" not in sys.modules:
1043
if "fcntl" not in sys.modules:
1135
1045
def if_nametoindex(interface):
1136
1046
"Get an interface index the hard way, i.e. using fcntl()"
1137
1047
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1138
1048
with closing(socket.socket()) as s:
1139
1049
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1140
struct.pack(str(u"16s16x"),
1142
interface_index = struct.unpack(str(u"I"),
1050
struct.pack("16s16x", interface))
1051
interface_index = struct.unpack("I", ifreq[16:20])[0]
1144
1052
return interface_index
1145
1053
return if_nametoindex(interface)
1148
1056
def daemon(nochdir = False, noclose = False):
1149
1057
"""See daemon(3). Standard BSD Unix function.
1151
1058
This should really exist as os.daemon, but it doesn't (yet)."""
1155
1062
if not nochdir:
1159
1066
if not noclose:
1175
1082
# Parsing of options, both command line and config file
1177
1084
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")
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")
1202
1110
options = parser.parse_args()[0]
1204
1112
if options.check:
1209
1117
# 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",
1118
server_defaults = { "interface": "",
1123
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1124
"servicename": "Mandos",
1221
1129
# Parse config file for server-global settings
1222
server_config = configparser.SafeConfigParser(server_defaults)
1130
server_config = ConfigParser.SafeConfigParser(server_defaults)
1223
1131
del server_defaults
1224
server_config.read(os.path.join(options.configdir,
1132
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1226
1133
# Convert the SafeConfigParser object to a dict
1227
1134
server_settings = server_config.defaults()
1228
1135
# 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",
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",
1232
1142
if server_settings["port"]:
1233
server_settings["port"] = server_config.getint(u"DEFAULT",
1143
server_settings["port"] = server_config.getint("DEFAULT",
1235
1145
del server_config
1237
1147
# Override the settings from the config file with command line
1238
1148
# 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"):
1149
for option in ("interface", "address", "port", "debug",
1150
"priority", "servicename", "configdir",
1151
"use_dbus", "use_ipv6"):
1242
1152
value = getattr(options, option)
1243
1153
if value is not None:
1244
1154
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
1156
# Now we have our good server settings in "server_settings"
1252
1158
##################################################################
1254
1160
# For convenience
1255
debug = server_settings[u"debug"]
1256
use_dbus = server_settings[u"use_dbus"]
1257
use_ipv6 = server_settings[u"use_ipv6"]
1161
debug = server_settings["debug"]
1162
use_dbus = server_settings["use_dbus"]
1163
use_ipv6 = server_settings["use_ipv6"]
1260
1166
syslogger.setLevel(logging.WARNING)
1261
1167
console.setLevel(logging.WARNING)
1263
if server_settings[u"servicename"] != u"Mandos":
1169
if server_settings["servicename"] != "Mandos":
1264
1170
syslogger.setFormatter(logging.Formatter
1265
(u'Mandos (%s) [%%(process)d]:'
1266
u' %%(levelname)s: %%(message)s'
1267
% server_settings[u"servicename"]))
1171
('Mandos (%s) [%%(process)d]:'
1172
' %%(levelname)s: %%(message)s'
1173
% server_settings["servicename"]))
1269
1175
# Parse config file with clients
1270
client_defaults = { u"timeout": u"1h",
1272
u"checker": u"fping -q -- %%(host)s",
1176
client_defaults = { "timeout": "1h",
1178
"checker": "fping -q -- %%(host)s",
1275
client_config = configparser.SafeConfigParser(client_defaults)
1276
client_config.read(os.path.join(server_settings[u"configdir"],
1181
client_config = ConfigParser.SafeConfigParser(client_defaults)
1182
client_config.read(os.path.join(server_settings["configdir"],
1279
1185
global mandos_dbus_service
1280
1186
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"
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"
1292
pidfile = open(pidfilename, u"w")
1196
pidfile = open(pidfilename, "w")
1293
1197
except IOError:
1294
logger.error(u"Could not open file %r", pidfilename)
1198
logger.error("Could not open file %r", pidfilename)
1297
uid = pwd.getpwnam(u"_mandos").pw_uid
1298
gid = pwd.getpwnam(u"_mandos").pw_gid
1201
uid = pwd.getpwnam("_mandos").pw_uid
1202
gid = pwd.getpwnam("_mandos").pw_gid
1299
1203
except KeyError:
1301
uid = pwd.getpwnam(u"mandos").pw_uid
1302
gid = pwd.getpwnam(u"mandos").pw_gid
1205
uid = pwd.getpwnam("mandos").pw_uid
1206
gid = pwd.getpwnam("mandos").pw_gid
1303
1207
except KeyError:
1305
uid = pwd.getpwnam(u"nobody").pw_uid
1306
gid = pwd.getpwnam(u"nobody").pw_gid
1209
uid = pwd.getpwnam("nobody").pw_uid
1210
gid = pwd.getpwnam("nogroup").pw_gid
1307
1211
except KeyError:
1323
1227
@gnutls.library.types.gnutls_log_func
1324
1228
def debug_gnutls(level, string):
1325
logger.debug(u"GnuTLS: %s", string[:-1])
1229
logger.debug("GnuTLS: %s", string[:-1])
1327
1231
(gnutls.library.functions
1328
1232
.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"]))
1330
1243
global main_loop
1331
1246
# From the Avahi example code
1332
1247
DBusGMainLoop(set_as_default=True )
1333
1248
main_loop = gobject.MainLoop()
1334
1249
bus = dbus.SystemBus()
1250
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1251
avahi.DBUS_PATH_SERVER),
1252
avahi.DBUS_INTERFACE_SERVER)
1335
1253
# End of Avahi example code
1337
1255
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
1257
client_class = Client
1348
client_class = functools.partial(ClientDBus, bus = bus)
1349
tcp_server.clients.update(set(
1259
client_class = ClientDBus
1350
1261
client_class(name = section,
1351
1262
config= dict(client_config.items(section)))
1352
1263
for section in client_config.sections()))
1353
if not tcp_server.clients:
1354
1265
logger.warning(u"No clients defined")
1398
1314
class MandosDBusService(dbus.service.Object):
1399
1315
"""A D-Bus proxy object"""
1400
1316
def __init__(self):
1401
dbus.service.Object.__init__(self, bus, u"/")
1317
dbus.service.Object.__init__(self, bus, "/")
1402
1318
_interface = u"se.bsnet.fukt.Mandos"
1404
@dbus.service.signal(_interface, signature=u"oa{sv}")
1320
@dbus.service.signal(_interface, signature="oa{sv}")
1405
1321
def ClientAdded(self, objpath, properties):
1409
@dbus.service.signal(_interface, signature=u"s")
1325
@dbus.service.signal(_interface, signature="s")
1410
1326
def ClientNotFound(self, fingerprint):
1414
@dbus.service.signal(_interface, signature=u"os")
1330
@dbus.service.signal(_interface, signature="os")
1415
1331
def ClientRemoved(self, objpath, name):
1419
@dbus.service.method(_interface, out_signature=u"ao")
1335
@dbus.service.method(_interface, out_signature="ao")
1420
1336
def GetAllClients(self):
1422
return dbus.Array(c.dbus_object_path
1423
for c in tcp_server.clients)
1338
return dbus.Array(c.dbus_object_path for c in clients)
1425
@dbus.service.method(_interface,
1426
out_signature=u"a{oa{sv}}")
1340
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1427
1341
def GetAllClientsWithProperties(self):
1429
1343
return dbus.Dictionary(
1430
1344
((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")
1348
@dbus.service.method(_interface, in_signature="o")
1435
1349
def RemoveClient(self, object_path):
1437
for c in tcp_server.clients:
1438
1352
if c.dbus_object_path == object_path:
1439
tcp_server.clients.remove(c)
1440
1354
c.remove_from_connection()
1441
1355
# Don't signal anything except ClientRemoved
1442
1356
c.disable(signal=False)