152
130
u" after %i retries, exiting.",
153
131
self.rename_count)
154
132
raise AvahiServiceError(u"Too many renames")
155
self.name = self.server.GetAlternativeServiceName(self.name)
133
self.name = server.GetAlternativeServiceName(self.name)
156
134
logger.info(u"Changing Zeroconf service name to %r ...",
158
136
syslogger.setFormatter(logging.Formatter
159
(u'Mandos (%s) [%%(process)d]:'
160
u' %%(levelname)s: %%(message)s'
137
('Mandos (%s): %%(levelname)s:'
138
' %%(message)s' % self.name))
164
141
self.rename_count += 1
165
142
def remove(self):
166
143
"""Derived from the Avahi example code"""
167
if self.group is not None:
144
if group is not None:
170
147
"""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)
150
group = dbus.Interface(bus.get_object
152
server.EntryGroupNew()),
153
avahi.DBUS_INTERFACE_ENTRY_GROUP)
154
group.connect_to_signal('StateChanged',
155
entry_group_state_changed)
178
156
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())
227
class Client(object):
157
service.name, service.type)
159
self.interface, # interface
160
avahi.PROTO_INET6, # protocol
161
dbus.UInt32(0), # flags
162
self.name, self.type,
163
self.domain, self.host,
164
dbus.UInt16(self.port),
165
avahi.string_array_to_txt_array(self.TXT))
168
# From the Avahi example code:
169
group = None # our entry group
170
# End of Avahi example code
173
def _datetime_to_dbus(dt, variant_level=0):
174
"""Convert a UTC datetime.datetime() to a D-Bus type."""
175
return dbus.String(dt.isoformat(), variant_level=variant_level)
178
class Client(dbus.service.Object):
228
179
"""A representation of a client host served by this server.
231
name: string; from the config file, used in log messages and
181
name: string; from the config file, used in log messages
233
182
fingerprint: string (40 or 32 hexadecimal digits); used to
234
183
uniquely identify the client
235
184
secret: bytestring; sent verbatim (over TLS) to client
462
433
if error.errno != errno.ESRCH: # No such process
464
435
self.checker = None
437
self.PropertyChanged(dbus.String(u"checker_running"),
438
dbus.Boolean(False, variant_level=1))
466
440
def still_valid(self):
467
441
"""Has the timeout not yet passed for this client?"""
468
if not getattr(self, u"enabled", False):
442
if not getattr(self, "enabled", False):
470
444
now = datetime.datetime.utcnow()
471
445
if self.last_checked_ok is None:
472
446
return now < (self.created + self.timeout)
474
448
return now < (self.last_checked_ok + self.timeout)
477
class ClientDBus(Client, dbus.service.Object):
478
"""A Client class using D-Bus
481
dbus_object_path: dbus.ObjectPath
482
bus: dbus.SystemBus()
484
# dbus.service.Object doesn't use super(), so we can't either.
486
def __init__(self, bus = None, *args, **kwargs):
488
Client.__init__(self, *args, **kwargs)
489
# Only now, when this client is initialized, can it show up on
491
self.dbus_object_path = (dbus.ObjectPath
493
+ self.name.replace(u".", u"_")))
494
dbus.service.Object.__init__(self, self.bus,
495
self.dbus_object_path)
498
def _datetime_to_dbus(dt, variant_level=0):
499
"""Convert a UTC datetime.datetime() to a D-Bus type."""
500
return dbus.String(dt.isoformat(),
501
variant_level=variant_level)
504
oldstate = getattr(self, u"enabled", False)
505
r = Client.enable(self)
506
if oldstate != self.enabled:
508
self.PropertyChanged(dbus.String(u"enabled"),
509
dbus.Boolean(True, variant_level=1))
510
self.PropertyChanged(
511
dbus.String(u"last_enabled"),
512
self._datetime_to_dbus(self.last_enabled,
516
def disable(self, signal = True):
517
oldstate = getattr(self, u"enabled", False)
518
r = Client.disable(self)
519
if signal and oldstate != self.enabled:
521
self.PropertyChanged(dbus.String(u"enabled"),
522
dbus.Boolean(False, variant_level=1))
525
def __del__(self, *args, **kwargs):
527
self.remove_from_connection()
530
if hasattr(dbus.service.Object, u"__del__"):
531
dbus.service.Object.__del__(self, *args, **kwargs)
532
Client.__del__(self, *args, **kwargs)
534
def checker_callback(self, pid, condition, command,
536
self.checker_callback_tag = None
539
self.PropertyChanged(dbus.String(u"checker_running"),
540
dbus.Boolean(False, variant_level=1))
541
if os.WIFEXITED(condition):
542
exitstatus = os.WEXITSTATUS(condition)
544
self.CheckerCompleted(dbus.Int16(exitstatus),
545
dbus.Int64(condition),
546
dbus.String(command))
549
self.CheckerCompleted(dbus.Int16(-1),
550
dbus.Int64(condition),
551
dbus.String(command))
553
return Client.checker_callback(self, pid, condition, command,
556
def checked_ok(self, *args, **kwargs):
557
r = Client.checked_ok(self, *args, **kwargs)
559
self.PropertyChanged(
560
dbus.String(u"last_checked_ok"),
561
(self._datetime_to_dbus(self.last_checked_ok,
565
def start_checker(self, *args, **kwargs):
566
old_checker = self.checker
567
if self.checker is not None:
568
old_checker_pid = self.checker.pid
570
old_checker_pid = None
571
r = Client.start_checker(self, *args, **kwargs)
572
# Only if new checker process was started
573
if (self.checker is not None
574
and old_checker_pid != self.checker.pid):
576
self.CheckerStarted(self.current_checker_command)
577
self.PropertyChanged(
578
dbus.String(u"checker_running"),
579
dbus.Boolean(True, variant_level=1))
582
def stop_checker(self, *args, **kwargs):
583
old_checker = getattr(self, u"checker", None)
584
r = Client.stop_checker(self, *args, **kwargs)
585
if (old_checker is not None
586
and getattr(self, u"checker", None) is None):
587
self.PropertyChanged(dbus.String(u"checker_running"),
588
dbus.Boolean(False, variant_level=1))
591
450
## D-Bus methods & signals
592
_interface = u"se.bsnet.fukt.Mandos.Client"
451
_interface = u"org.mandos_system.Mandos.Client"
595
@dbus.service.method(_interface)
597
return self.checked_ok()
453
# BumpTimeout - method
454
BumpTimeout = dbus.service.method(_interface)(bump_timeout)
455
BumpTimeout.__name__ = "BumpTimeout"
599
457
# CheckerCompleted - signal
600
@dbus.service.signal(_interface, signature=u"nxs")
601
def CheckerCompleted(self, exitcode, waitstatus, command):
458
@dbus.service.signal(_interface, signature="bqs")
459
def CheckerCompleted(self, success, condition, command):
605
463
# CheckerStarted - signal
606
@dbus.service.signal(_interface, signature=u"s")
464
@dbus.service.signal(_interface, signature="s")
607
465
def CheckerStarted(self, command):
611
469
# GetAllProperties - method
612
@dbus.service.method(_interface, out_signature=u"a{sv}")
470
@dbus.service.method(_interface, out_signature="a{sv}")
613
471
def GetAllProperties(self):
615
473
return dbus.Dictionary({
616
dbus.String(u"name"):
617
475
dbus.String(self.name, variant_level=1),
618
dbus.String(u"fingerprint"):
476
dbus.String("fingerprint"):
619
477
dbus.String(self.fingerprint, variant_level=1),
620
dbus.String(u"host"):
621
479
dbus.String(self.host, variant_level=1),
622
dbus.String(u"created"):
623
self._datetime_to_dbus(self.created,
625
dbus.String(u"last_enabled"):
626
(self._datetime_to_dbus(self.last_enabled,
480
dbus.String("created"):
481
_datetime_to_dbus(self.created, variant_level=1),
482
dbus.String("last_enabled"):
483
(_datetime_to_dbus(self.last_enabled,
628
485
if self.last_enabled is not None
629
486
else dbus.Boolean(False, variant_level=1)),
630
dbus.String(u"enabled"):
487
dbus.String("enabled"):
631
488
dbus.Boolean(self.enabled, variant_level=1),
632
dbus.String(u"last_checked_ok"):
633
(self._datetime_to_dbus(self.last_checked_ok,
489
dbus.String("last_checked_ok"):
490
(_datetime_to_dbus(self.last_checked_ok,
635
492
if self.last_checked_ok is not None
636
493
else dbus.Boolean (False, variant_level=1)),
637
dbus.String(u"timeout"):
494
dbus.String("timeout"):
638
495
dbus.UInt64(self.timeout_milliseconds(),
639
496
variant_level=1),
640
dbus.String(u"interval"):
497
dbus.String("interval"):
641
498
dbus.UInt64(self.interval_milliseconds(),
642
499
variant_level=1),
643
dbus.String(u"checker"):
500
dbus.String("checker"):
644
501
dbus.String(self.checker_command,
645
502
variant_level=1),
646
dbus.String(u"checker_running"):
503
dbus.String("checker_running"):
647
504
dbus.Boolean(self.checker is not None,
648
505
variant_level=1),
649
dbus.String(u"object_path"):
650
dbus.ObjectPath(self.dbus_object_path,
654
508
# IsStillValid - method
655
@dbus.service.method(_interface, out_signature=u"b")
656
def IsStillValid(self):
657
return self.still_valid()
509
IsStillValid = (dbus.service.method(_interface, out_signature="b")
511
IsStillValid.__name__ = "IsStillValid"
659
513
# PropertyChanged - signal
660
@dbus.service.signal(_interface, signature=u"sv")
514
@dbus.service.signal(_interface, signature="sv")
661
515
def PropertyChanged(self, property, value):
665
# ReceivedSecret - signal
666
@dbus.service.signal(_interface)
667
def ReceivedSecret(self):
672
@dbus.service.signal(_interface)
677
519
# SetChecker - method
678
@dbus.service.method(_interface, in_signature=u"s")
520
@dbus.service.method(_interface, in_signature="s")
679
521
def SetChecker(self, checker):
680
522
"D-Bus setter method"
681
523
self.checker_command = checker
739
579
# StopChecker - method
740
@dbus.service.method(_interface)
741
def StopChecker(self):
580
StopChecker = dbus.service.method(_interface)(stop_checker)
581
StopChecker.__name__ = "StopChecker"
747
class ClientHandler(socketserver.BaseRequestHandler, object):
748
"""A class to handle client connections.
750
Instantiated once for each connection to handle it.
586
def peer_certificate(session):
587
"Return the peer's OpenPGP certificate as a bytestring"
588
# If not an OpenPGP certificate...
589
if (gnutls.library.functions
590
.gnutls_certificate_type_get(session._c_object)
591
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
592
# ...do the normal thing
593
return session.peer_certificate
594
list_size = ctypes.c_uint()
595
cert_list = (gnutls.library.functions
596
.gnutls_certificate_get_peers
597
(session._c_object, ctypes.byref(list_size)))
598
if list_size.value == 0:
601
return ctypes.string_at(cert.data, cert.size)
604
def fingerprint(openpgp):
605
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
606
# New GnuTLS "datum" with the OpenPGP public key
607
datum = (gnutls.library.types
608
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
611
ctypes.c_uint(len(openpgp))))
612
# New empty GnuTLS certificate
613
crt = gnutls.library.types.gnutls_openpgp_crt_t()
614
(gnutls.library.functions
615
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
616
# Import the OpenPGP public key into the certificate
617
(gnutls.library.functions
618
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
619
gnutls.library.constants
620
.GNUTLS_OPENPGP_FMT_RAW))
621
# Verify the self signature in the key
622
crtverify = ctypes.c_uint()
623
(gnutls.library.functions
624
.gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
625
if crtverify.value != 0:
626
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
627
raise gnutls.errors.CertificateSecurityError("Verify failed")
628
# New buffer for the fingerprint
629
buf = ctypes.create_string_buffer(20)
630
buf_len = ctypes.c_size_t()
631
# Get the fingerprint from the certificate into the buffer
632
(gnutls.library.functions
633
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
634
ctypes.byref(buf_len)))
635
# Deinit the certificate
636
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
637
# Convert the buffer to a Python bytestring
638
fpr = ctypes.string_at(buf, buf_len.value)
639
# Convert the bytestring to hexadecimal notation
640
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
644
class TCP_handler(SocketServer.BaseRequestHandler, object):
645
"""A TCP request handler class.
646
Instantiated by IPv6_TCPServer for each request to handle it.
751
647
Note: This will run in its own forked process."""
753
649
def handle(self):
754
650
logger.info(u"TCP connection from: %s",
755
651
unicode(self.client_address))
756
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
757
# Open IPC pipe to parent process
758
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
759
session = (gnutls.connection
760
.ClientSession(self.request,
764
line = self.request.makefile().readline()
765
logger.debug(u"Protocol version: %r", line)
767
if int(line.strip().split()[0]) > 1:
769
except (ValueError, IndexError, RuntimeError), error:
770
logger.error(u"Unknown protocol version: %s", error)
773
# Note: gnutls.connection.X509Credentials is really a
774
# generic GnuTLS certificate credentials object so long as
775
# no X.509 keys are added to it. Therefore, we can use it
776
# here despite using OpenPGP certificates.
778
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
779
# u"+AES-256-CBC", u"+SHA1",
780
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
782
# Use a fallback default, since this MUST be set.
783
priority = self.server.gnutls_priority
786
(gnutls.library.functions
787
.gnutls_priority_set_direct(session._c_object,
792
except gnutls.errors.GNUTLSError, error:
793
logger.warning(u"Handshake failed: %s", error)
794
# Do not run session.bye() here: the session is not
795
# established. Just abandon the request.
797
logger.debug(u"Handshake succeeded")
799
fpr = self.fingerprint(self.peer_certificate(session))
800
except (TypeError, gnutls.errors.GNUTLSError), error:
801
logger.warning(u"Bad certificate: %s", error)
804
logger.debug(u"Fingerprint: %s", fpr)
806
for c in self.server.clients:
807
if c.fingerprint == fpr:
811
ipc.write(u"NOTFOUND %s\n" % fpr)
814
# Have to check if client.still_valid(), since it is
815
# possible that the client timed out while establishing
816
# the GnuTLS session.
817
if not client.still_valid():
818
ipc.write(u"INVALID %s\n" % client.name)
821
ipc.write(u"SENDING %s\n" % client.name)
823
while sent_size < len(client.secret):
824
sent = session.send(client.secret[sent_size:])
825
logger.debug(u"Sent: %d, remaining: %d",
826
sent, len(client.secret)
827
- (sent_size + sent))
832
def peer_certificate(session):
833
"Return the peer's OpenPGP certificate as a bytestring"
834
# If not an OpenPGP certificate...
835
if (gnutls.library.functions
836
.gnutls_certificate_type_get(session._c_object)
837
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
838
# ...do the normal thing
839
return session.peer_certificate
840
list_size = ctypes.c_uint(1)
841
cert_list = (gnutls.library.functions
842
.gnutls_certificate_get_peers
843
(session._c_object, ctypes.byref(list_size)))
844
if not bool(cert_list) and list_size.value != 0:
845
raise gnutls.errors.GNUTLSError(u"error getting peer"
847
if list_size.value == 0:
850
return ctypes.string_at(cert.data, cert.size)
853
def fingerprint(openpgp):
854
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
855
# New GnuTLS "datum" with the OpenPGP public key
856
datum = (gnutls.library.types
857
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
860
ctypes.c_uint(len(openpgp))))
861
# New empty GnuTLS certificate
862
crt = gnutls.library.types.gnutls_openpgp_crt_t()
863
(gnutls.library.functions
864
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
865
# Import the OpenPGP public key into the certificate
866
(gnutls.library.functions
867
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
868
gnutls.library.constants
869
.GNUTLS_OPENPGP_FMT_RAW))
870
# Verify the self signature in the key
871
crtverify = ctypes.c_uint()
872
(gnutls.library.functions
873
.gnutls_openpgp_crt_verify_self(crt, 0,
874
ctypes.byref(crtverify)))
875
if crtverify.value != 0:
876
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
877
raise (gnutls.errors.CertificateSecurityError
879
# New buffer for the fingerprint
880
buf = ctypes.create_string_buffer(20)
881
buf_len = ctypes.c_size_t()
882
# Get the fingerprint from the certificate into the buffer
883
(gnutls.library.functions
884
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
885
ctypes.byref(buf_len)))
886
# Deinit the certificate
887
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
888
# Convert the buffer to a Python bytestring
889
fpr = ctypes.string_at(buf, buf_len.value)
890
# Convert the bytestring to hexadecimal notation
891
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
895
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
896
"""Like socketserver.ForkingMixIn, but also pass a pipe."""
897
def process_request(self, request, client_address):
898
"""Overrides and wraps the original process_request().
900
This function creates a new pipe in self.pipe
902
self.pipe = os.pipe()
903
super(ForkingMixInWithPipe,
904
self).process_request(request, client_address)
905
os.close(self.pipe[1]) # close write end
906
self.add_pipe(self.pipe[0])
907
def add_pipe(self, pipe):
908
"""Dummy function; override as necessary"""
912
class IPv6_TCPServer(ForkingMixInWithPipe,
913
socketserver.TCPServer, object):
914
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
652
session = (gnutls.connection
653
.ClientSession(self.request,
657
line = self.request.makefile().readline()
658
logger.debug(u"Protocol version: %r", line)
660
if int(line.strip().split()[0]) > 1:
662
except (ValueError, IndexError, RuntimeError), error:
663
logger.error(u"Unknown protocol version: %s", error)
666
# Note: gnutls.connection.X509Credentials is really a generic
667
# GnuTLS certificate credentials object so long as no X.509
668
# keys are added to it. Therefore, we can use it here despite
669
# using OpenPGP certificates.
671
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
672
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
674
# Use a fallback default, since this MUST be set.
675
priority = self.server.settings.get("priority", "NORMAL")
676
(gnutls.library.functions
677
.gnutls_priority_set_direct(session._c_object,
682
except gnutls.errors.GNUTLSError, error:
683
logger.warning(u"Handshake failed: %s", error)
684
# Do not run session.bye() here: the session is not
685
# established. Just abandon the request.
688
fpr = fingerprint(peer_certificate(session))
689
except (TypeError, gnutls.errors.GNUTLSError), error:
690
logger.warning(u"Bad certificate: %s", error)
693
logger.debug(u"Fingerprint: %s", fpr)
694
for c in self.server.clients:
695
if c.fingerprint == fpr:
699
logger.warning(u"Client not found for fingerprint: %s",
703
# Have to check if client.still_valid(), since it is possible
704
# that the client timed out while establishing the GnuTLS
706
if not client.still_valid():
707
logger.warning(u"Client %(name)s is invalid",
711
## This won't work here, since we're in a fork.
712
# client.bump_timeout()
714
while sent_size < len(client.secret):
715
sent = session.send(client.secret[sent_size:])
716
logger.debug(u"Sent: %d, remaining: %d",
717
sent, len(client.secret)
718
- (sent_size + sent))
723
class IPv6_TCPServer(SocketServer.ForkingMixIn,
724
SocketServer.TCPServer, object):
725
"""IPv6 TCP server. Accepts 'None' as address and/or port.
727
settings: Server settings
728
clients: Set() of Client objects
917
729
enabled: Boolean; whether this server is activated yet
918
interface: None or a network interface name (string)
919
use_ipv6: Boolean; to use IPv6 or not
921
def __init__(self, server_address, RequestHandlerClass,
922
interface=None, use_ipv6=True):
923
self.interface = interface
925
self.address_family = socket.AF_INET6
926
socketserver.TCPServer.__init__(self, server_address,
731
address_family = socket.AF_INET6
732
def __init__(self, *args, **kwargs):
733
if "settings" in kwargs:
734
self.settings = kwargs["settings"]
735
del kwargs["settings"]
736
if "clients" in kwargs:
737
self.clients = kwargs["clients"]
738
del kwargs["clients"]
740
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
928
741
def server_bind(self):
929
742
"""This overrides the normal server_bind() function
930
743
to bind to an interface if one was specified, and also NOT to
931
744
bind to an address or port if they were not specified."""
932
if self.interface is not None:
933
if SO_BINDTODEVICE is None:
934
logger.error(u"SO_BINDTODEVICE does not exist;"
935
u" cannot bind to interface %s",
939
self.socket.setsockopt(socket.SOL_SOCKET,
943
except socket.error, error:
944
if error[0] == errno.EPERM:
945
logger.error(u"No permission to"
946
u" bind to interface %s",
948
elif error[0] == errno.ENOPROTOOPT:
949
logger.error(u"SO_BINDTODEVICE not available;"
950
u" cannot bind to interface %s",
745
if self.settings["interface"]:
746
# 25 is from /usr/include/asm-i486/socket.h
747
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
749
self.socket.setsockopt(socket.SOL_SOCKET,
751
self.settings["interface"])
752
except socket.error, error:
753
if error[0] == errno.EPERM:
754
logger.error(u"No permission to"
755
u" bind to interface %s",
756
self.settings["interface"])
954
759
# Only bind(2) the socket if we really need to.
955
760
if self.server_address[0] or self.server_address[1]:
956
761
if not self.server_address[0]:
957
if self.address_family == socket.AF_INET6:
958
any_address = u"::" # in6addr_any
960
any_address = socket.INADDR_ANY
961
self.server_address = (any_address,
763
self.server_address = (in6addr_any,
962
764
self.server_address[1])
963
765
elif not self.server_address[1]:
964
766
self.server_address = (self.server_address[0],
768
# if self.settings["interface"]:
967
769
# self.server_address = (self.server_address[0],
972
return socketserver.TCPServer.server_bind(self)
975
class MandosServer(IPv6_TCPServer):
979
clients: set of Client objects
980
gnutls_priority GnuTLS priority string
981
use_dbus: Boolean; to emit D-Bus signals or not
982
clients: set of Client objects
983
gnutls_priority GnuTLS priority string
984
use_dbus: Boolean; to emit D-Bus signals or not
986
Assumes a gobject.MainLoop event loop.
988
def __init__(self, server_address, RequestHandlerClass,
989
interface=None, use_ipv6=True, clients=None,
990
gnutls_priority=None, use_dbus=True):
992
self.clients = clients
993
self.use_dbus = use_dbus
994
self.gnutls_priority = gnutls_priority
995
IPv6_TCPServer.__init__(self, server_address,
997
interface = interface,
775
return super(IPv6_TCPServer, self).server_bind()
999
776
def server_activate(self):
1000
777
if self.enabled:
1001
return socketserver.TCPServer.server_activate(self)
778
return super(IPv6_TCPServer, self).server_activate()
1002
779
def enable(self):
1003
780
self.enabled = True
1004
def add_pipe(self, pipe):
1005
# Call "handle_ipc" for both data and EOF events
1006
gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1008
def handle_ipc(self, source, condition, file_objects={}):
1010
gobject.IO_IN: u"IN", # There is data to read.
1011
gobject.IO_OUT: u"OUT", # Data can be written (without
1013
gobject.IO_PRI: u"PRI", # There is urgent data to read.
1014
gobject.IO_ERR: u"ERR", # Error condition.
1015
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
1016
# broken, usually for pipes and
1019
conditions_string = ' | '.join(name
1021
condition_names.iteritems()
1022
if cond & condition)
1023
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1026
# Turn the pipe file descriptor into a Python file object
1027
if source not in file_objects:
1028
file_objects[source] = os.fdopen(source, u"r", 1)
1030
# Read a line from the file object
1031
cmdline = file_objects[source].readline()
1032
if not cmdline: # Empty line means end of file
1033
# close the IPC pipe
1034
file_objects[source].close()
1035
del file_objects[source]
1037
# Stop calling this function
1040
logger.debug(u"IPC command: %r", cmdline)
1042
# Parse and act on command
1043
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1045
if cmd == u"NOTFOUND":
1046
logger.warning(u"Client not found for fingerprint: %s",
1050
mandos_dbus_service.ClientNotFound(args)
1051
elif cmd == u"INVALID":
1052
for client in self.clients:
1053
if client.name == args:
1054
logger.warning(u"Client %s is invalid", args)
1060
logger.error(u"Unknown client %s is invalid", args)
1061
elif cmd == u"SENDING":
1062
for client in self.clients:
1063
if client.name == args:
1064
logger.info(u"Sending secret to %s", client.name)
1068
client.ReceivedSecret()
1071
logger.error(u"Sending secret to unknown client %s",
1074
logger.error(u"Unknown IPC command: %r", cmdline)
1076
# Keep calling this function
1080
783
def string_to_delta(interval):
1081
784
"""Parse a string and return a datetime.timedelta
1083
>>> string_to_delta(u'7d')
786
>>> string_to_delta('7d')
1084
787
datetime.timedelta(7)
1085
>>> string_to_delta(u'60s')
788
>>> string_to_delta('60s')
1086
789
datetime.timedelta(0, 60)
1087
>>> string_to_delta(u'60m')
790
>>> string_to_delta('60m')
1088
791
datetime.timedelta(0, 3600)
1089
>>> string_to_delta(u'24h')
792
>>> string_to_delta('24h')
1090
793
datetime.timedelta(1)
1091
794
>>> string_to_delta(u'1w')
1092
795
datetime.timedelta(7)
1093
>>> string_to_delta(u'5m 30s')
796
>>> string_to_delta('5m 30s')
1094
797
datetime.timedelta(0, 330)
1096
799
timevalue = datetime.timedelta(0)
1204
923
# Default values for config file for server-global settings
1205
server_defaults = { u"interface": u"",
1210
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1211
u"servicename": u"Mandos",
1212
u"use_dbus": u"True",
1213
u"use_ipv6": u"True",
924
server_defaults = { "interface": "",
929
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
930
"servicename": "Mandos",
1216
934
# Parse config file for server-global settings
1217
server_config = configparser.SafeConfigParser(server_defaults)
935
server_config = ConfigParser.SafeConfigParser(server_defaults)
1218
936
del server_defaults
1219
server_config.read(os.path.join(options.configdir,
937
server_config.read(os.path.join(options.configdir, "mandos.conf"))
1221
938
# Convert the SafeConfigParser object to a dict
1222
939
server_settings = server_config.defaults()
1223
# Use the appropriate methods on the non-string config options
1224
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1225
server_settings[option] = server_config.getboolean(u"DEFAULT",
1227
if server_settings["port"]:
1228
server_settings["port"] = server_config.getint(u"DEFAULT",
940
# Use getboolean on the boolean config options
941
server_settings["debug"] = (server_config.getboolean
942
("DEFAULT", "debug"))
943
server_settings["use_dbus"] = (server_config.getboolean
944
("DEFAULT", "use_dbus"))
1230
945
del server_config
1232
947
# Override the settings from the config file with command line
1233
948
# options, if set.
1234
for option in (u"interface", u"address", u"port", u"debug",
1235
u"priority", u"servicename", u"configdir",
1236
u"use_dbus", u"use_ipv6"):
949
for option in ("interface", "address", "port", "debug",
950
"priority", "servicename", "configdir",
1237
952
value = getattr(options, option)
1238
953
if value is not None:
1239
954
server_settings[option] = value
1241
# Force all strings to be unicode
1242
for option in server_settings.keys():
1243
if type(server_settings[option]) is str:
1244
server_settings[option] = unicode(server_settings[option])
1245
956
# Now we have our good server settings in "server_settings"
1247
##################################################################
1249
958
# For convenience
1250
debug = server_settings[u"debug"]
1251
use_dbus = server_settings[u"use_dbus"]
1252
use_ipv6 = server_settings[u"use_ipv6"]
959
debug = server_settings["debug"]
960
use_dbus = server_settings["use_dbus"]
1255
963
syslogger.setLevel(logging.WARNING)
1256
964
console.setLevel(logging.WARNING)
1258
if server_settings[u"servicename"] != u"Mandos":
966
if server_settings["servicename"] != "Mandos":
1259
967
syslogger.setFormatter(logging.Formatter
1260
(u'Mandos (%s) [%%(process)d]:'
1261
u' %%(levelname)s: %%(message)s'
1262
% server_settings[u"servicename"]))
968
('Mandos (%s): %%(levelname)s:'
970
% server_settings["servicename"]))
1264
972
# Parse config file with clients
1265
client_defaults = { u"timeout": u"1h",
1267
u"checker": u"fping -q -- %%(host)s",
973
client_defaults = { "timeout": "1h",
975
"checker": "fping -q -- %(host)s",
1270
client_config = configparser.SafeConfigParser(client_defaults)
1271
client_config.read(os.path.join(server_settings[u"configdir"],
1274
global mandos_dbus_service
1275
mandos_dbus_service = None
1278
tcp_server = MandosServer((server_settings[u"address"],
1279
server_settings[u"port"]),
1281
interface=server_settings[u"interface"],
1285
server_settings[u"priority"],
1287
pidfilename = u"/var/run/mandos.pid"
1289
pidfile = open(pidfilename, u"w")
1291
logger.error(u"Could not open file %r", pidfilename)
1294
uid = pwd.getpwnam(u"_mandos").pw_uid
1295
gid = pwd.getpwnam(u"_mandos").pw_gid
978
client_config = ConfigParser.SafeConfigParser(client_defaults)
979
client_config.read(os.path.join(server_settings["configdir"],
983
tcp_server = IPv6_TCPServer((server_settings["address"],
984
server_settings["port"]),
986
settings=server_settings,
988
pidfilename = "/var/run/mandos.pid"
990
pidfile = open(pidfilename, "w")
991
except IOError, error:
992
logger.error("Could not open file %r", pidfilename)
995
uid = pwd.getpwnam("_mandos").pw_uid
1296
996
except KeyError:
1298
uid = pwd.getpwnam(u"mandos").pw_uid
1299
gid = pwd.getpwnam(u"mandos").pw_gid
998
uid = pwd.getpwnam("mandos").pw_uid
1300
999
except KeyError:
1302
uid = pwd.getpwnam(u"nobody").pw_uid
1303
gid = pwd.getpwnam(u"nobody").pw_gid
1001
uid = pwd.getpwnam("nobody").pw_uid
1304
1002
except KeyError:
1005
gid = pwd.getpwnam("_mandos").pw_gid
1008
gid = pwd.getpwnam("mandos").pw_gid
1011
gid = pwd.getpwnam("nogroup").pw_gid
1310
1017
except OSError, error:
1311
1018
if error[0] != errno.EPERM:
1314
# Enable all possible GnuTLS debugging
1316
# "Use a log level over 10 to enable all debugging options."
1318
gnutls.library.functions.gnutls_global_set_log_level(11)
1320
@gnutls.library.types.gnutls_log_func
1321
def debug_gnutls(level, string):
1322
logger.debug(u"GnuTLS: %s", string[:-1])
1324
(gnutls.library.functions
1325
.gnutls_global_set_log_function(debug_gnutls))
1022
service = AvahiService(name = server_settings["servicename"],
1023
servicetype = "_mandos._tcp", )
1024
if server_settings["interface"]:
1025
service.interface = (if_nametoindex
1026
(server_settings["interface"]))
1327
1028
global main_loop
1328
1031
# From the Avahi example code
1329
1032
DBusGMainLoop(set_as_default=True )
1330
1033
main_loop = gobject.MainLoop()
1331
1034
bus = dbus.SystemBus()
1035
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
1036
avahi.DBUS_PATH_SERVER),
1037
avahi.DBUS_INTERFACE_SERVER)
1332
1038
# End of Avahi example code
1334
bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1335
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1336
service = AvahiService(name = server_settings[u"servicename"],
1337
servicetype = u"_mandos._tcp",
1338
protocol = protocol, bus = bus)
1339
if server_settings["interface"]:
1340
service.interface = (if_nametoindex
1341
(str(server_settings[u"interface"])))
1040
bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
1343
client_class = Client
1345
client_class = functools.partial(ClientDBus, bus = bus)
1347
client_class(name = section,
1348
config= dict(client_config.items(section)))
1349
for section in client_config.sections()))
1043
clients.update(Set(Client(name = section,
1045
= dict(client_config.items(section)),
1046
use_dbus = use_dbus)
1047
for section in client_config.sections()))
1350
1048
if not clients:
1351
1049
logger.warning(u"No clients defined")