40
9
import gnutls.crypto
41
10
import gnutls.connection
42
11
import gnutls.errors
43
import gnutls.library.functions
44
import gnutls.library.constants
45
import gnutls.library.types
46
12
import ConfigParser
56
import logging.handlers
61
from dbus.mainloop.glib import DBusGMainLoop
66
logger = logging.Logger('mandos')
67
syslogger = logging.handlers.SysLogHandler\
68
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
70
syslogger.setFormatter(logging.Formatter\
71
('Mandos: %(levelname)s: %(message)s'))
72
logger.addHandler(syslogger)
74
console = logging.StreamHandler()
75
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
77
logger.addHandler(console)
79
class AvahiError(Exception):
80
def __init__(self, value):
83
return repr(self.value)
85
class AvahiServiceError(AvahiError):
88
class AvahiGroupError(AvahiError):
92
class AvahiService(object):
93
"""An Avahi (Zeroconf) service.
95
interface: integer; avahi.IF_UNSPEC or an interface index.
96
Used to optionally bind to the specified interface.
97
name: string; Example: 'Mandos'
98
type: string; Example: '_mandos._tcp'.
99
See <http://www.dns-sd.org/ServiceTypes.html>
100
port: integer; what port to announce
101
TXT: list of strings; TXT record for the service
102
domain: string; Domain to publish on, default to .local if empty.
103
host: string; Host to publish records for, default is localhost
104
max_renames: integer; maximum number of renames
105
rename_count: integer; counter so we only rename after collisions
106
a sensible number of times
108
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
109
type = None, port = None, TXT = None, domain = "",
110
host = "", max_renames = 32768):
111
self.interface = interface
121
self.rename_count = 0
122
self.max_renames = max_renames
124
"""Derived from the Avahi example code"""
125
if self.rename_count >= self.max_renames:
126
logger.critical(u"No suitable service name found after %i"
127
u" retries, exiting.", rename_count)
128
raise AvahiServiceError("Too many renames")
129
self.name = server.GetAlternativeServiceName(self.name)
130
logger.info(u"Changing name to %r ...", str(self.name))
131
syslogger.setFormatter(logging.Formatter\
132
('Mandos (%s): %%(levelname)s:'
133
' %%(message)s' % self.name))
136
self.rename_count += 1
138
"""Derived from the Avahi example code"""
139
if group is not None:
142
"""Derived from the Avahi example code"""
145
group = dbus.Interface\
146
(bus.get_object(avahi.DBUS_NAME,
147
server.EntryGroupNew()),
148
avahi.DBUS_INTERFACE_ENTRY_GROUP)
149
group.connect_to_signal('StateChanged',
150
entry_group_state_changed)
151
logger.debug(u"Adding service '%s' of type '%s' ...",
152
service.name, service.type)
154
self.interface, # interface
155
avahi.PROTO_INET6, # protocol
156
dbus.UInt32(0), # flags
157
self.name, self.type,
158
self.domain, self.host,
159
dbus.UInt16(self.port),
160
avahi.string_array_to_txt_array(self.TXT))
163
# From the Avahi example code:
164
group = None # our entry group
165
# End of Avahi example code
168
16
class Client(object):
169
"""A representation of a client host served by this server.
171
name: string; from the config file, used in log messages
172
fingerprint: string (40 or 32 hexadecimal digits); used to
173
uniquely identify the client
174
secret: bytestring; sent verbatim (over TLS) to client
175
host: string; available for use by the checker command
176
created: datetime.datetime(); object creation, not client host
177
last_checked_ok: datetime.datetime() or None if not yet checked OK
178
timeout: datetime.timedelta(); How long from last_checked_ok
179
until this client is invalid
180
interval: datetime.timedelta(); How often to start a new checker
181
stop_hook: If set, called by stop() as stop_hook(self)
182
checker: subprocess.Popen(); a running checker process used
183
to see if the client lives.
184
'None' if no process is running.
185
checker_initiator_tag: a gobject event source tag, or None
186
stop_initiator_tag: - '' -
187
checker_callback_tag: - '' -
188
checker_command: string; External command which is run to check if
189
client lives. %() expansions are done at
190
runtime with vars(self) as dict, so that for
191
instance %(name)s can be used in the command.
193
_timeout: Real variable for 'timeout'
194
_interval: Real variable for 'interval'
195
_timeout_milliseconds: Used when calling gobject.timeout_add()
196
_interval_milliseconds: - '' -
198
def _set_timeout(self, timeout):
199
"Setter function for 'timeout' attribute"
200
self._timeout = timeout
201
self._timeout_milliseconds = ((self.timeout.days
202
* 24 * 60 * 60 * 1000)
203
+ (self.timeout.seconds * 1000)
204
+ (self.timeout.microseconds
206
timeout = property(lambda self: self._timeout,
209
def _set_interval(self, interval):
210
"Setter function for 'interval' attribute"
211
self._interval = interval
212
self._interval_milliseconds = ((self.interval.days
213
* 24 * 60 * 60 * 1000)
214
+ (self.interval.seconds
216
+ (self.interval.microseconds
218
interval = property(lambda self: self._interval,
221
def __init__(self, name = None, stop_hook=None, config={}):
222
"""Note: the 'checker' key in 'config' sets the
223
'checker_command' attribute and *not* the 'checker'
17
def __init__(self, name=None, options=None, dn=None,
18
password=None, passfile=None, fqdn=None,
19
timeout=None, interval=-1):
226
logger.debug(u"Creating client %r", self.name)
227
# Uppercase and remove spaces from fingerprint for later
228
# comparison purposes with return value from the fingerprint()
230
self.fingerprint = config["fingerprint"].upper()\
232
logger.debug(u" Fingerprint: %s", self.fingerprint)
233
if "secret" in config:
234
self.secret = config["secret"].decode(u"base64")
235
elif "secfile" in config:
236
sf = open(config["secfile"])
237
self.secret = sf.read()
23
self.password = password
25
self.password = open(passfile).readall()
240
raise TypeError(u"No secret or secfile for client %s"
242
self.host = config.get("host", "")
27
print "No Password or Passfile in client config file"
28
# raise RuntimeError XXX
29
self.password = "gazonk"
243
31
self.created = datetime.datetime.now()
244
self.last_checked_ok = None
245
self.timeout = string_to_delta(config["timeout"])
246
self.interval = string_to_delta(config["interval"])
247
self.stop_hook = stop_hook
249
self.checker_initiator_tag = None
250
self.stop_initiator_tag = None
251
self.checker_callback_tag = None
252
self.check_command = config["checker"]
254
"""Start this client's checker and timeout hooks"""
255
# Schedule a new checker to be started an 'interval' from now,
256
# and every interval from then on.
257
self.checker_initiator_tag = gobject.timeout_add\
258
(self._interval_milliseconds,
260
# Also start a new checker *right now*.
262
# Schedule a stop() when 'timeout' has passed
263
self.stop_initiator_tag = gobject.timeout_add\
264
(self._timeout_milliseconds,
268
The possibility that a client might be restarted is left open,
269
but not currently used."""
270
# If this client doesn't have a secret, it is already stopped.
271
if hasattr(self, "secret") and self.secret:
272
logger.info(u"Stopping client %s", self.name)
276
if getattr(self, "stop_initiator_tag", False):
277
gobject.source_remove(self.stop_initiator_tag)
278
self.stop_initiator_tag = None
279
if getattr(self, "checker_initiator_tag", False):
280
gobject.source_remove(self.checker_initiator_tag)
281
self.checker_initiator_tag = None
285
# Do not run this again if called by a gobject.timeout_add
288
self.stop_hook = None
290
def checker_callback(self, pid, condition):
291
"""The checker has completed, so take appropriate actions."""
292
now = datetime.datetime.now()
293
self.checker_callback_tag = None
295
if os.WIFEXITED(condition) \
296
and (os.WEXITSTATUS(condition) == 0):
297
logger.info(u"Checker for %(name)s succeeded",
299
self.last_checked_ok = now
300
gobject.source_remove(self.stop_initiator_tag)
301
self.stop_initiator_tag = gobject.timeout_add\
302
(self._timeout_milliseconds,
304
elif not os.WIFEXITED(condition):
305
logger.warning(u"Checker for %(name)s crashed?",
308
logger.info(u"Checker for %(name)s failed",
310
def start_checker(self):
311
"""Start a new checker subprocess if one is not running.
312
If a checker already exists, leave it running and do
314
# The reason for not killing a running checker is that if we
315
# did that, then if a checker (for some reason) started
316
# running slowly and taking more than 'interval' time, the
317
# client would inevitably timeout, since no checker would get
318
# a chance to run to completion. If we instead leave running
319
# checkers alone, the checker would have to take more time
320
# than 'timeout' for the client to be declared invalid, which
321
# is as it should be.
322
if self.checker is None:
324
# In case check_command has exactly one % operator
325
command = self.check_command % self.host
327
# Escape attributes for the shell
328
escaped_attrs = dict((key, re.escape(str(val)))
330
vars(self).iteritems())
34
timeout = options.timeout
35
self.timeout = timeout
37
interval = options.interval
38
self.interval = interval
39
self.next_check = datetime.datetime.now()
42
class server_metaclass(type):
43
"Common behavior for the UDP and TCP server classes"
44
def __new__(cls, name, bases, attrs):
45
attrs["address_family"] = socket.AF_INET6
46
attrs["allow_reuse_address"] = True
47
def server_bind(self):
48
if self.options.interface:
49
if not hasattr(socket, "SO_BINDTODEVICE"):
50
# From /usr/include/asm-i486/socket.h
51
socket.SO_BINDTODEVICE = 25
332
command = self.check_command % escaped_attrs
333
except TypeError, error:
334
logger.error(u'Could not format string "%s":'
335
u' %s', self.check_command, error)
336
return True # Try again later
338
logger.info(u"Starting checker %r for %s",
340
self.checker = subprocess.Popen(command,
343
self.checker_callback_tag = gobject.child_watch_add\
345
self.checker_callback)
346
except subprocess.OSError, error:
347
logger.error(u"Failed to start subprocess: %s",
349
# Re-run this periodically if run by gobject.timeout_add
351
def stop_checker(self):
352
"""Force the checker process, if any, to stop."""
353
if self.checker_callback_tag:
354
gobject.source_remove(self.checker_callback_tag)
355
self.checker_callback_tag = None
356
if getattr(self, "checker", None) is None:
358
logger.debug(u"Stopping checker for %(name)s", vars(self))
360
os.kill(self.checker.pid, signal.SIGTERM)
362
#if self.checker.poll() is None:
363
# os.kill(self.checker.pid, signal.SIGKILL)
364
except OSError, error:
365
if error.errno != errno.ESRCH: # No such process
368
def still_valid(self):
369
"""Has the timeout not yet passed for this client?"""
370
now = datetime.datetime.now()
371
if self.last_checked_ok is None:
372
return now < (self.created + self.timeout)
374
return now < (self.last_checked_ok + self.timeout)
377
def peer_certificate(session):
378
"Return the peer's OpenPGP certificate as a bytestring"
379
# If not an OpenPGP certificate...
380
if gnutls.library.functions.gnutls_certificate_type_get\
381
(session._c_object) \
382
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
383
# ...do the normal thing
384
return session.peer_certificate
385
list_size = ctypes.c_uint()
386
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
387
(session._c_object, ctypes.byref(list_size))
388
if list_size.value == 0:
391
return ctypes.string_at(cert.data, cert.size)
394
def fingerprint(openpgp):
395
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
396
# New GnuTLS "datum" with the OpenPGP public key
397
datum = gnutls.library.types.gnutls_datum_t\
398
(ctypes.cast(ctypes.c_char_p(openpgp),
399
ctypes.POINTER(ctypes.c_ubyte)),
400
ctypes.c_uint(len(openpgp)))
401
# New empty GnuTLS certificate
402
crt = gnutls.library.types.gnutls_openpgp_crt_t()
403
gnutls.library.functions.gnutls_openpgp_crt_init\
405
# Import the OpenPGP public key into the certificate
406
gnutls.library.functions.gnutls_openpgp_crt_import\
407
(crt, ctypes.byref(datum),
408
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
409
# New buffer for the fingerprint
410
buffer = ctypes.create_string_buffer(20)
411
buffer_length = ctypes.c_size_t()
412
# Get the fingerprint from the certificate into the buffer
413
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
414
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
415
# Deinit the certificate
416
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
417
# Convert the buffer to a Python bytestring
418
fpr = ctypes.string_at(buffer, buffer_length.value)
419
# Convert the bytestring to hexadecimal notation
420
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
53
self.socket.setsockopt(socket.SOL_SOCKET,
54
socket.SO_BINDTODEVICE,
55
self.options.interface)
56
except socket.error, error:
57
if error[0] == errno.EPERM:
58
print "Warning: No permission to bind to interface", \
59
self.options.interface
62
return super(type(self), self).server_bind()
63
attrs["server_bind"] = server_bind
64
def init(self, *args, **kwargs):
65
if "options" in kwargs:
66
self.options = kwargs["options"]
68
if "clients" in kwargs:
69
self.clients = kwargs["clients"]
71
if "credentials" in kwargs:
72
self.credentials = kwargs["credentials"]
73
del kwargs["credentials"]
74
return super(type(self), self).__init__(*args, **kwargs)
75
attrs["__init__"] = init
76
return type.__new__(cls, name, bases, attrs)
79
class udp_handler(SocketServer.DatagramRequestHandler, object):
81
self.wfile.write("Polo")
82
print "UDP request answered"
85
class IPv6_UDPServer(SocketServer.UDPServer, object):
86
__metaclass__ = server_metaclass
87
def verify_request(self, request, client_address):
88
print "UDP request came"
89
return request[0] == "Marco"
424
92
class tcp_handler(SocketServer.BaseRequestHandler, object):
425
"""A TCP request handler class.
426
Instantiated by IPv6_TCPServer for each request to handle it.
427
Note: This will run in its own forked process."""
430
logger.info(u"TCP connection from: %s",
431
unicode(self.client_address))
432
session = gnutls.connection.ClientSession\
433
(self.request, gnutls.connection.X509Credentials())
435
line = self.request.makefile().readline()
436
logger.debug(u"Protocol version: %r", line)
438
if int(line.strip().split()[0]) > 1:
440
except (ValueError, IndexError, RuntimeError), error:
441
logger.error(u"Unknown protocol version: %s", error)
444
# Note: gnutls.connection.X509Credentials is really a generic
445
# GnuTLS certificate credentials object so long as no X.509
446
# keys are added to it. Therefore, we can use it here despite
447
# using OpenPGP certificates.
449
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
450
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
452
priority = "NORMAL" # Fallback default, since this
454
if self.server.settings["priority"]:
455
priority = self.server.settings["priority"]
456
gnutls.library.functions.gnutls_priority_set_direct\
457
(session._c_object, priority, None);
461
except gnutls.errors.GNUTLSError, error:
462
logger.warning(u"Handshake failed: %s", error)
463
# Do not run session.bye() here: the session is not
464
# established. Just abandon the request.
467
fpr = fingerprint(peer_certificate(session))
468
except (TypeError, gnutls.errors.GNUTLSError), error:
469
logger.warning(u"Bad certificate: %s", error)
472
logger.debug(u"Fingerprint: %s", fpr)
474
for c in self.server.clients:
475
if c.fingerprint == fpr:
479
logger.warning(u"Client not found for fingerprint: %s",
483
# Have to check if client.still_valid(), since it is possible
484
# that the client timed out while establishing the GnuTLS
486
if not client.still_valid():
487
logger.warning(u"Client %(name)s is invalid",
492
while sent_size < len(client.secret):
493
sent = session.send(client.secret[sent_size:])
494
logger.debug(u"Sent: %d, remaining: %d",
495
sent, len(client.secret)
496
- (sent_size + sent))
94
print "TCP request came"
95
print "Request:", self.request
96
print "Client Address:", self.client_address
97
print "Server:", self.server
98
session = gnutls.connection.ServerSession(self.request,
99
self.server.credentials)
101
if session.peer_certificate:
102
print "DN:", session.peer_certificate.subject
104
session.verify_peer()
105
except gnutls.errors.CertificateError, error:
106
print "Verify failed", error
110
session.send(dict((client.dn, client.password)
111
for client in self.server.clients)
112
[session.peer_certificate.subject])
114
session.send("gazonk")
501
119
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
502
"""IPv6 TCP server. Accepts 'None' as address and/or port.
504
settings: Server settings
505
clients: Set() of Client objects
507
address_family = socket.AF_INET6
508
def __init__(self, *args, **kwargs):
509
if "settings" in kwargs:
510
self.settings = kwargs["settings"]
511
del kwargs["settings"]
512
if "clients" in kwargs:
513
self.clients = kwargs["clients"]
514
del kwargs["clients"]
515
return super(type(self), self).__init__(*args, **kwargs)
516
def server_bind(self):
517
"""This overrides the normal server_bind() function
518
to bind to an interface if one was specified, and also NOT to
519
bind to an address or port if they were not specified."""
520
if self.settings["interface"]:
521
# 25 is from /usr/include/asm-i486/socket.h
522
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
524
self.socket.setsockopt(socket.SOL_SOCKET,
526
self.settings["interface"])
527
except socket.error, error:
528
if error[0] == errno.EPERM:
529
logger.error(u"No permission to"
530
u" bind to interface %s",
531
self.settings["interface"])
534
# Only bind(2) the socket if we really need to.
535
if self.server_address[0] or self.server_address[1]:
536
if not self.server_address[0]:
538
self.server_address = (in6addr_any,
539
self.server_address[1])
540
elif not self.server_address[1]:
541
self.server_address = (self.server_address[0],
543
# if self.settings["interface"]:
544
# self.server_address = (self.server_address[0],
550
return super(type(self), self).server_bind()
120
__metaclass__ = server_metaclass
121
request_queue_size = 1024
553
128
def string_to_delta(interval):
554
129
"""Parse a string and return a datetime.timedelta
563
138
datetime.timedelta(1)
564
139
>>> string_to_delta(u'1w')
565
140
datetime.timedelta(7)
566
>>> string_to_delta('5m 30s')
567
datetime.timedelta(0, 330)
569
timevalue = datetime.timedelta(0)
570
for s in interval.split():
572
suffix=unicode(s[-1])
575
delta = datetime.timedelta(value)
577
delta = datetime.timedelta(0, value)
579
delta = datetime.timedelta(0, 0, 0, 0, value)
581
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
583
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
586
except (ValueError, IndexError):
143
suffix=unicode(interval[-1])
144
value=int(interval[:-1])
146
delta = datetime.timedelta(value)
148
delta = datetime.timedelta(0, value)
150
delta = datetime.timedelta(0, 0, 0, 0, value)
152
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
154
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
592
def server_state_changed(state):
593
"""Derived from the Avahi example code"""
594
if state == avahi.SERVER_COLLISION:
595
logger.error(u"Server name collision")
597
elif state == avahi.SERVER_RUNNING:
601
def entry_group_state_changed(state, error):
602
"""Derived from the Avahi example code"""
603
logger.debug(u"state change: %i", state)
605
if state == avahi.ENTRY_GROUP_ESTABLISHED:
606
logger.debug(u"Service established.")
607
elif state == avahi.ENTRY_GROUP_COLLISION:
608
logger.warning(u"Service name collision.")
610
elif state == avahi.ENTRY_GROUP_FAILURE:
611
logger.critical(u"Error in group state changed %s",
613
raise AvahiGroupError("State changed: %s", str(error))
615
def if_nametoindex(interface):
616
"""Call the C function if_nametoindex(), or equivalent"""
617
global if_nametoindex
619
if "ctypes.util" not in sys.modules:
621
if_nametoindex = ctypes.cdll.LoadLibrary\
622
(ctypes.util.find_library("c")).if_nametoindex
623
except (OSError, AttributeError):
624
if "struct" not in sys.modules:
626
if "fcntl" not in sys.modules:
628
def if_nametoindex(interface):
629
"Get an interface index the hard way, i.e. using fcntl()"
630
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
632
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
633
struct.pack("16s16x", interface))
635
interface_index = struct.unpack("I", ifreq[16:20])[0]
636
return interface_index
637
return if_nametoindex(interface)
640
def daemon(nochdir = False, noclose = False):
641
"""See daemon(3). Standard BSD Unix function.
642
This should really exist as os.daemon, but it doesn't (yet)."""
651
# Close all standard open file descriptors
652
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
653
if not stat.S_ISCHR(os.fstat(null).st_mode):
654
raise OSError(errno.ENODEV,
655
"/dev/null not a character device")
656
os.dup2(null, sys.stdin.fileno())
657
os.dup2(null, sys.stdout.fileno())
658
os.dup2(null, sys.stderr.fileno())
157
except (ValueError, IndexError):
664
global main_loop_started
665
main_loop_started = False
667
parser = OptionParser(version = "%%prog %s" % version)
162
parser = OptionParser()
668
163
parser.add_option("-i", "--interface", type="string",
669
metavar="IF", help="Bind to interface IF")
670
parser.add_option("-a", "--address", type="string",
671
help="Address to listen for requests on")
672
parser.add_option("-p", "--port", type="int",
164
default="eth0", metavar="IF",
165
help="Interface to bind to")
166
parser.add_option("--cert", type="string", default="cert.pem",
168
help="Public key certificate to use")
169
parser.add_option("--key", type="string", default="key.pem",
171
help="Private key to use")
172
parser.add_option("--ca", type="string", default="ca.pem",
174
help="Certificate Authority certificate to use")
175
parser.add_option("--crl", type="string", default="crl.pem",
177
help="Certificate Revokation List to use")
178
parser.add_option("-p", "--port", type="int", default=49001,
673
179
help="Port number to receive requests on")
180
parser.add_option("--dh", type="int", metavar="BITS",
181
help="DH group to use")
182
parser.add_option("-t", "--timeout", type="string", # Parsed later
184
help="Amount of downtime allowed for clients")
185
parser.add_option("--interval", type="string", # Parsed later
187
help="How often to check that a client is up")
674
188
parser.add_option("--check", action="store_true", default=False,
675
189
help="Run self-test")
676
parser.add_option("--debug", action="store_true",
677
help="Debug mode; run in foreground and log to"
679
parser.add_option("--priority", type="string", help="GnuTLS"
680
" priority string (see GnuTLS documentation)")
681
parser.add_option("--servicename", type="string", metavar="NAME",
682
help="Zeroconf service name")
683
parser.add_option("--configdir", type="string",
684
default="/etc/mandos", metavar="DIR",
685
help="Directory to search for configuration"
687
190
(options, args) = parser.parse_args()
689
192
if options.check:
691
194
doctest.testmod()
694
# Default values for config file for server-global settings
695
server_defaults = { "interface": "",
700
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
701
"servicename": "Mandos",
704
# Parse config file for server-global settings
705
server_config = ConfigParser.SafeConfigParser(server_defaults)
707
server_config.read(os.path.join(options.configdir, "mandos.conf"))
708
# Convert the SafeConfigParser object to a dict
709
server_settings = server_config.defaults()
710
# Use getboolean on the boolean config option
711
server_settings["debug"] = server_config.getboolean\
715
# Override the settings from the config file with command line
717
for option in ("interface", "address", "port", "debug",
718
"priority", "servicename", "configdir"):
719
value = getattr(options, option)
720
if value is not None:
721
server_settings[option] = value
723
# Now we have our good server settings in "server_settings"
725
debug = server_settings["debug"]
728
syslogger.setLevel(logging.WARNING)
729
console.setLevel(logging.WARNING)
731
if server_settings["servicename"] != "Mandos":
732
syslogger.setFormatter(logging.Formatter\
733
('Mandos (%s): %%(levelname)s:'
735
% server_settings["servicename"]))
737
# Parse config file with clients
738
client_defaults = { "timeout": "1h",
740
"checker": "fping -q -- %%(host)s",
743
client_config = ConfigParser.SafeConfigParser(client_defaults)
744
client_config.read(os.path.join(server_settings["configdir"],
748
service = AvahiService(name = server_settings["servicename"],
749
type = "_mandos._tcp", );
750
if server_settings["interface"]:
751
service.interface = if_nametoindex(server_settings["interface"])
756
# From the Avahi example code
757
DBusGMainLoop(set_as_default=True )
758
main_loop = gobject.MainLoop()
759
bus = dbus.SystemBus()
760
server = dbus.Interface(
761
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
762
avahi.DBUS_INTERFACE_SERVER )
763
# End of Avahi example code
766
def remove_from_clients(client):
767
clients.remove(client)
769
logger.critical(u"No clients left, exiting")
772
clients.update(Set(Client(name = section,
773
stop_hook = remove_from_clients,
775
= dict(client_config.items(section)))
776
for section in client_config.sections()))
778
logger.critical(u"No clients defined")
782
logger.removeHandler(console)
785
pidfilename = "/var/run/mandos/mandos.pid"
788
pidfile = open(pidfilename, "w")
789
pidfile.write(str(pid) + "\n")
793
logger.error(u"Could not write %s file with PID %d",
794
pidfilename, os.getpid())
797
"Cleanup function; run on exit"
799
# From the Avahi example code
800
if not group is None:
803
# End of Avahi example code
806
client = clients.pop()
807
client.stop_hook = None
810
atexit.register(cleanup)
813
signal.signal(signal.SIGINT, signal.SIG_IGN)
814
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
815
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
817
for client in clients:
820
tcp_server = IPv6_TCPServer((server_settings["address"],
821
server_settings["port"]),
197
# Parse the time arguments
199
options.timeout = string_to_delta(options.timeout)
201
parser.error("option --timeout: Unparseable time")
204
options.interval = string_to_delta(options.interval)
206
parser.error("option --interval: Unparseable time")
208
cert = gnutls.crypto.X509Certificate(open(options.cert).read())
209
key = gnutls.crypto.X509PrivateKey(open(options.key).read())
210
ca = gnutls.crypto.X509Certificate(open(options.ca).read())
211
crl = gnutls.crypto.X509CRL(open(options.crl).read())
212
cred = gnutls.connection.X509Credentials(cert, key, [ca], [crl])
216
client_config_object = ConfigParser.SafeConfigParser(defaults)
217
client_config_object.read("mandos-clients.conf")
218
clients = [Client(name=section, options=options,
219
**(dict(client_config_object.items(section))))
220
for section in client_config_object.sections()]
222
udp_server = IPv6_UDPServer((in6addr_any, options.port),
226
tcp_server = IPv6_TCPServer((in6addr_any, options.port),
823
settings=server_settings,
825
# Find out what port we got
826
service.port = tcp_server.socket.getsockname()[1]
827
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
828
u" scope_id %d" % tcp_server.socket.getsockname())
830
#service.interface = tcp_server.socket.getsockname()[3]
833
# From the Avahi example code
834
server.connect_to_signal("StateChanged", server_state_changed)
836
server_state_changed(server.GetState())
837
except dbus.exceptions.DBusException, error:
838
logger.critical(u"DBusException: %s", error)
840
# End of Avahi example code
842
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
843
lambda *args, **kwargs:
844
tcp_server.handle_request\
845
(*args[2:], **kwargs) or True)
847
logger.debug(u"Starting main loop")
848
main_loop_started = True
850
except AvahiError, error:
851
logger.critical(u"AvahiError: %s" + unicode(error))
853
except KeyboardInterrupt:
857
if __name__ == '__main__':
233
in_, out, err = select.select((udp_server,
236
server.handle_request()
239
if __name__ == "__main__":