51
22
from sets import Set
56
import logging.handlers
58
from contextlib import closing
63
28
from dbus.mainloop.glib import DBusGMainLoop
69
logger = logging.Logger('mandos')
70
syslogger = logging.handlers.SysLogHandler\
71
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
73
syslogger.setFormatter(logging.Formatter\
74
('Mandos: %(levelname)s: %(message)s'))
75
logger.addHandler(syslogger)
77
console = logging.StreamHandler()
78
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
80
logger.addHandler(console)
82
class AvahiError(Exception):
83
def __init__(self, value):
85
super(AvahiError, self).__init__()
87
return repr(self.value)
89
class AvahiServiceError(AvahiError):
92
class AvahiGroupError(AvahiError):
96
class AvahiService(object):
97
"""An Avahi (Zeroconf) service.
99
interface: integer; avahi.IF_UNSPEC or an interface index.
100
Used to optionally bind to the specified interface.
101
name: string; Example: 'Mandos'
102
type: string; Example: '_mandos._tcp'.
103
See <http://www.dns-sd.org/ServiceTypes.html>
104
port: integer; what port to announce
105
TXT: list of strings; TXT record for the service
106
domain: string; Domain to publish on, default to .local if empty.
107
host: string; Host to publish records for, default is localhost
108
max_renames: integer; maximum number of renames
109
rename_count: integer; counter so we only rename after collisions
110
a sensible number of times
112
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
113
servicetype = None, port = None, TXT = None, domain = "",
114
host = "", max_renames = 32768):
115
self.interface = interface
117
self.type = servicetype
125
self.rename_count = 0
126
self.max_renames = max_renames
128
"""Derived from the Avahi example code"""
129
if self.rename_count >= self.max_renames:
130
logger.critical(u"No suitable Zeroconf service name found"
131
u" after %i retries, exiting.",
133
raise AvahiServiceError("Too many renames")
134
self.name = server.GetAlternativeServiceName(self.name)
135
logger.info(u"Changing Zeroconf service name to %r ...",
137
syslogger.setFormatter(logging.Formatter\
138
('Mandos (%s): %%(levelname)s:'
139
' %%(message)s' % self.name))
142
self.rename_count += 1
144
"""Derived from the Avahi example code"""
145
if group is not None:
148
"""Derived from the Avahi example code"""
151
group = dbus.Interface\
152
(bus.get_object(avahi.DBUS_NAME,
153
server.EntryGroupNew()),
154
avahi.DBUS_INTERFACE_ENTRY_GROUP)
155
group.connect_to_signal('StateChanged',
156
entry_group_state_changed)
157
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
158
service.name, service.type)
160
self.interface, # interface
161
avahi.PROTO_INET6, # protocol
162
dbus.UInt32(0), # flags
163
self.name, self.type,
164
self.domain, self.host,
165
dbus.UInt16(self.port),
166
avahi.string_array_to_txt_array(self.TXT))
169
# From the Avahi example code:
170
group = None # our entry group
31
# This variable is used to optionally bind to a specified interface.
32
# It is a global variable to fit in with the other variables from the
33
# Avahi server example code.
34
serviceInterface = avahi.IF_UNSPEC
35
# From the Avahi server example code:
36
serviceName = "Mandos"
37
serviceType = "_mandos._tcp" # http://www.dns-sd.org/ServiceTypes.html
38
servicePort = None # Not known at startup
39
serviceTXT = [] # TXT record for the service
40
domain = "" # Domain to publish on, default to .local
41
host = "" # Host to publish records for, default to localhost
42
group = None #our entry group
43
rename_count = 12 # Counter so we only rename after collisions a
44
# sensible number of times
171
45
# End of Avahi example code
295
164
# Do not run this again if called by a gobject.timeout_add
297
166
def __del__(self):
298
self.stop_hook = None
167
# Some code duplication here and in stop()
168
if hasattr(self, "stop_initiator_tag") \
169
and self.stop_initiator_tag:
170
gobject.source_remove(self.stop_initiator_tag)
171
self.stop_initiator_tag = None
172
if hasattr(self, "checker_initiator_tag") \
173
and self.checker_initiator_tag:
174
gobject.source_remove(self.checker_initiator_tag)
175
self.checker_initiator_tag = None
300
177
def checker_callback(self, pid, condition):
301
178
"""The checker has completed, so take appropriate actions."""
302
self.checker_callback_tag = None
179
now = datetime.datetime.now()
304
180
if os.WIFEXITED(condition) \
305
181
and (os.WEXITSTATUS(condition) == 0):
306
logger.info(u"Checker for %(name)s succeeded",
309
elif not os.WIFEXITED(condition):
310
logger.warning(u"Checker for %(name)s crashed?",
313
logger.info(u"Checker for %(name)s failed",
315
def bump_timeout(self):
316
"""Bump up the timeout for this client.
317
This should only be called when the client has been seen,
320
self.last_checked_ok = datetime.datetime.now()
321
gobject.source_remove(self.stop_initiator_tag)
322
self.stop_initiator_tag = gobject.timeout_add\
323
(self._timeout_milliseconds, self.stop)
183
sys.stderr.write(u"Checker for %(name)s succeeded\n"
186
gobject.source_remove(self.stop_initiator_tag)
187
self.stop_initiator_tag = gobject.timeout_add\
188
(self._timeout_milliseconds,
191
if not os.WIFEXITED(condition):
192
sys.stderr.write(u"Checker for %(name)s crashed?\n"
195
sys.stderr.write(u"Checker for %(name)s failed\n"
198
self.checker_callback_tag = None
324
199
def start_checker(self):
325
200
"""Start a new checker subprocess if one is not running.
326
201
If a checker already exists, leave it running and do
328
# The reason for not killing a running checker is that if we
329
# did that, then if a checker (for some reason) started
330
# running slowly and taking more than 'interval' time, the
331
# client would inevitably timeout, since no checker would get
332
# a chance to run to completion. If we instead leave running
333
# checkers alone, the checker would have to take more time
334
# than 'timeout' for the client to be declared invalid, which
335
# is as it should be.
336
203
if self.checker is None:
205
sys.stderr.write(u"Starting checker for %s\n"
338
# In case check_command has exactly one % operator
339
command = self.check_command % self.host
208
command = self.check_command % self.fqdn
340
209
except TypeError:
341
# Escape attributes for the shell
342
210
escaped_attrs = dict((key, re.escape(str(val)))
344
212
vars(self).iteritems())
346
command = self.check_command % escaped_attrs
347
except TypeError, error:
348
logger.error(u'Could not format string "%s":'
349
u' %s', self.check_command, error)
350
return True # Try again later
213
command = self.check_command % escaped_attrs
352
logger.info(u"Starting checker %r for %s",
354
# We don't need to redirect stdout and stderr, since
355
# in normal mode, that is already done by daemon(),
356
# and in debug mode we don't want to. (Stdin is
357
# always replaced by /dev/null.)
358
self.checker = subprocess.Popen(command,
361
self.checker_callback_tag = gobject.child_watch_add\
363
self.checker_callback)
364
except OSError, error:
365
logger.error(u"Failed to start subprocess: %s",
215
self.checker = subprocess.\
217
stdout=subprocess.PIPE,
218
close_fds=True, shell=True,
220
self.checker_callback_tag = gobject.\
221
child_watch_add(self.checker.pid,
224
except subprocess.OSError, error:
225
sys.stderr.write(u"Failed to start subprocess: %s\n"
367
227
# Re-run this periodically if run by gobject.timeout_add
369
229
def stop_checker(self):
370
230
"""Force the checker process, if any, to stop."""
371
if self.checker_callback_tag:
372
gobject.source_remove(self.checker_callback_tag)
373
self.checker_callback_tag = None
374
if getattr(self, "checker", None) is None:
231
if not hasattr(self, "checker") or self.checker is None:
376
logger.debug(u"Stopping checker for %(name)s", vars(self))
378
os.kill(self.checker.pid, signal.SIGTERM)
380
#if self.checker.poll() is None:
381
# os.kill(self.checker.pid, signal.SIGKILL)
382
except OSError, error:
383
if error.errno != errno.ESRCH: # No such process
233
gobject.source_remove(self.checker_callback_tag)
234
self.checker_callback_tag = None
235
os.kill(self.checker.pid, signal.SIGTERM)
236
if self.checker.poll() is None:
237
os.kill(self.checker.pid, signal.SIGKILL)
385
238
self.checker = None
386
def still_valid(self):
239
def still_valid(self, now=None):
387
240
"""Has the timeout not yet passed for this client?"""
388
now = datetime.datetime.now()
389
if self.last_checked_ok is None:
242
now = datetime.datetime.now()
243
if self.last_seen is None:
390
244
return now < (self.created + self.timeout)
392
return now < (self.last_checked_ok + self.timeout)
246
return now < (self.last_seen + self.timeout)
395
249
def peer_certificate(session):
396
"Return the peer's OpenPGP certificate as a bytestring"
397
250
# If not an OpenPGP certificate...
398
251
if gnutls.library.functions.gnutls_certificate_type_get\
399
252
(session._c_object) \
412
265
def fingerprint(openpgp):
413
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
266
# New empty GnuTLS certificate
267
crt = gnutls.library.types.gnutls_openpgp_crt_t()
268
gnutls.library.functions.gnutls_openpgp_crt_init\
414
270
# New GnuTLS "datum" with the OpenPGP public key
415
271
datum = gnutls.library.types.gnutls_datum_t\
416
272
(ctypes.cast(ctypes.c_char_p(openpgp),
417
273
ctypes.POINTER(ctypes.c_ubyte)),
418
274
ctypes.c_uint(len(openpgp)))
419
# New empty GnuTLS certificate
420
crt = gnutls.library.types.gnutls_openpgp_crt_t()
421
gnutls.library.functions.gnutls_openpgp_crt_init\
423
275
# Import the OpenPGP public key into the certificate
424
gnutls.library.functions.gnutls_openpgp_crt_import\
425
(crt, ctypes.byref(datum),
426
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
427
# Verify the self signature in the key
428
crtverify = ctypes.c_uint()
429
gnutls.library.functions.gnutls_openpgp_crt_verify_self\
430
(crt, 0, ctypes.byref(crtverify))
431
if crtverify.value != 0:
432
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
433
raise gnutls.errors.CertificateSecurityError("Verify failed")
276
ret = gnutls.library.functions.gnutls_openpgp_crt_import\
279
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
434
280
# New buffer for the fingerprint
435
buf = ctypes.create_string_buffer(20)
436
buf_len = ctypes.c_size_t()
281
buffer = ctypes.create_string_buffer(20)
282
buffer_length = ctypes.c_size_t()
437
283
# Get the fingerprint from the certificate into the buffer
438
284
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
439
(crt, ctypes.byref(buf), ctypes.byref(buf_len))
285
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
440
286
# Deinit the certificate
441
287
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
442
288
# Convert the buffer to a Python bytestring
443
fpr = ctypes.string_at(buf, buf_len.value)
289
fpr = ctypes.string_at(buffer, buffer_length.value)
444
290
# Convert the bytestring to hexadecimal notation
445
291
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
449
class TCP_handler(SocketServer.BaseRequestHandler, object):
295
class tcp_handler(SocketServer.BaseRequestHandler, object):
450
296
"""A TCP request handler class.
451
297
Instantiated by IPv6_TCPServer for each request to handle it.
452
298
Note: This will run in its own forked process."""
454
300
def handle(self):
455
logger.info(u"TCP connection from: %s",
456
unicode(self.client_address))
457
session = gnutls.connection.ClientSession\
458
(self.request, gnutls.connection.X509Credentials())
460
line = self.request.makefile().readline()
461
logger.debug(u"Protocol version: %r", line)
463
if int(line.strip().split()[0]) > 1:
465
except (ValueError, IndexError, RuntimeError), error:
466
logger.error(u"Unknown protocol version: %s", error)
469
# Note: gnutls.connection.X509Credentials is really a generic
470
# GnuTLS certificate credentials object so long as no X.509
471
# keys are added to it. Therefore, we can use it here despite
472
# using OpenPGP certificates.
302
sys.stderr.write(u"TCP request came\n")
303
sys.stderr.write(u"Request: %s\n" % self.request)
304
sys.stderr.write(u"Client Address: %s\n"
305
% unicode(self.client_address))
306
sys.stderr.write(u"Server: %s\n" % self.server)
307
session = gnutls.connection.ClientSession(self.request,
474
311
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
475
312
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
477
# Use a fallback default, since this MUST be set.
478
priority = self.server.settings.get("priority", "NORMAL")
314
priority = "SECURE256"
479
316
gnutls.library.functions.gnutls_priority_set_direct\
480
(session._c_object, priority, None)
317
(session._c_object, priority, None);
483
320
session.handshake()
484
321
except gnutls.errors.GNUTLSError, error:
485
logger.warning(u"Handshake failed: %s", error)
323
sys.stderr.write(u"Handshake failed: %s\n" % error)
486
324
# Do not run session.bye() here: the session is not
487
325
# established. Just abandon the request.
490
328
fpr = fingerprint(peer_certificate(session))
491
329
except (TypeError, gnutls.errors.GNUTLSError), error:
492
logger.warning(u"Bad certificate: %s", error)
331
sys.stderr.write(u"Bad certificate: %s\n" % error)
495
logger.debug(u"Fingerprint: %s", fpr)
335
sys.stderr.write(u"Fingerprint: %s\n" % fpr)
497
for c in self.server.clients:
498
338
if c.fingerprint == fpr:
502
logger.warning(u"Client not found for fingerprint: %s",
506
341
# Have to check if client.still_valid(), since it is possible
507
342
# that the client timed out while establishing the GnuTLS
509
if not client.still_valid():
510
logger.warning(u"Client %(name)s is invalid",
344
if (not client) or (not client.still_valid()):
347
sys.stderr.write(u"Client %(name)s is invalid\n"
350
sys.stderr.write(u"Client not found for "
351
u"fingerprint: %s\n" % fpr)
514
## This won't work here, since we're in a fork.
515
# client.bump_timeout()
517
355
while sent_size < len(client.secret):
518
356
sent = session.send(client.secret[sent_size:])
519
logger.debug(u"Sent: %d, remaining: %d",
520
sent, len(client.secret)
521
- (sent_size + sent))
358
sys.stderr.write(u"Sent: %d, remaining: %d\n"
359
% (sent, len(client.secret)
360
- (sent_size + sent)))
522
361
sent_size += sent
526
class IPv6_TCPServer(SocketServer.ForkingMixIn,
527
SocketServer.TCPServer, object):
365
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
528
366
"""IPv6 TCP server. Accepts 'None' as address and/or port.
530
settings: Server settings
368
options: Command line options
531
369
clients: Set() of Client objects
532
enabled: Boolean; whether this server is activated yet
534
371
address_family = socket.AF_INET6
535
372
def __init__(self, *args, **kwargs):
536
if "settings" in kwargs:
537
self.settings = kwargs["settings"]
538
del kwargs["settings"]
373
if "options" in kwargs:
374
self.options = kwargs["options"]
375
del kwargs["options"]
539
376
if "clients" in kwargs:
540
377
self.clients = kwargs["clients"]
541
378
del kwargs["clients"]
543
super(IPv6_TCPServer, self).__init__(*args, **kwargs)
379
return super(type(self), self).__init__(*args, **kwargs)
544
380
def server_bind(self):
545
381
"""This overrides the normal server_bind() function
546
382
to bind to an interface if one was specified, and also NOT to
547
383
bind to an address or port if they were not specified."""
548
if self.settings["interface"]:
549
# 25 is from /usr/include/asm-i486/socket.h
550
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
384
if self.options.interface:
385
if not hasattr(socket, "SO_BINDTODEVICE"):
386
# From /usr/include/asm-i486/socket.h
387
socket.SO_BINDTODEVICE = 25
552
389
self.socket.setsockopt(socket.SOL_SOCKET,
554
self.settings["interface"])
390
socket.SO_BINDTODEVICE,
391
self.options.interface)
555
392
except socket.error, error:
556
393
if error[0] == errno.EPERM:
557
logger.error(u"No permission to"
558
u" bind to interface %s",
559
self.settings["interface"])
394
sys.stderr.write(u"Warning: No permission to" \
395
u" bind to interface %s\n"
396
% self.options.interface)
562
399
# Only bind(2) the socket if we really need to.
596
421
datetime.timedelta(1)
597
422
>>> string_to_delta(u'1w')
598
423
datetime.timedelta(7)
599
>>> string_to_delta('5m 30s')
600
datetime.timedelta(0, 330)
602
timevalue = datetime.timedelta(0)
603
for s in interval.split():
605
suffix = unicode(s[-1])
608
delta = datetime.timedelta(value)
610
delta = datetime.timedelta(0, value)
612
delta = datetime.timedelta(0, 0, 0, 0, value)
614
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
616
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
619
except (ValueError, IndexError):
426
suffix=unicode(interval[-1])
427
value=int(interval[:-1])
429
delta = datetime.timedelta(value)
431
delta = datetime.timedelta(0, value)
433
delta = datetime.timedelta(0, 0, 0, 0, value)
435
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
437
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
440
except (ValueError, IndexError):
446
"""From the Avahi server example code"""
447
global group, serviceName, serviceType, servicePort, serviceTXT, \
450
group = dbus.Interface(
451
bus.get_object( avahi.DBUS_NAME,
452
server.EntryGroupNew()),
453
avahi.DBUS_INTERFACE_ENTRY_GROUP)
454
group.connect_to_signal('StateChanged',
455
entry_group_state_changed)
457
sys.stderr.write(u"Adding service '%s' of type '%s' ...\n"
458
% (serviceName, serviceType))
461
serviceInterface, # interface
462
avahi.PROTO_INET6, # protocol
463
dbus.UInt32(0), # flags
464
serviceName, serviceType,
466
dbus.UInt16(servicePort),
467
avahi.string_array_to_txt_array(serviceTXT))
471
def remove_service():
472
"""From the Avahi server example code"""
475
if not group is None:
625
479
def server_state_changed(state):
626
"""Derived from the Avahi example code"""
480
"""From the Avahi server example code"""
627
481
if state == avahi.SERVER_COLLISION:
628
logger.error(u"Zeroconf server name collision")
482
sys.stderr.write(u"WARNING: Server name collision\n")
630
484
elif state == avahi.SERVER_RUNNING:
634
488
def entry_group_state_changed(state, error):
635
"""Derived from the Avahi example code"""
636
logger.debug(u"Avahi state change: %i", state)
489
"""From the Avahi server example code"""
490
global serviceName, server, rename_count
493
sys.stderr.write(u"state change: %i\n" % state)
638
495
if state == avahi.ENTRY_GROUP_ESTABLISHED:
639
logger.debug(u"Zeroconf service established.")
497
sys.stderr.write(u"Service established.\n")
640
498
elif state == avahi.ENTRY_GROUP_COLLISION:
641
logger.warning(u"Zeroconf service name collision.")
500
rename_count = rename_count - 1
502
name = server.GetAlternativeServiceName(name)
503
sys.stderr.write(u"WARNING: Service name collision, "
504
u"changing name to '%s' ...\n" % name)
509
sys.stderr.write(u"ERROR: No suitable service name found "
510
u"after %i retries, exiting.\n"
643
513
elif state == avahi.ENTRY_GROUP_FAILURE:
644
logger.critical(u"Avahi: Error in group state changed %s",
646
raise AvahiGroupError("State changed: %s", str(error))
514
sys.stderr.write(u"Error in group state changed %s\n"
648
520
def if_nametoindex(interface):
649
"""Call the C function if_nametoindex(), or equivalent"""
650
global if_nametoindex
521
"""Call the C function if_nametoindex()"""
652
if_nametoindex = ctypes.cdll.LoadLibrary\
653
(ctypes.util.find_library("c")).if_nametoindex
523
libc = ctypes.cdll.LoadLibrary("libc.so.6")
524
return libc.if_nametoindex(interface)
654
525
except (OSError, AttributeError):
655
526
if "struct" not in sys.modules:
657
528
if "fcntl" not in sys.modules:
659
def if_nametoindex(interface):
660
"Get an interface index the hard way, i.e. using fcntl()"
661
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
662
with closing(socket.socket()) as s:
663
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
664
struct.pack("16s16x", interface))
665
interface_index = struct.unpack("I", ifreq[16:20])[0]
666
return interface_index
667
return if_nametoindex(interface)
670
def daemon(nochdir = False, noclose = False):
671
"""See daemon(3). Standard BSD Unix function.
672
This should really exist as os.daemon, but it doesn't (yet)."""
681
# Close all standard open file descriptors
682
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
683
if not stat.S_ISCHR(os.fstat(null).st_mode):
684
raise OSError(errno.ENODEV,
685
"/dev/null not a character device")
686
os.dup2(null, sys.stdin.fileno())
687
os.dup2(null, sys.stdout.fileno())
688
os.dup2(null, sys.stderr.fileno())
694
parser = OptionParser(version = "%%prog %s" % version)
530
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
532
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
533
struct.pack("16s16x", interface))
535
interface_index = struct.unpack("I", ifreq[16:20])[0]
536
return interface_index
539
if __name__ == '__main__':
540
parser = OptionParser()
695
541
parser.add_option("-i", "--interface", type="string",
696
metavar="IF", help="Bind to interface IF")
697
parser.add_option("-a", "--address", type="string",
698
help="Address to listen for requests on")
699
parser.add_option("-p", "--port", type="int",
542
default=None, metavar="IF",
543
help="Bind to interface IF")
544
parser.add_option("--cert", type="string", default="cert.pem",
546
help="Public key certificate PEM file to use")
547
parser.add_option("--key", type="string", default="key.pem",
549
help="Private key PEM file to use")
550
parser.add_option("--ca", type="string", default="ca.pem",
552
help="Certificate Authority certificate PEM file to use")
553
parser.add_option("--crl", type="string", default="crl.pem",
555
help="Certificate Revokation List PEM file to use")
556
parser.add_option("-p", "--port", type="int", default=None,
700
557
help="Port number to receive requests on")
558
parser.add_option("--timeout", type="string", # Parsed later
560
help="Amount of downtime allowed for clients")
561
parser.add_option("--interval", type="string", # Parsed later
563
help="How often to check that a client is up")
701
564
parser.add_option("--check", action="store_true", default=False,
702
565
help="Run self-test")
703
parser.add_option("--debug", action="store_true",
704
help="Debug mode; run in foreground and log to"
706
parser.add_option("--priority", type="string", help="GnuTLS"
707
" priority string (see GnuTLS documentation)")
708
parser.add_option("--servicename", type="string", metavar="NAME",
709
help="Zeroconf service name")
710
parser.add_option("--configdir", type="string",
711
default="/etc/mandos", metavar="DIR",
712
help="Directory to search for configuration"
714
options = parser.parse_args()[0]
566
parser.add_option("--debug", action="store_true", default=False,
568
(options, args) = parser.parse_args()
716
570
if options.check:
718
572
doctest.testmod()
721
# Default values for config file for server-global settings
722
server_defaults = { "interface": "",
727
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
728
"servicename": "Mandos",
731
# Parse config file for server-global settings
732
server_config = ConfigParser.SafeConfigParser(server_defaults)
734
server_config.read(os.path.join(options.configdir, "mandos.conf"))
735
# Convert the SafeConfigParser object to a dict
736
server_settings = server_config.defaults()
737
# Use getboolean on the boolean config option
738
server_settings["debug"] = server_config.getboolean\
742
# Override the settings from the config file with command line
744
for option in ("interface", "address", "port", "debug",
745
"priority", "servicename", "configdir"):
746
value = getattr(options, option)
747
if value is not None:
748
server_settings[option] = value
750
# Now we have our good server settings in "server_settings"
752
debug = server_settings["debug"]
755
syslogger.setLevel(logging.WARNING)
756
console.setLevel(logging.WARNING)
758
if server_settings["servicename"] != "Mandos":
759
syslogger.setFormatter(logging.Formatter\
760
('Mandos (%s): %%(levelname)s:'
762
% server_settings["servicename"]))
764
# Parse config file with clients
765
client_defaults = { "timeout": "1h",
767
"checker": "fping -q -- %(host)s",
770
client_config = ConfigParser.SafeConfigParser(client_defaults)
771
client_config.read(os.path.join(server_settings["configdir"],
775
tcp_server = IPv6_TCPServer((server_settings["address"],
776
server_settings["port"]),
778
settings=server_settings,
780
pidfilename = "/var/run/mandos.pid"
782
pidfile = open(pidfilename, "w")
783
except IOError, error:
784
logger.error("Could not open file %r", pidfilename)
789
uid = pwd.getpwnam("mandos").pw_uid
792
uid = pwd.getpwnam("nobody").pw_uid
796
gid = pwd.getpwnam("mandos").pw_gid
799
gid = pwd.getpwnam("nogroup").pw_gid
805
except OSError, error:
806
if error[0] != errno.EPERM:
810
service = AvahiService(name = server_settings["servicename"],
811
servicetype = "_mandos._tcp", )
812
if server_settings["interface"]:
813
service.interface = if_nametoindex\
814
(server_settings["interface"])
819
# From the Avahi example code
575
# Parse the time arguments
577
options.timeout = string_to_delta(options.timeout)
579
parser.error("option --timeout: Unparseable time")
581
options.interval = string_to_delta(options.interval)
583
parser.error("option --interval: Unparseable time")
586
defaults = { "checker": "sleep 1; fping -q -- %%(fqdn)s" }
587
client_config = ConfigParser.SafeConfigParser(defaults)
588
#client_config.readfp(open("secrets.conf"), "secrets.conf")
589
client_config.read("mandos-clients.conf")
591
# From the Avahi server example code
820
592
DBusGMainLoop(set_as_default=True )
821
593
main_loop = gobject.MainLoop()
822
594
bus = dbus.SystemBus()
823
server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
824
avahi.DBUS_PATH_SERVER),
825
avahi.DBUS_INTERFACE_SERVER)
595
server = dbus.Interface(
596
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
597
avahi.DBUS_INTERFACE_SERVER )
826
598
# End of Avahi example code
600
debug = options.debug
828
603
def remove_from_clients(client):
829
604
clients.remove(client)
831
logger.critical(u"No clients left, exiting")
607
sys.stderr.write(u"No clients left, exiting\n")
834
clients.update(Set(Client(name = section,
610
clients.update(Set(Client(name=section, options=options,
835
611
stop_hook = remove_from_clients,
837
= dict(client_config.items(section)))
612
**(dict(client_config\
838
614
for section in client_config.sections()))
840
logger.critical(u"No clients defined")
844
# Redirect stdin so all checkers get /dev/null
845
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
846
os.dup2(null, sys.stdin.fileno())
851
logger.removeHandler(console)
852
# Close all input and output, do double fork, etc.
857
pidfile.write(str(pid) + "\n")
861
logger.error(u"Could not write to file %r with PID %d",
864
# "pidfile" was never created
869
"Cleanup function; run on exit"
871
# From the Avahi example code
872
if not group is None:
875
# End of Avahi example code
878
client = clients.pop()
879
client.stop_hook = None
882
atexit.register(cleanup)
885
signal.signal(signal.SIGINT, signal.SIG_IGN)
886
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
887
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
889
615
for client in clients:
893
tcp_server.server_activate()
895
# Find out what port we got
896
service.port = tcp_server.socket.getsockname()[1]
897
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
898
u" scope_id %d" % tcp_server.socket.getsockname())
900
#service.interface = tcp_server.socket.getsockname()[3]
618
tcp_server = IPv6_TCPServer((None, options.port),
622
# Find out what random port we got
623
servicePort = tcp_server.socket.getsockname()[1]
625
sys.stderr.write(u"Now listening on port %d\n" % servicePort)
627
if options.interface is not None:
628
serviceInterface = if_nametoindex(options.interface)
630
# From the Avahi server example code
631
server.connect_to_signal("StateChanged", server_state_changed)
632
server_state_changed(server.GetState())
633
# End of Avahi example code
635
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
636
lambda *args, **kwargs:
637
tcp_server.handle_request(*args[2:],
903
# From the Avahi example code
904
server.connect_to_signal("StateChanged", server_state_changed)
906
server_state_changed(server.GetState())
907
except dbus.exceptions.DBusException, error:
908
logger.critical(u"DBusException: %s", error)
910
# End of Avahi example code
912
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
913
lambda *args, **kwargs:
914
tcp_server.handle_request\
915
(*args[2:], **kwargs) or True)
917
logger.debug(u"Starting main loop")
919
except AvahiError, error:
920
logger.critical(u"AvahiError: %s" + unicode(error))
922
641
except KeyboardInterrupt:
926
if __name__ == '__main__':
646
# From the Avahi server example code
647
if not group is None:
649
# End of Avahi example code
651
for client in clients:
652
client.stop_hook = None