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
123
"""Derived from the Avahi example code"""
124
if self.rename_count >= self.max_renames:
125
logger.critical(u"No suitable service name found after %i"
126
u" retries, exiting.", rename_count)
127
raise AvahiServiceError("Too many renames")
128
name = server.GetAlternativeServiceName(name)
129
logger.error(u"Changing name to %r ...", name)
130
syslogger.setFormatter(logging.Formatter\
131
('Mandos (%s): %%(levelname)s:'
132
' %%(message)s' % name))
135
self.rename_count += 1
137
"""Derived from the Avahi example code"""
138
if group is not None:
141
"""Derived from the Avahi example code"""
144
group = dbus.Interface\
145
(bus.get_object(avahi.DBUS_NAME,
146
server.EntryGroupNew()),
147
avahi.DBUS_INTERFACE_ENTRY_GROUP)
148
group.connect_to_signal('StateChanged',
149
entry_group_state_changed)
150
logger.debug(u"Adding service '%s' of type '%s' ...",
151
service.name, service.type)
153
self.interface, # interface
154
avahi.PROTO_INET6, # protocol
155
dbus.UInt32(0), # flags
156
self.name, self.type,
157
self.domain, self.host,
158
dbus.UInt16(self.port),
159
avahi.string_array_to_txt_array(self.TXT))
162
# From the Avahi example code:
163
group = None # our entry group
164
# End of Avahi example code
167
18
class Client(object):
168
"""A representation of a client host served by this server.
170
name: string; from the config file, used in log messages
171
fingerprint: string (40 or 32 hexadecimal digits); used to
172
uniquely identify the client
173
secret: bytestring; sent verbatim (over TLS) to client
174
host: string; available for use by the checker command
175
created: datetime.datetime(); object creation, not client host
176
last_checked_ok: datetime.datetime() or None if not yet checked OK
177
timeout: datetime.timedelta(); How long from last_checked_ok
178
until this client is invalid
179
interval: datetime.timedelta(); How often to start a new checker
180
stop_hook: If set, called by stop() as stop_hook(self)
181
checker: subprocess.Popen(); a running checker process used
182
to see if the client lives.
183
'None' if no process is running.
184
checker_initiator_tag: a gobject event source tag, or None
185
stop_initiator_tag: - '' -
186
checker_callback_tag: - '' -
187
checker_command: string; External command which is run to check if
188
client lives. %() expansions are done at
189
runtime with vars(self) as dict, so that for
190
instance %(name)s can be used in the command.
192
_timeout: Real variable for 'timeout'
193
_interval: Real variable for 'interval'
194
_timeout_milliseconds: Used when calling gobject.timeout_add()
195
_interval_milliseconds: - '' -
197
def _set_timeout(self, timeout):
198
"Setter function for 'timeout' attribute"
199
self._timeout = timeout
200
self._timeout_milliseconds = ((self.timeout.days
201
* 24 * 60 * 60 * 1000)
202
+ (self.timeout.seconds * 1000)
203
+ (self.timeout.microseconds
205
timeout = property(lambda self: self._timeout,
208
def _set_interval(self, interval):
209
"Setter function for 'interval' attribute"
210
self._interval = interval
211
self._interval_milliseconds = ((self.interval.days
212
* 24 * 60 * 60 * 1000)
213
+ (self.interval.seconds
215
+ (self.interval.microseconds
217
interval = property(lambda self: self._interval,
220
def __init__(self, name = None, stop_hook=None, config={}):
221
"""Note: the 'checker' key in 'config' sets the
222
'checker_command' attribute and *not* the 'checker'
19
def __init__(self, name=None, options=None, dn=None,
20
password=None, passfile=None, fqdn=None,
21
timeout=None, interval=-1):
225
logger.debug(u"Creating client %r", self.name)
226
# Uppercase and remove spaces from fingerprint for later
227
# comparison purposes with return value from the fingerprint()
229
self.fingerprint = config["fingerprint"].upper()\
231
logger.debug(u" Fingerprint: %s", self.fingerprint)
232
if "secret" in config:
233
self.secret = config["secret"].decode(u"base64")
234
elif "secfile" in config:
235
sf = open(config["secfile"])
236
self.secret = sf.read()
25
self.password = password
27
self.password = open(passfile).readall()
239
raise TypeError(u"No secret or secfile for client %s"
241
self.host = config.get("host", "")
29
print "No Password or Passfile in client config file"
30
# raise RuntimeError XXX
31
self.password = "gazonk"
32
self.fqdn = fqdn # string
242
33
self.created = datetime.datetime.now()
243
self.last_checked_ok = None
244
self.timeout = string_to_delta(config["timeout"])
245
self.interval = string_to_delta(config["interval"])
246
self.stop_hook = stop_hook
248
self.checker_initiator_tag = None
249
self.stop_initiator_tag = None
250
self.checker_callback_tag = None
251
self.check_command = config["checker"]
253
"""Start this client's checker and timeout hooks"""
254
# Schedule a new checker to be started an 'interval' from now,
255
# and every interval from then on.
256
self.checker_initiator_tag = gobject.timeout_add\
257
(self._interval_milliseconds,
259
# Also start a new checker *right now*.
261
# Schedule a stop() when 'timeout' has passed
262
self.stop_initiator_tag = gobject.timeout_add\
263
(self._timeout_milliseconds,
267
The possibility that a client might be restarted is left open,
268
but not currently used."""
269
# If this client doesn't have a secret, it is already stopped.
270
if hasattr(self, "secret") and self.secret:
271
logger.info(u"Stopping client %s", self.name)
275
if getattr(self, "stop_initiator_tag", False):
276
gobject.source_remove(self.stop_initiator_tag)
277
self.stop_initiator_tag = None
278
if getattr(self, "checker_initiator_tag", False):
279
gobject.source_remove(self.checker_initiator_tag)
280
self.checker_initiator_tag = None
34
self.last_seen = None # datetime.datetime()
36
timeout = options.timeout
37
self.timeout = timeout # datetime.timedelta()
39
interval = options.interval
40
self.interval = interval # datetime.timedelta()
41
self.next_check = datetime.datetime.now() # datetime.datetime()
42
self.checker = None # or a subprocess.Popen()
43
def check_action(self, now=None):
44
"""The checker said something and might have completed.
45
Check if is has, and take appropriate actions."""
46
if self.checker.poll() is None:
47
# False alarm, no result yet
51
now = datetime.datetime.now()
52
if self.checker.returncode == 0:
54
while self.next_check <= now:
55
self.next_check += self.interval
56
handle_request = check_action
57
def start_checker(self):
281
58
self.stop_checker()
284
# Do not run this again if called by a gobject.timeout_add
287
self.stop_hook = None
289
def checker_callback(self, pid, condition):
290
"""The checker has completed, so take appropriate actions."""
291
now = datetime.datetime.now()
292
self.checker_callback_tag = None
60
self.checker = subprocess.Popen("sleep 1; fping -q -- %s"
61
% re.escape(self.fqdn),
62
stdout=subprocess.PIPE,
65
except subprocess.OSError, e:
66
print "Failed to start subprocess:", e
67
def stop_checker(self):
68
if self.checker is None:
70
os.kill(self.checker.pid, signal.SIGTERM)
71
if self.checker.poll() is None:
72
os.kill(self.checker.pid, signal.SIGKILL)
293
73
self.checker = None
294
if os.WIFEXITED(condition) \
295
and (os.WEXITSTATUS(condition) == 0):
296
logger.info(u"Checker for %(name)s succeeded",
298
self.last_checked_ok = now
299
gobject.source_remove(self.stop_initiator_tag)
300
self.stop_initiator_tag = gobject.timeout_add\
301
(self._timeout_milliseconds,
303
elif not os.WIFEXITED(condition):
304
logger.warning(u"Checker for %(name)s crashed?",
307
logger.info(u"Checker for %(name)s failed",
309
def start_checker(self):
310
"""Start a new checker subprocess if one is not running.
311
If a checker already exists, leave it running and do
313
# The reason for not killing a running checker is that if we
314
# did that, then if a checker (for some reason) started
315
# running slowly and taking more than 'interval' time, the
316
# client would inevitably timeout, since no checker would get
317
# a chance to run to completion. If we instead leave running
318
# checkers alone, the checker would have to take more time
319
# than 'timeout' for the client to be declared invalid, which
320
# is as it should be.
74
__del__ = stop_checker
321
76
if self.checker is None:
323
# In case check_command has exactly one % operator
324
command = self.check_command % self.host
326
# Escape attributes for the shell
327
escaped_attrs = dict((key, re.escape(str(val)))
329
vars(self).iteritems())
78
return self.checker.stdout.fileno()
80
"""The time when something must be done about this client"""
81
return min(self.last_seen + self.timeout, self.next_check)
82
def still_valid(self, now=None):
83
"""Has this client's timeout not passed?"""
85
now = datetime.datetime.now()
86
return now < (self.last_seen + timeout)
87
def it_is_time_to_check(self, now=None):
89
now = datetime.datetime.now()
90
return self.next_check <= now
93
class server_metaclass(type):
94
"Common behavior for the UDP and TCP server classes"
95
def __new__(cls, name, bases, attrs):
96
attrs["address_family"] = socket.AF_INET6
97
attrs["allow_reuse_address"] = True
98
def server_bind(self):
99
if self.options.interface:
100
if not hasattr(socket, "SO_BINDTODEVICE"):
101
# From /usr/include/asm-i486/socket.h
102
socket.SO_BINDTODEVICE = 25
331
command = self.check_command % escaped_attrs
332
except TypeError, error:
333
logger.error(u'Could not format string "%s":'
334
u' %s', self.check_command, error)
335
return True # Try again later
337
logger.info(u"Starting checker %r for %s",
339
self.checker = subprocess.Popen(command,
342
self.checker_callback_tag = gobject.child_watch_add\
344
self.checker_callback)
345
except subprocess.OSError, error:
346
logger.error(u"Failed to start subprocess: %s",
348
# Re-run this periodically if run by gobject.timeout_add
350
def stop_checker(self):
351
"""Force the checker process, if any, to stop."""
352
if self.checker_callback_tag:
353
gobject.source_remove(self.checker_callback_tag)
354
self.checker_callback_tag = None
355
if getattr(self, "checker", None) is None:
357
logger.debug(u"Stopping checker for %(name)s", vars(self))
359
os.kill(self.checker.pid, signal.SIGTERM)
361
#if self.checker.poll() is None:
362
# os.kill(self.checker.pid, signal.SIGKILL)
363
except OSError, error:
364
if error.errno != errno.ESRCH: # No such process
367
def still_valid(self):
368
"""Has the timeout not yet passed for this client?"""
369
now = datetime.datetime.now()
370
if self.last_checked_ok is None:
371
return now < (self.created + self.timeout)
373
return now < (self.last_checked_ok + self.timeout)
376
def peer_certificate(session):
377
"Return the peer's OpenPGP certificate as a bytestring"
378
# If not an OpenPGP certificate...
379
if gnutls.library.functions.gnutls_certificate_type_get\
380
(session._c_object) \
381
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
382
# ...do the normal thing
383
return session.peer_certificate
384
list_size = ctypes.c_uint()
385
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
386
(session._c_object, ctypes.byref(list_size))
387
if list_size.value == 0:
390
return ctypes.string_at(cert.data, cert.size)
393
def fingerprint(openpgp):
394
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
395
# New GnuTLS "datum" with the OpenPGP public key
396
datum = gnutls.library.types.gnutls_datum_t\
397
(ctypes.cast(ctypes.c_char_p(openpgp),
398
ctypes.POINTER(ctypes.c_ubyte)),
399
ctypes.c_uint(len(openpgp)))
400
# New empty GnuTLS certificate
401
crt = gnutls.library.types.gnutls_openpgp_crt_t()
402
gnutls.library.functions.gnutls_openpgp_crt_init\
404
# Import the OpenPGP public key into the certificate
405
gnutls.library.functions.gnutls_openpgp_crt_import\
406
(crt, ctypes.byref(datum),
407
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
408
# New buffer for the fingerprint
409
buffer = ctypes.create_string_buffer(20)
410
buffer_length = ctypes.c_size_t()
411
# Get the fingerprint from the certificate into the buffer
412
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
413
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
414
# Deinit the certificate
415
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
416
# Convert the buffer to a Python bytestring
417
fpr = ctypes.string_at(buffer, buffer_length.value)
418
# Convert the bytestring to hexadecimal notation
419
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
104
self.socket.setsockopt(socket.SOL_SOCKET,
105
socket.SO_BINDTODEVICE,
106
self.options.interface)
107
except socket.error, error:
108
if error[0] == errno.EPERM:
109
print "Warning: No permission to bind to interface", \
110
self.options.interface
113
return super(type(self), self).server_bind()
114
attrs["server_bind"] = server_bind
115
def init(self, *args, **kwargs):
116
if "options" in kwargs:
117
self.options = kwargs["options"]
118
del kwargs["options"]
119
if "clients" in kwargs:
120
self.clients = kwargs["clients"]
121
del kwargs["clients"]
122
if "credentials" in kwargs:
123
self.credentials = kwargs["credentials"]
124
del kwargs["credentials"]
125
return super(type(self), self).__init__(*args, **kwargs)
126
attrs["__init__"] = init
127
return type.__new__(cls, name, bases, attrs)
130
class udp_handler(SocketServer.DatagramRequestHandler, object):
132
self.wfile.write("Polo")
133
print "UDP request answered"
136
class IPv6_UDPServer(SocketServer.UDPServer, object):
137
__metaclass__ = server_metaclass
138
def verify_request(self, request, client_address):
139
print "UDP request came"
140
return request[0] == "Marco"
423
143
class tcp_handler(SocketServer.BaseRequestHandler, object):
424
"""A TCP request handler class.
425
Instantiated by IPv6_TCPServer for each request to handle it.
426
Note: This will run in its own forked process."""
428
144
def handle(self):
429
logger.info(u"TCP connection from: %s",
430
unicode(self.client_address))
431
session = gnutls.connection.ClientSession\
432
(self.request, gnutls.connection.X509Credentials())
434
line = self.request.makefile().readline()
435
logger.debug(u"Protocol version: %r", line)
437
if int(line.strip().split()[0]) > 1:
439
except (ValueError, IndexError, RuntimeError), error:
440
logger.error(u"Unknown protocol version: %s", error)
443
# Note: gnutls.connection.X509Credentials is really a generic
444
# GnuTLS certificate credentials object so long as no X.509
445
# keys are added to it. Therefore, we can use it here despite
446
# using OpenPGP certificates.
448
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
449
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
451
priority = "NORMAL" # Fallback default, since this
453
if self.server.settings["priority"]:
454
priority = self.server.settings["priority"]
455
gnutls.library.functions.gnutls_priority_set_direct\
456
(session._c_object, priority, None);
460
except gnutls.errors.GNUTLSError, error:
461
logger.warning(u"Handshake failed: %s", error)
462
# Do not run session.bye() here: the session is not
463
# established. Just abandon the request.
466
fpr = fingerprint(peer_certificate(session))
467
except (TypeError, gnutls.errors.GNUTLSError), error:
468
logger.warning(u"Bad certificate: %s", error)
471
logger.debug(u"Fingerprint: %s", fpr)
473
for c in self.server.clients:
474
if c.fingerprint == fpr:
478
logger.warning(u"Client not found for fingerprint: %s",
482
# Have to check if client.still_valid(), since it is possible
483
# that the client timed out while establishing the GnuTLS
485
if not client.still_valid():
486
logger.warning(u"Client %(name)s is invalid",
491
while sent_size < len(client.secret):
492
sent = session.send(client.secret[sent_size:])
493
logger.debug(u"Sent: %d, remaining: %d",
494
sent, len(client.secret)
495
- (sent_size + sent))
145
print "TCP request came"
146
print "Request:", self.request
147
print "Client Address:", self.client_address
148
print "Server:", self.server
149
session = gnutls.connection.ServerSession(self.request,
150
self.server.credentials)
152
if session.peer_certificate:
153
print "DN:", session.peer_certificate.subject
155
session.verify_peer()
156
except gnutls.errors.CertificateError, error:
157
print "Verify failed", error
161
session.send(dict((client.dn, client.password)
162
for client in self.server.clients)
163
[session.peer_certificate.subject])
165
session.send("gazonk")
500
170
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
501
"""IPv6 TCP server. Accepts 'None' as address and/or port.
503
settings: Server settings
504
clients: Set() of Client objects
506
address_family = socket.AF_INET6
507
def __init__(self, *args, **kwargs):
508
if "settings" in kwargs:
509
self.settings = kwargs["settings"]
510
del kwargs["settings"]
511
if "clients" in kwargs:
512
self.clients = kwargs["clients"]
513
del kwargs["clients"]
514
return super(type(self), self).__init__(*args, **kwargs)
515
def server_bind(self):
516
"""This overrides the normal server_bind() function
517
to bind to an interface if one was specified, and also NOT to
518
bind to an address or port if they were not specified."""
519
if self.settings["interface"]:
520
# 25 is from /usr/include/asm-i486/socket.h
521
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
523
self.socket.setsockopt(socket.SOL_SOCKET,
525
self.settings["interface"])
526
except socket.error, error:
527
if error[0] == errno.EPERM:
528
logger.error(u"No permission to"
529
u" bind to interface %s",
530
self.settings["interface"])
533
# Only bind(2) the socket if we really need to.
534
if self.server_address[0] or self.server_address[1]:
535
if not self.server_address[0]:
537
self.server_address = (in6addr_any,
538
self.server_address[1])
539
elif not self.server_address[1]:
540
self.server_address = (self.server_address[0],
542
# if self.settings["interface"]:
543
# self.server_address = (self.server_address[0],
549
return super(type(self), self).server_bind()
171
__metaclass__ = server_metaclass
172
request_queue_size = 1024
552
177
def string_to_delta(interval):
553
178
"""Parse a string and return a datetime.timedelta
586
def server_state_changed(state):
587
"""Derived from the Avahi example code"""
588
if state == avahi.SERVER_COLLISION:
589
logger.error(u"Server name collision")
591
elif state == avahi.SERVER_RUNNING:
595
def entry_group_state_changed(state, error):
596
"""Derived from the Avahi example code"""
597
logger.debug(u"state change: %i", state)
599
if state == avahi.ENTRY_GROUP_ESTABLISHED:
600
logger.debug(u"Service established.")
601
elif state == avahi.ENTRY_GROUP_COLLISION:
602
logger.warning(u"Service name collision.")
604
elif state == avahi.ENTRY_GROUP_FAILURE:
605
logger.critical(u"Error in group state changed %s",
607
raise AvahiGroupError("State changed: %s", str(error))
609
def if_nametoindex(interface):
610
"""Call the C function if_nametoindex(), or equivalent"""
611
global if_nametoindex
613
if "ctypes.util" not in sys.modules:
615
if_nametoindex = ctypes.cdll.LoadLibrary\
616
(ctypes.util.find_library("c")).if_nametoindex
617
except (OSError, AttributeError):
618
if "struct" not in sys.modules:
620
if "fcntl" not in sys.modules:
622
def if_nametoindex(interface):
623
"Get an interface index the hard way, i.e. using fcntl()"
624
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
626
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
627
struct.pack("16s16x", interface))
629
interface_index = struct.unpack("I", ifreq[16:20])[0]
630
return interface_index
631
return if_nametoindex(interface)
634
def daemon(nochdir = False, noclose = False):
635
"""See daemon(3). Standard BSD Unix function.
636
This should really exist as os.daemon, but it doesn't (yet)."""
645
# Close all standard open file descriptors
646
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
647
if not stat.S_ISCHR(os.fstat(null).st_mode):
648
raise OSError(errno.ENODEV,
649
"/dev/null not a character device")
650
os.dup2(null, sys.stdin.fileno())
651
os.dup2(null, sys.stdout.fileno())
652
os.dup2(null, sys.stderr.fileno())
658
global main_loop_started
659
main_loop_started = False
661
parser = OptionParser(version = "%%prog %s" % version)
212
parser = OptionParser()
662
213
parser.add_option("-i", "--interface", type="string",
663
metavar="IF", help="Bind to interface IF")
664
parser.add_option("-a", "--address", type="string",
665
help="Address to listen for requests on")
666
parser.add_option("-p", "--port", type="int",
214
default="eth0", metavar="IF",
215
help="Interface to bind to")
216
parser.add_option("--cert", type="string", default="cert.pem",
218
help="Public key certificate to use")
219
parser.add_option("--key", type="string", default="key.pem",
221
help="Private key to use")
222
parser.add_option("--ca", type="string", default="ca.pem",
224
help="Certificate Authority certificate to use")
225
parser.add_option("--crl", type="string", default="crl.pem",
227
help="Certificate Revokation List to use")
228
parser.add_option("-p", "--port", type="int", default=49001,
667
229
help="Port number to receive requests on")
230
parser.add_option("--dh", type="int", metavar="BITS",
231
help="DH group to use")
232
parser.add_option("-t", "--timeout", type="string", # Parsed later
234
help="Amount of downtime allowed for clients")
235
parser.add_option("--interval", type="string", # Parsed later
237
help="How often to check that a client is up")
668
238
parser.add_option("--check", action="store_true", default=False,
669
239
help="Run self-test")
670
parser.add_option("--debug", action="store_true",
671
help="Debug mode; run in foreground and log to"
673
parser.add_option("--priority", type="string", help="GnuTLS"
674
" priority string (see GnuTLS documentation)")
675
parser.add_option("--servicename", type="string", metavar="NAME",
676
help="Zeroconf service name")
677
parser.add_option("--configdir", type="string",
678
default="/etc/mandos", metavar="DIR",
679
help="Directory to search for configuration"
681
240
(options, args) = parser.parse_args()
683
242
if options.check:
685
244
doctest.testmod()
688
# Default values for config file for server-global settings
689
server_defaults = { "interface": "",
694
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
695
"servicename": "Mandos",
698
# Parse config file for server-global settings
699
server_config = ConfigParser.SafeConfigParser(server_defaults)
701
server_config.read(os.path.join(options.configdir, "mandos.conf"))
702
server_section = "server"
703
# Convert the SafeConfigParser object to a dict
704
server_settings = dict(server_config.items(server_section))
705
# Use getboolean on the boolean config option
706
server_settings["debug"] = server_config.getboolean\
707
(server_section, "debug")
710
# Override the settings from the config file with command line
712
for option in ("interface", "address", "port", "debug",
713
"priority", "servicename", "configdir"):
714
value = getattr(options, option)
715
if value is not None:
716
server_settings[option] = value
718
# Now we have our good server settings in "server_settings"
720
debug = server_settings["debug"]
723
syslogger.setLevel(logging.WARNING)
724
console.setLevel(logging.WARNING)
726
if server_settings["servicename"] != "Mandos":
727
syslogger.setFormatter(logging.Formatter\
728
('Mandos (%s): %%(levelname)s:'
730
% server_settings["servicename"]))
732
# Parse config file with clients
733
client_defaults = { "timeout": "1h",
735
"checker": "fping -q -- %%(host)s",
737
client_config = ConfigParser.SafeConfigParser(client_defaults)
738
client_config.read(os.path.join(server_settings["configdir"],
742
service = AvahiService(name = server_settings["servicename"],
743
type = "_mandos._tcp", );
744
if server_settings["interface"]:
745
service.interface = if_nametoindex(server_settings["interface"])
750
# From the Avahi example code
751
DBusGMainLoop(set_as_default=True )
752
main_loop = gobject.MainLoop()
753
bus = dbus.SystemBus()
754
server = dbus.Interface(
755
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
756
avahi.DBUS_INTERFACE_SERVER )
757
# End of Avahi example code
760
def remove_from_clients(client):
761
clients.remove(client)
763
logger.critical(u"No clients left, exiting")
766
clients.update(Set(Client(name = section,
767
stop_hook = remove_from_clients,
769
= dict(client_config.items(section)))
770
for section in client_config.sections()))
772
logger.critical(u"No clients defined")
776
logger.removeHandler(console)
779
pidfilename = "/var/run/mandos/mandos.pid"
782
pidfile = open(pidfilename, "w")
783
pidfile.write(str(pid) + "\n")
787
logger.error(u"Could not write %s file with PID %d",
788
pidfilename, os.getpid())
791
"Cleanup function; run on exit"
793
# From the Avahi example code
794
if not group is None:
797
# End of Avahi example code
800
client = clients.pop()
801
client.stop_hook = None
804
atexit.register(cleanup)
807
signal.signal(signal.SIGINT, signal.SIG_IGN)
808
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
809
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
247
# Parse the time arguments
249
options.timeout = string_to_delta(options.timeout)
251
parser.error("option --timeout: Unparseable time")
254
options.interval = string_to_delta(options.interval)
256
parser.error("option --interval: Unparseable time")
258
cert = gnutls.crypto.X509Certificate(open(options.cert).read())
259
key = gnutls.crypto.X509PrivateKey(open(options.key).read())
260
ca = gnutls.crypto.X509Certificate(open(options.ca).read())
261
crl = gnutls.crypto.X509CRL(open(options.crl).read())
262
cred = gnutls.connection.X509Credentials(cert, key, [ca], [crl])
266
client_config_object = ConfigParser.SafeConfigParser(defaults)
267
client_config_object.read("mandos-clients.conf")
268
clients = [Client(name=section, options=options,
269
**(dict(client_config_object.items(section))))
270
for section in client_config_object.sections()]
272
udp_server = IPv6_UDPServer((in6addr_any, options.port),
276
tcp_server = IPv6_TCPServer((in6addr_any, options.port),
284
input, out, err = select.select((udp_server,
291
except KeyboardInterrupt:
811
295
for client in clients:
814
tcp_server = IPv6_TCPServer((server_settings["address"],
815
server_settings["port"]),
817
settings=server_settings,
819
# Find out what port we got
820
service.port = tcp_server.socket.getsockname()[1]
821
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
822
u" scope_id %d" % tcp_server.socket.getsockname())
824
#service.interface = tcp_server.socket.getsockname()[3]
827
# From the Avahi example code
828
server.connect_to_signal("StateChanged", server_state_changed)
830
server_state_changed(server.GetState())
831
except dbus.exceptions.DBusException, error:
832
logger.critical(u"DBusException: %s", error)
834
# End of Avahi example code
836
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
837
lambda *args, **kwargs:
838
tcp_server.handle_request\
839
(*args[2:], **kwargs) or True)
841
logger.debug(u"Starting main loop")
842
main_loop_started = True
844
except AvahiError, error:
845
logger.critical(u"AvahiError: %s" + unicode(error))
847
except KeyboardInterrupt:
851
if __name__ == '__main__':
296
client.stop_checker()
299
if __name__ == "__main__":