152
133
u" after %i retries, exiting.",
153
134
self.rename_count)
154
135
raise AvahiServiceError(u"Too many renames")
155
self.name = self.server.GetAlternativeServiceName(self.name)
136
self.name = server.GetAlternativeServiceName(self.name)
156
137
logger.info(u"Changing Zeroconf service name to %r ...",
158
139
syslogger.setFormatter(logging.Formatter
159
(u'Mandos (%s) [%%(process)d]:'
160
u' %%(levelname)s: %%(message)s'
140
('Mandos (%s) [%%(process)d]:'
141
' %%(levelname)s: %%(message)s'
164
145
self.rename_count += 1
165
146
def remove(self):
166
147
"""Derived from the Avahi example code"""
167
if self.group is not None:
148
if group is not None:
170
151
"""Derived from the Avahi example code"""
171
if self.group is None:
172
self.group = dbus.Interface(
173
self.bus.get_object(avahi.DBUS_NAME,
174
self.server.EntryGroupNew()),
175
avahi.DBUS_INTERFACE_ENTRY_GROUP)
176
self.group.connect_to_signal('StateChanged',
177
self.entry_group_state_changed)
154
group = dbus.Interface(bus.get_object
156
server.EntryGroupNew()),
157
avahi.DBUS_INTERFACE_ENTRY_GROUP)
158
group.connect_to_signal('StateChanged',
159
entry_group_state_changed)
178
160
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
179
self.name, self.type)
180
self.group.AddService(
183
dbus.UInt32(0), # flags
184
self.name, self.type,
185
self.domain, self.host,
186
dbus.UInt16(self.port),
187
avahi.string_array_to_txt_array(self.TXT))
189
def entry_group_state_changed(self, state, error):
190
"""Derived from the Avahi example code"""
191
logger.debug(u"Avahi state change: %i", state)
193
if state == avahi.ENTRY_GROUP_ESTABLISHED:
194
logger.debug(u"Zeroconf service established.")
195
elif state == avahi.ENTRY_GROUP_COLLISION:
196
logger.warning(u"Zeroconf service name collision.")
198
elif state == avahi.ENTRY_GROUP_FAILURE:
199
logger.critical(u"Avahi: Error in group state changed %s",
201
raise AvahiGroupError(u"State changed: %s"
204
"""Derived from the Avahi example code"""
205
if self.group is not None:
208
def server_state_changed(self, state):
209
"""Derived from the Avahi example code"""
210
if state == avahi.SERVER_COLLISION:
211
logger.error(u"Zeroconf server name collision")
213
elif state == avahi.SERVER_RUNNING:
216
"""Derived from the Avahi example code"""
217
if self.server is None:
218
self.server = dbus.Interface(
219
self.bus.get_object(avahi.DBUS_NAME,
220
avahi.DBUS_PATH_SERVER),
221
avahi.DBUS_INTERFACE_SERVER)
222
self.server.connect_to_signal(u"StateChanged",
223
self.server_state_changed)
224
self.server_state_changed(self.server.GetState())
161
service.name, service.type)
163
self.interface, # interface
164
self.protocol, # protocol
165
dbus.UInt32(0), # flags
166
self.name, self.type,
167
self.domain, self.host,
168
dbus.UInt16(self.port),
169
avahi.string_array_to_txt_array(self.TXT))
172
# From the Avahi example code:
173
group = None # our entry group
174
# End of Avahi example code
177
def _datetime_to_dbus(dt, variant_level=0):
178
"""Convert a UTC datetime.datetime() to a D-Bus type."""
179
return dbus.String(dt.isoformat(), variant_level=variant_level)
227
182
class Client(object):
228
183
"""A representation of a client host served by this server.
231
185
name: string; from the config file, used in log messages and
232
186
D-Bus identifiers
254
208
instance %(name)s can be used in the command.
255
209
current_checker_command: string; current running checker_command
259
def _datetime_to_milliseconds(dt):
260
"Convert a datetime.datetime() to milliseconds"
261
return ((dt.days * 24 * 60 * 60 * 1000)
262
+ (dt.seconds * 1000)
263
+ (dt.microseconds // 1000))
265
211
def timeout_milliseconds(self):
266
212
"Return the 'timeout' attribute in milliseconds"
267
return self._datetime_to_milliseconds(self.timeout)
213
return ((self.timeout.days * 24 * 60 * 60 * 1000)
214
+ (self.timeout.seconds * 1000)
215
+ (self.timeout.microseconds // 1000))
269
217
def interval_milliseconds(self):
270
218
"Return the 'interval' attribute in milliseconds"
271
return self._datetime_to_milliseconds(self.interval)
219
return ((self.interval.days * 24 * 60 * 60 * 1000)
220
+ (self.interval.seconds * 1000)
221
+ (self.interval.microseconds // 1000))
273
223
def __init__(self, name = None, disable_hook=None, config=None):
274
224
"""Note: the 'checker' key in 'config' sets the
281
231
# Uppercase and remove spaces from fingerprint for later
282
232
# comparison purposes with return value from the fingerprint()
284
self.fingerprint = (config[u"fingerprint"].upper()
234
self.fingerprint = (config["fingerprint"].upper()
285
235
.replace(u" ", u""))
286
236
logger.debug(u" Fingerprint: %s", self.fingerprint)
287
if u"secret" in config:
288
self.secret = config[u"secret"].decode(u"base64")
289
elif u"secfile" in config:
237
if "secret" in config:
238
self.secret = config["secret"].decode(u"base64")
239
elif "secfile" in config:
290
240
with closing(open(os.path.expanduser
291
241
(os.path.expandvars
292
(config[u"secfile"])))) as secfile:
242
(config["secfile"])))) as secfile:
293
243
self.secret = secfile.read()
295
245
raise TypeError(u"No secret or secfile for client %s"
297
self.host = config.get(u"host", u"")
247
self.host = config.get("host", "")
298
248
self.created = datetime.datetime.utcnow()
299
249
self.enabled = False
300
250
self.last_enabled = None
301
251
self.last_checked_ok = None
302
self.timeout = string_to_delta(config[u"timeout"])
303
self.interval = string_to_delta(config[u"interval"])
252
self.timeout = string_to_delta(config["timeout"])
253
self.interval = string_to_delta(config["interval"])
304
254
self.disable_hook = disable_hook
305
255
self.checker = None
306
256
self.checker_initiator_tag = None
307
257
self.disable_initiator_tag = None
308
258
self.checker_callback_tag = None
309
self.checker_command = config[u"checker"]
259
self.checker_command = config["checker"]
310
260
self.current_checker_command = None
311
261
self.last_connect = None
477
422
class ClientDBus(Client, dbus.service.Object):
478
423
"""A Client class using D-Bus
481
425
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
483
427
# dbus.service.Object doesn't use super(), so we can't either.
485
def __init__(self, bus = None, *args, **kwargs):
429
def __init__(self, *args, **kwargs):
487
430
Client.__init__(self, *args, **kwargs)
488
431
# Only now, when this client is initialized, can it show up on
490
433
self.dbus_object_path = (dbus.ObjectPath
492
+ self.name.replace(u".", u"_")))
493
dbus.service.Object.__init__(self, self.bus,
435
+ self.name.replace(".", "_")))
436
dbus.service.Object.__init__(self, bus,
494
437
self.dbus_object_path)
497
def _datetime_to_dbus(dt, variant_level=0):
498
"""Convert a UTC datetime.datetime() to a D-Bus type."""
499
return dbus.String(dt.isoformat(),
500
variant_level=variant_level)
502
438
def enable(self):
503
oldstate = getattr(self, u"enabled", False)
439
oldstate = getattr(self, "enabled", False)
504
440
r = Client.enable(self)
505
441
if oldstate != self.enabled:
506
442
# Emit D-Bus signals
507
443
self.PropertyChanged(dbus.String(u"enabled"),
508
444
dbus.Boolean(True, variant_level=1))
509
self.PropertyChanged(
510
dbus.String(u"last_enabled"),
511
self._datetime_to_dbus(self.last_enabled,
445
self.PropertyChanged(dbus.String(u"last_enabled"),
446
(_datetime_to_dbus(self.last_enabled,
515
450
def disable(self, signal = True):
516
oldstate = getattr(self, u"enabled", False)
451
oldstate = getattr(self, "enabled", False)
517
452
r = Client.disable(self)
518
453
if signal and oldstate != self.enabled:
519
454
# Emit D-Bus signal
574
509
# Emit D-Bus signal
575
510
self.CheckerStarted(self.current_checker_command)
576
511
self.PropertyChanged(
577
dbus.String(u"checker_running"),
512
dbus.String("checker_running"),
578
513
dbus.Boolean(True, variant_level=1))
581
516
def stop_checker(self, *args, **kwargs):
582
old_checker = getattr(self, u"checker", None)
517
old_checker = getattr(self, "checker", None)
583
518
r = Client.stop_checker(self, *args, **kwargs)
584
519
if (old_checker is not None
585
and getattr(self, u"checker", None) is None):
520
and getattr(self, "checker", None) is None):
586
521
self.PropertyChanged(dbus.String(u"checker_running"),
587
522
dbus.Boolean(False, variant_level=1))
591
526
_interface = u"se.bsnet.fukt.Mandos.Client"
593
528
# CheckedOK - method
594
@dbus.service.method(_interface)
596
return self.checked_ok()
529
CheckedOK = dbus.service.method(_interface)(checked_ok)
530
CheckedOK.__name__ = "CheckedOK"
598
532
# CheckerCompleted - signal
599
@dbus.service.signal(_interface, signature=u"nxs")
533
@dbus.service.signal(_interface, signature="nxs")
600
534
def CheckerCompleted(self, exitcode, waitstatus, command):
604
538
# CheckerStarted - signal
605
@dbus.service.signal(_interface, signature=u"s")
539
@dbus.service.signal(_interface, signature="s")
606
540
def CheckerStarted(self, command):
610
544
# GetAllProperties - method
611
@dbus.service.method(_interface, out_signature=u"a{sv}")
545
@dbus.service.method(_interface, out_signature="a{sv}")
612
546
def GetAllProperties(self):
614
548
return dbus.Dictionary({
615
dbus.String(u"name"):
616
550
dbus.String(self.name, variant_level=1),
617
dbus.String(u"fingerprint"):
551
dbus.String("fingerprint"):
618
552
dbus.String(self.fingerprint, variant_level=1),
619
dbus.String(u"host"):
620
554
dbus.String(self.host, variant_level=1),
621
dbus.String(u"created"):
622
self._datetime_to_dbus(self.created,
624
dbus.String(u"last_enabled"):
625
(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,
627
560
if self.last_enabled is not None
628
561
else dbus.Boolean(False, variant_level=1)),
629
dbus.String(u"enabled"):
562
dbus.String("enabled"):
630
563
dbus.Boolean(self.enabled, variant_level=1),
631
dbus.String(u"last_checked_ok"):
632
(self._datetime_to_dbus(self.last_checked_ok,
564
dbus.String("last_checked_ok"):
565
(_datetime_to_dbus(self.last_checked_ok,
634
567
if self.last_checked_ok is not None
635
568
else dbus.Boolean (False, variant_level=1)),
636
dbus.String(u"timeout"):
569
dbus.String("timeout"):
637
570
dbus.UInt64(self.timeout_milliseconds(),
638
571
variant_level=1),
639
dbus.String(u"interval"):
572
dbus.String("interval"):
640
573
dbus.UInt64(self.interval_milliseconds(),
641
574
variant_level=1),
642
dbus.String(u"checker"):
575
dbus.String("checker"):
643
576
dbus.String(self.checker_command,
644
577
variant_level=1),
645
dbus.String(u"checker_running"):
578
dbus.String("checker_running"):
646
579
dbus.Boolean(self.checker is not None,
647
580
variant_level=1),
648
dbus.String(u"object_path"):
581
dbus.String("object_path"):
649
582
dbus.ObjectPath(self.dbus_object_path,
653
586
# IsStillValid - method
654
@dbus.service.method(_interface, out_signature=u"b")
587
@dbus.service.method(_interface, out_signature="b")
655
588
def IsStillValid(self):
656
589
return self.still_valid()
658
591
# PropertyChanged - signal
659
@dbus.service.signal(_interface, signature=u"sv")
592
@dbus.service.signal(_interface, signature="sv")
660
593
def PropertyChanged(self, property, value):
738
669
# StopChecker - method
739
@dbus.service.method(_interface)
740
def StopChecker(self):
670
StopChecker = dbus.service.method(_interface)(stop_checker)
671
StopChecker.__name__ = "StopChecker"
746
class ClientHandler(socketserver.BaseRequestHandler, object):
747
"""A class to handle client connections.
749
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.
750
740
Note: This will run in its own forked process."""
752
742
def handle(self):
774
764
# no X.509 keys are added to it. Therefore, we can use it
775
765
# here despite using OpenPGP certificates.
777
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
778
# u"+AES-256-CBC", u"+SHA1",
779
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
767
#priority = ':'.join(("NONE", "+VERS-TLS1.1",
768
# "+AES-256-CBC", "+SHA1",
769
# "+COMP-NULL", "+CTYPE-OPENPGP",
781
771
# Use a fallback default, since this MUST be set.
782
priority = self.server.gnutls_priority
772
priority = self.server.settings.get("priority", "NORMAL")
785
773
(gnutls.library.functions
786
774
.gnutls_priority_set_direct(session._c_object,
826
818
- (sent_size + sent))
827
819
sent_size += sent
831
def peer_certificate(session):
832
"Return the peer's OpenPGP certificate as a bytestring"
833
# If not an OpenPGP certificate...
834
if (gnutls.library.functions
835
.gnutls_certificate_type_get(session._c_object)
836
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
837
# ...do the normal thing
838
return session.peer_certificate
839
list_size = ctypes.c_uint(1)
840
cert_list = (gnutls.library.functions
841
.gnutls_certificate_get_peers
842
(session._c_object, ctypes.byref(list_size)))
843
if not bool(cert_list) and list_size.value != 0:
844
raise gnutls.errors.GNUTLSError(u"error getting peer"
846
if list_size.value == 0:
849
return ctypes.string_at(cert.data, cert.size)
852
def fingerprint(openpgp):
853
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
854
# New GnuTLS "datum" with the OpenPGP public key
855
datum = (gnutls.library.types
856
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
859
ctypes.c_uint(len(openpgp))))
860
# New empty GnuTLS certificate
861
crt = gnutls.library.types.gnutls_openpgp_crt_t()
862
(gnutls.library.functions
863
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
864
# Import the OpenPGP public key into the certificate
865
(gnutls.library.functions
866
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
867
gnutls.library.constants
868
.GNUTLS_OPENPGP_FMT_RAW))
869
# Verify the self signature in the key
870
crtverify = ctypes.c_uint()
871
(gnutls.library.functions
872
.gnutls_openpgp_crt_verify_self(crt, 0,
873
ctypes.byref(crtverify)))
874
if crtverify.value != 0:
875
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
876
raise (gnutls.errors.CertificateSecurityError
878
# New buffer for the fingerprint
879
buf = ctypes.create_string_buffer(20)
880
buf_len = ctypes.c_size_t()
881
# Get the fingerprint from the certificate into the buffer
882
(gnutls.library.functions
883
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
884
ctypes.byref(buf_len)))
885
# Deinit the certificate
886
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
887
# Convert the buffer to a Python bytestring
888
fpr = ctypes.string_at(buf, buf_len.value)
889
# Convert the bytestring to hexadecimal notation
890
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
894
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
895
"""Like socketserver.ForkingMixIn, but also pass a pipe.
823
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
824
"""Like SocketServer.ForkingMixIn, but also pass a pipe.
897
825
Assumes a gobject.MainLoop event loop.
899
827
def process_request(self, request, client_address):
900
"""Overrides and wraps the original process_request().
828
"""This overrides and wraps the original process_request().
902
829
This function creates a new pipe in self.pipe
904
831
self.pipe = os.pipe()
918
845
class IPv6_TCPServer(ForkingMixInWithPipe,
919
socketserver.TCPServer, object):
846
SocketServer.TCPServer, object):
920
847
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
849
settings: Server settings
850
clients: Set() of Client objects
923
851
enabled: Boolean; whether this server is activated yet
924
interface: None or a network interface name (string)
925
use_ipv6: Boolean; to use IPv6 or not
927
clients: set of Client objects
928
gnutls_priority GnuTLS priority string
929
use_dbus: Boolean; to emit D-Bus signals or not
931
def __init__(self, server_address, RequestHandlerClass,
932
interface=None, use_ipv6=True, clients=None,
933
gnutls_priority=None, use_dbus=True):
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"]
934
865
self.enabled = False
935
self.interface = interface
937
self.address_family = socket.AF_INET6
938
self.clients = clients
939
self.use_dbus = use_dbus
940
self.gnutls_priority = gnutls_priority
941
socketserver.TCPServer.__init__(self, server_address,
866
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
943
867
def server_bind(self):
944
868
"""This overrides the normal server_bind() function
945
869
to bind to an interface if one was specified, and also NOT to
946
870
bind to an address or port if they were not specified."""
947
if self.interface is not None:
871
if self.settings["interface"]:
872
# 25 is from /usr/include/asm-i486/socket.h
873
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
949
875
self.socket.setsockopt(socket.SOL_SOCKET,
951
str(self.interface + u'\0'))
877
self.settings["interface"])
952
878
except socket.error, error:
953
879
if error[0] == errno.EPERM:
954
880
logger.error(u"No permission to"
955
881
u" bind to interface %s",
882
self.settings["interface"])
959
885
# Only bind(2) the socket if we really need to.
960
886
if self.server_address[0] or self.server_address[1]:
961
887
if not self.server_address[0]:
962
888
if self.address_family == socket.AF_INET6:
963
any_address = u"::" # in6addr_any
889
any_address = "::" # in6addr_any
965
891
any_address = socket.INADDR_ANY
966
892
self.server_address = (any_address,
968
894
elif not self.server_address[1]:
969
895
self.server_address = (self.server_address[0],
897
# if self.settings["interface"]:
972
898
# self.server_address = (self.server_address[0],
977
return socketserver.TCPServer.server_bind(self)
904
return super(IPv6_TCPServer, self).server_bind()
978
905
def server_activate(self):
980
return socketserver.TCPServer.server_activate(self)
907
return super(IPv6_TCPServer, self).server_activate()
981
908
def enable(self):
982
909
self.enabled = True
983
910
def handle_ipc(self, source, condition, file_objects={}):
984
911
condition_names = {
985
gobject.IO_IN: u"IN", # There is data to read.
986
gobject.IO_OUT: u"OUT", # Data can be written (without
988
gobject.IO_PRI: u"PRI", # There is urgent data to read.
989
gobject.IO_ERR: u"ERR", # Error condition.
990
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
991
# 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
994
921
conditions_string = ' | '.join(name
995
922
for cond, name in
996
923
condition_names.iteritems()
997
924
if cond & condition)
998
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
925
logger.debug("Handling IPC: FD = %d, condition = %s", source,
999
926
conditions_string)
1001
928
# Turn the pipe file descriptor into a Python file object
1002
929
if source not in file_objects:
1003
file_objects[source] = os.fdopen(source, u"r", 1)
930
file_objects[source] = os.fdopen(source, "r", 1)
1005
932
# Read a line from the file object
1006
933
cmdline = file_objects[source].readline()
1012
939
# Stop calling this function
1015
logger.debug(u"IPC command: %r", cmdline)
942
logger.debug("IPC command: %r\n" % cmdline)
1017
944
# Parse and act on command
1018
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1020
if cmd == u"NOTFOUND":
1021
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"]:
1024
948
# Emit D-Bus signal
1025
949
mandos_dbus_service.ClientNotFound(args)
1026
elif cmd == u"INVALID":
1027
for client in self.clients:
1028
if client.name == args:
1029
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:
1031
954
# Emit D-Bus signal
1032
955
client.Rejected()
1035
logger.error(u"Unknown client %s is invalid", args)
1036
elif cmd == u"SENDING":
957
elif cmd == "SENDING":
1037
958
for client in self.clients:
1038
959
if client.name == args:
1039
logger.info(u"Sending secret to %s", client.name)
1040
960
client.checked_ok()
961
if self.settings["use_dbus"]:
1042
962
# Emit D-Bus signal
1043
963
client.ReceivedSecret()
1046
logger.error(u"Sending secret to unknown client %s",
1049
logger.error(u"Unknown IPC command: %r", cmdline)
966
logger.error("Unknown IPC command: %r", cmdline)
1051
968
# Keep calling this function
1091
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))
1094
1034
def if_nametoindex(interface):
1095
"""Call the C function if_nametoindex(), or equivalent
1097
Note: This function cannot accept a unicode string."""
1035
"""Call the C function if_nametoindex(), or equivalent"""
1098
1036
global if_nametoindex
1100
1038
if_nametoindex = (ctypes.cdll.LoadLibrary
1101
(ctypes.util.find_library(u"c"))
1039
(ctypes.util.find_library("c"))
1102
1040
.if_nametoindex)
1103
1041
except (OSError, AttributeError):
1104
logger.warning(u"Doing if_nametoindex the hard way")
1042
if "struct" not in sys.modules:
1044
if "fcntl" not in sys.modules:
1105
1046
def if_nametoindex(interface):
1106
1047
"Get an interface index the hard way, i.e. using fcntl()"
1107
1048
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1108
1049
with closing(socket.socket()) as s:
1109
1050
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1110
struct.pack(str(u"16s16x"),
1112
interface_index = struct.unpack(str(u"I"),
1051
struct.pack("16s16x", interface))
1052
interface_index = struct.unpack("I", ifreq[16:20])[0]
1114
1053
return interface_index
1115
1054
return if_nametoindex(interface)
1118
1057
def daemon(nochdir = False, noclose = False):
1119
1058
"""See daemon(3). Standard BSD Unix function.
1121
1059
This should really exist as os.daemon, but it doesn't (yet)."""
1125
1063
if not nochdir:
1129
1067
if not noclose:
1145
1083
# Parsing of options, both command line and config file
1147
1085
parser = optparse.OptionParser(version = "%%prog %s" % version)
1148
parser.add_option("-i", u"--interface", type=u"string",
1149
metavar="IF", help=u"Bind to interface IF")
1150
parser.add_option("-a", u"--address", type=u"string",
1151
help=u"Address to listen for requests on")
1152
parser.add_option("-p", u"--port", type=u"int",
1153
help=u"Port number to receive requests on")
1154
parser.add_option("--check", action=u"store_true",
1155
help=u"Run self-test")
1156
parser.add_option("--debug", action=u"store_true",
1157
help=u"Debug mode; run in foreground and log to"
1159
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1160
u" priority string (see GnuTLS documentation)")
1161
parser.add_option("--servicename", type=u"string",
1162
metavar=u"NAME", help=u"Zeroconf service name")
1163
parser.add_option("--configdir", type=u"string",
1164
default=u"/etc/mandos", metavar=u"DIR",
1165
help=u"Directory to search for configuration"
1167
parser.add_option("--no-dbus", action=u"store_false",
1168
dest=u"use_dbus", help=u"Do not provide D-Bus"
1169
u" system bus interface")
1170
parser.add_option("--no-ipv6", action=u"store_false",
1171
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")
1172
1111
options = parser.parse_args()[0]
1174
1113
if options.check:
1179
1118
# Default values for config file for server-global settings
1180
server_defaults = { u"interface": u"",
1185
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1186
u"servicename": u"Mandos",
1187
u"use_dbus": u"True",
1188
u"use_ipv6": u"True",
1119
server_defaults = { "interface": "",
1124
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1125
"servicename": "Mandos",
1191
1130
# Parse config file for server-global settings
1192
server_config = configparser.SafeConfigParser(server_defaults)
1131
server_config = ConfigParser.SafeConfigParser(server_defaults)
1193
1132
del server_defaults
1194
server_config.read(os.path.join(options.configdir,
1133
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1196
1134
# Convert the SafeConfigParser object to a dict
1197
1135
server_settings = server_config.defaults()
1198
1136
# Use the appropriate methods on the non-string config options
1199
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1200
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",
1202
1143
if server_settings["port"]:
1203
server_settings["port"] = server_config.getint(u"DEFAULT",
1144
server_settings["port"] = server_config.getint("DEFAULT",
1205
1146
del server_config
1207
1148
# Override the settings from the config file with command line
1208
1149
# options, if set.
1209
for option in (u"interface", u"address", u"port", u"debug",
1210
u"priority", u"servicename", u"configdir",
1211
u"use_dbus", u"use_ipv6"):
1150
for option in ("interface", "address", "port", "debug",
1151
"priority", "servicename", "configdir",
1152
"use_dbus", "use_ipv6"):
1212
1153
value = getattr(options, option)
1213
1154
if value is not None:
1214
1155
server_settings[option] = value
1216
# Force all strings to be unicode
1217
for option in server_settings.keys():
1218
if type(server_settings[option]) is str:
1219
server_settings[option] = unicode(server_settings[option])
1220
1157
# Now we have our good server settings in "server_settings"
1222
1159
##################################################################
1224
1161
# For convenience
1225
debug = server_settings[u"debug"]
1226
use_dbus = server_settings[u"use_dbus"]
1227
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"]
1230
1167
syslogger.setLevel(logging.WARNING)
1231
1168
console.setLevel(logging.WARNING)
1233
if server_settings[u"servicename"] != u"Mandos":
1170
if server_settings["servicename"] != "Mandos":
1234
1171
syslogger.setFormatter(logging.Formatter
1235
(u'Mandos (%s) [%%(process)d]:'
1236
u' %%(levelname)s: %%(message)s'
1237
% server_settings[u"servicename"]))
1172
('Mandos (%s) [%%(process)d]:'
1173
' %%(levelname)s: %%(message)s'
1174
% server_settings["servicename"]))
1239
1176
# Parse config file with clients
1240
client_defaults = { u"timeout": u"1h",
1242
u"checker": u"fping -q -- %%(host)s",
1177
client_defaults = { "timeout": "1h",
1179
"checker": "fping -q -- %%(host)s",
1245
client_config = configparser.SafeConfigParser(client_defaults)
1246
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"],
1249
1186
global mandos_dbus_service
1250
1187
mandos_dbus_service = None
1253
tcp_server = IPv6_TCPServer((server_settings[u"address"],
1254
server_settings[u"port"]),
1257
server_settings[u"interface"],
1261
server_settings[u"priority"],
1263
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"
1265
pidfile = open(pidfilename, u"w")
1197
pidfile = open(pidfilename, "w")
1266
1198
except IOError:
1267
logger.error(u"Could not open file %r", pidfilename)
1199
logger.error("Could not open file %r", pidfilename)
1270
uid = pwd.getpwnam(u"_mandos").pw_uid
1271
gid = pwd.getpwnam(u"_mandos").pw_gid
1202
uid = pwd.getpwnam("_mandos").pw_uid
1203
gid = pwd.getpwnam("_mandos").pw_gid
1272
1204
except KeyError:
1274
uid = pwd.getpwnam(u"mandos").pw_uid
1275
gid = pwd.getpwnam(u"mandos").pw_gid
1206
uid = pwd.getpwnam("mandos").pw_uid
1207
gid = pwd.getpwnam("mandos").pw_gid
1276
1208
except KeyError:
1278
uid = pwd.getpwnam(u"nobody").pw_uid
1279
gid = pwd.getpwnam(u"nobody").pw_gid
1210
uid = pwd.getpwnam("nobody").pw_uid
1211
gid = pwd.getpwnam("nogroup").pw_gid
1280
1212
except KeyError:
1296
1228
@gnutls.library.types.gnutls_log_func
1297
1229
def debug_gnutls(level, string):
1298
logger.debug(u"GnuTLS: %s", string[:-1])
1230
logger.debug("GnuTLS: %s", string[:-1])
1300
1232
(gnutls.library.functions
1301
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"]))
1303
1244
global main_loop
1304
1247
# From the Avahi example code
1305
1248
DBusGMainLoop(set_as_default=True )
1306
1249
main_loop = gobject.MainLoop()
1307
1250
bus = dbus.SystemBus()
1251
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1252
avahi.DBUS_PATH_SERVER),
1253
avahi.DBUS_INTERFACE_SERVER)
1308
1254
# End of Avahi example code
1310
1256
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1311
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1312
service = AvahiService(name = server_settings[u"servicename"],
1313
servicetype = u"_mandos._tcp",
1314
protocol = protocol, bus = bus)
1315
if server_settings["interface"]:
1316
service.interface = (if_nametoindex
1317
(str(server_settings[u"interface"])))
1319
1258
client_class = Client
1321
client_class = functools.partial(ClientDBus, bus = bus)
1260
client_class = ClientDBus
1323
1262
client_class(name = section,
1324
1263
config= dict(client_config.items(section)))
1325
1264
for section in client_config.sections()))
1371
1315
class MandosDBusService(dbus.service.Object):
1372
1316
"""A D-Bus proxy object"""
1373
1317
def __init__(self):
1374
dbus.service.Object.__init__(self, bus, u"/")
1318
dbus.service.Object.__init__(self, bus, "/")
1375
1319
_interface = u"se.bsnet.fukt.Mandos"
1377
@dbus.service.signal(_interface, signature=u"oa{sv}")
1321
@dbus.service.signal(_interface, signature="oa{sv}")
1378
1322
def ClientAdded(self, objpath, properties):
1382
@dbus.service.signal(_interface, signature=u"s")
1326
@dbus.service.signal(_interface, signature="s")
1383
1327
def ClientNotFound(self, fingerprint):
1387
@dbus.service.signal(_interface, signature=u"os")
1331
@dbus.service.signal(_interface, signature="os")
1388
1332
def ClientRemoved(self, objpath, name):
1392
@dbus.service.method(_interface, out_signature=u"ao")
1336
@dbus.service.method(_interface, out_signature="ao")
1393
1337
def GetAllClients(self):
1395
1339
return dbus.Array(c.dbus_object_path for c in clients)
1397
@dbus.service.method(_interface,
1398
out_signature=u"a{oa{sv}}")
1341
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1399
1342
def GetAllClientsWithProperties(self):
1401
1344
return dbus.Dictionary(
1402
1345
((c.dbus_object_path, c.GetAllProperties())
1403
1346
for c in clients),
1404
signature=u"oa{sv}")
1406
@dbus.service.method(_interface, in_signature=u"o")
1349
@dbus.service.method(_interface, in_signature="o")
1407
1350
def RemoveClient(self, object_path):
1409
1352
for c in clients: