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
65
logger = logging.Logger('mandos')
66
syslogger = logging.handlers.SysLogHandler\
67
(facility = logging.handlers.SysLogHandler.LOG_DAEMON)
68
syslogger.setFormatter(logging.Formatter\
69
('%(levelname)s: %(message)s'))
70
logger.addHandler(syslogger)
74
class AvahiError(Exception):
75
def __init__(self, value):
78
return repr(self.value)
80
class AvahiServiceError(AvahiError):
83
class AvahiGroupError(AvahiError):
87
class AvahiService(object):
88
"""An Avahi (Zeroconf) service.
90
interface: integer; avahi.IF_UNSPEC or an interface index.
91
Used to optionally bind to the specified interface.
92
name: string; Example: 'Mandos'
93
type: string; Example: '_mandos._tcp'.
94
See <http://www.dns-sd.org/ServiceTypes.html>
95
port: integer; what port to announce
96
TXT: list of strings; TXT record for the service
97
domain: string; Domain to publish on, default to .local if empty.
98
host: string; Host to publish records for, default is localhost
99
max_renames: integer; maximum number of renames
100
rename_count: integer; counter so we only rename after collisions
101
a sensible number of times
103
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
104
type = None, port = None, TXT = None, domain = "",
105
host = "", max_renames = 12):
106
self.interface = interface
116
self.rename_count = 0
118
"""Derived from the Avahi example code"""
119
if self.rename_count >= self.max_renames:
120
logger.critical(u"No suitable service name found after %i"
121
u" retries, exiting.", rename_count)
122
raise AvahiServiceError("Too many renames")
123
name = server.GetAlternativeServiceName(name)
124
logger.error(u"Changing name to %r ...", name)
127
self.rename_count += 1
129
"""Derived from the Avahi example code"""
130
if group is not None:
133
"""Derived from the Avahi example code"""
136
group = dbus.Interface\
137
(bus.get_object(avahi.DBUS_NAME,
138
server.EntryGroupNew()),
139
avahi.DBUS_INTERFACE_ENTRY_GROUP)
140
group.connect_to_signal('StateChanged',
141
entry_group_state_changed)
142
logger.debug(u"Adding service '%s' of type '%s' ...",
143
service.name, service.type)
145
self.interface, # interface
146
avahi.PROTO_INET6, # protocol
147
dbus.UInt32(0), # flags
148
self.name, self.type,
149
self.domain, self.host,
150
dbus.UInt16(self.port),
151
avahi.string_array_to_txt_array(self.TXT))
154
# From the Avahi example code:
155
group = None # our entry group
156
# End of Avahi example code
159
18
class Client(object):
160
"""A representation of a client host served by this server.
162
name: string; from the config file, used in log messages
163
fingerprint: string (40 or 32 hexadecimal digits); used to
164
uniquely identify the client
165
secret: bytestring; sent verbatim (over TLS) to client
166
host: string; available for use by the checker command
167
created: datetime.datetime(); object creation, not client host
168
last_checked_ok: datetime.datetime() or None if not yet checked OK
169
timeout: datetime.timedelta(); How long from last_checked_ok
170
until this client is invalid
171
interval: datetime.timedelta(); How often to start a new checker
172
stop_hook: If set, called by stop() as stop_hook(self)
173
checker: subprocess.Popen(); a running checker process used
174
to see if the client lives.
175
'None' if no process is running.
176
checker_initiator_tag: a gobject event source tag, or None
177
stop_initiator_tag: - '' -
178
checker_callback_tag: - '' -
179
checker_command: string; External command which is run to check if
180
client lives. %() expansions are done at
181
runtime with vars(self) as dict, so that for
182
instance %(name)s can be used in the command.
184
_timeout: Real variable for 'timeout'
185
_interval: Real variable for 'interval'
186
_timeout_milliseconds: Used when calling gobject.timeout_add()
187
_interval_milliseconds: - '' -
189
def _set_timeout(self, timeout):
190
"Setter function for 'timeout' attribute"
191
self._timeout = timeout
192
self._timeout_milliseconds = ((self.timeout.days
193
* 24 * 60 * 60 * 1000)
194
+ (self.timeout.seconds * 1000)
195
+ (self.timeout.microseconds
197
timeout = property(lambda self: self._timeout,
200
def _set_interval(self, interval):
201
"Setter function for 'interval' attribute"
202
self._interval = interval
203
self._interval_milliseconds = ((self.interval.days
204
* 24 * 60 * 60 * 1000)
205
+ (self.interval.seconds
207
+ (self.interval.microseconds
209
interval = property(lambda self: self._interval,
212
def __init__(self, name = None, stop_hook=None, config={}):
213
"""Note: the 'checker' key in 'config' sets the
214
'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):
217
logger.debug(u"Creating client %r", self.name)
218
# Uppercase and remove spaces from fingerprint for later
219
# comparison purposes with return value from the fingerprint()
221
self.fingerprint = config["fingerprint"].upper()\
223
logger.debug(u" Fingerprint: %s", self.fingerprint)
224
if "secret" in config:
225
self.secret = config["secret"].decode(u"base64")
226
elif "secfile" in config:
227
sf = open(config["secfile"])
228
self.secret = sf.read()
25
self.password = password
27
self.password = open(passfile).readall()
231
raise TypeError(u"No secret or secfile for client %s"
233
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
234
33
self.created = datetime.datetime.now()
235
self.last_checked_ok = None
236
self.timeout = string_to_delta(config["timeout"])
237
self.interval = string_to_delta(config["interval"])
238
self.stop_hook = stop_hook
240
self.checker_initiator_tag = None
241
self.stop_initiator_tag = None
242
self.checker_callback_tag = None
243
self.check_command = config["checker"]
245
"""Start this client's checker and timeout hooks"""
246
# Schedule a new checker to be started an 'interval' from now,
247
# and every interval from then on.
248
self.checker_initiator_tag = gobject.timeout_add\
249
(self._interval_milliseconds,
251
# Also start a new checker *right now*.
253
# Schedule a stop() when 'timeout' has passed
254
self.stop_initiator_tag = gobject.timeout_add\
255
(self._timeout_milliseconds,
259
The possibility that a client might be restarted is left open,
260
but not currently used."""
261
# If this client doesn't have a secret, it is already stopped.
262
if hasattr(self, "secret") and self.secret:
263
logger.info(u"Stopping client %s", self.name)
267
if getattr(self, "stop_initiator_tag", False):
268
gobject.source_remove(self.stop_initiator_tag)
269
self.stop_initiator_tag = None
270
if getattr(self, "checker_initiator_tag", False):
271
gobject.source_remove(self.checker_initiator_tag)
272
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):
273
58
self.stop_checker()
276
# Do not run this again if called by a gobject.timeout_add
279
self.stop_hook = None
281
def checker_callback(self, pid, condition):
282
"""The checker has completed, so take appropriate actions."""
283
now = datetime.datetime.now()
284
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)
285
73
self.checker = None
286
if os.WIFEXITED(condition) \
287
and (os.WEXITSTATUS(condition) == 0):
288
logger.info(u"Checker for %(name)s succeeded",
290
self.last_checked_ok = now
291
gobject.source_remove(self.stop_initiator_tag)
292
self.stop_initiator_tag = gobject.timeout_add\
293
(self._timeout_milliseconds,
295
elif not os.WIFEXITED(condition):
296
logger.warning(u"Checker for %(name)s crashed?",
299
logger.info(u"Checker for %(name)s failed",
301
def start_checker(self):
302
"""Start a new checker subprocess if one is not running.
303
If a checker already exists, leave it running and do
305
# The reason for not killing a running checker is that if we
306
# did that, then if a checker (for some reason) started
307
# running slowly and taking more than 'interval' time, the
308
# client would inevitably timeout, since no checker would get
309
# a chance to run to completion. If we instead leave running
310
# checkers alone, the checker would have to take more time
311
# than 'timeout' for the client to be declared invalid, which
312
# is as it should be.
74
__del__ = stop_checker
313
76
if self.checker is None:
315
# In case check_command has exactly one % operator
316
command = self.check_command % self.host
318
# Escape attributes for the shell
319
escaped_attrs = dict((key, re.escape(str(val)))
321
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
323
command = self.check_command % escaped_attrs
324
except TypeError, error:
325
logger.error(u'Could not format string "%s":'
326
u' %s', self.check_command, error)
327
return True # Try again later
329
logger.info(u"Starting checker %r for %s",
331
self.checker = subprocess.Popen(command,
334
self.checker_callback_tag = gobject.child_watch_add\
336
self.checker_callback)
337
except subprocess.OSError, error:
338
logger.error(u"Failed to start subprocess: %s",
340
# Re-run this periodically if run by gobject.timeout_add
342
def stop_checker(self):
343
"""Force the checker process, if any, to stop."""
344
if self.checker_callback_tag:
345
gobject.source_remove(self.checker_callback_tag)
346
self.checker_callback_tag = None
347
if getattr(self, "checker", None) is None:
349
logger.debug(u"Stopping checker for %(name)s", vars(self))
351
os.kill(self.checker.pid, signal.SIGTERM)
353
#if self.checker.poll() is None:
354
# os.kill(self.checker.pid, signal.SIGKILL)
355
except OSError, error:
356
if error.errno != errno.ESRCH: # No such process
359
def still_valid(self):
360
"""Has the timeout not yet passed for this client?"""
361
now = datetime.datetime.now()
362
if self.last_checked_ok is None:
363
return now < (self.created + self.timeout)
365
return now < (self.last_checked_ok + self.timeout)
368
def peer_certificate(session):
369
"Return the peer's OpenPGP certificate as a bytestring"
370
# If not an OpenPGP certificate...
371
if gnutls.library.functions.gnutls_certificate_type_get\
372
(session._c_object) \
373
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
374
# ...do the normal thing
375
return session.peer_certificate
376
list_size = ctypes.c_uint()
377
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
378
(session._c_object, ctypes.byref(list_size))
379
if list_size.value == 0:
382
return ctypes.string_at(cert.data, cert.size)
385
def fingerprint(openpgp):
386
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
387
# New GnuTLS "datum" with the OpenPGP public key
388
datum = gnutls.library.types.gnutls_datum_t\
389
(ctypes.cast(ctypes.c_char_p(openpgp),
390
ctypes.POINTER(ctypes.c_ubyte)),
391
ctypes.c_uint(len(openpgp)))
392
# New empty GnuTLS certificate
393
crt = gnutls.library.types.gnutls_openpgp_crt_t()
394
gnutls.library.functions.gnutls_openpgp_crt_init\
396
# Import the OpenPGP public key into the certificate
397
gnutls.library.functions.gnutls_openpgp_crt_import\
398
(crt, ctypes.byref(datum),
399
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
400
# New buffer for the fingerprint
401
buffer = ctypes.create_string_buffer(20)
402
buffer_length = ctypes.c_size_t()
403
# Get the fingerprint from the certificate into the buffer
404
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
405
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
406
# Deinit the certificate
407
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
408
# Convert the buffer to a Python bytestring
409
fpr = ctypes.string_at(buffer, buffer_length.value)
410
# Convert the bytestring to hexadecimal notation
411
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"
415
143
class tcp_handler(SocketServer.BaseRequestHandler, object):
416
"""A TCP request handler class.
417
Instantiated by IPv6_TCPServer for each request to handle it.
418
Note: This will run in its own forked process."""
420
144
def handle(self):
421
logger.info(u"TCP connection from: %s",
422
unicode(self.client_address))
423
session = gnutls.connection.ClientSession\
424
(self.request, gnutls.connection.X509Credentials())
426
line = self.request.makefile().readline()
427
logger.debug(u"Protocol version: %r", line)
429
if int(line.strip().split()[0]) > 1:
431
except (ValueError, IndexError, RuntimeError), error:
432
logger.error(u"Unknown protocol version: %s", error)
435
# Note: gnutls.connection.X509Credentials is really a generic
436
# GnuTLS certificate credentials object so long as no X.509
437
# keys are added to it. Therefore, we can use it here despite
438
# using OpenPGP certificates.
440
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
441
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
443
priority = "NORMAL" # Fallback default, since this
445
if self.server.settings["priority"]:
446
priority = self.server.settings["priority"]
447
gnutls.library.functions.gnutls_priority_set_direct\
448
(session._c_object, priority, None);
452
except gnutls.errors.GNUTLSError, error:
453
logger.warning(u"Handshake failed: %s", error)
454
# Do not run session.bye() here: the session is not
455
# established. Just abandon the request.
458
fpr = fingerprint(peer_certificate(session))
459
except (TypeError, gnutls.errors.GNUTLSError), error:
460
logger.warning(u"Bad certificate: %s", error)
463
logger.debug(u"Fingerprint: %s", fpr)
465
for c in self.server.clients:
466
if c.fingerprint == fpr:
470
logger.warning(u"Client not found for fingerprint: %s",
474
# Have to check if client.still_valid(), since it is possible
475
# that the client timed out while establishing the GnuTLS
477
if not client.still_valid():
478
logger.warning(u"Client %(name)s is invalid",
483
while sent_size < len(client.secret):
484
sent = session.send(client.secret[sent_size:])
485
logger.debug(u"Sent: %d, remaining: %d",
486
sent, len(client.secret)
487
- (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")
492
170
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
493
"""IPv6 TCP server. Accepts 'None' as address and/or port.
495
settings: Server settings
496
clients: Set() of Client objects
498
address_family = socket.AF_INET6
499
def __init__(self, *args, **kwargs):
500
if "settings" in kwargs:
501
self.settings = kwargs["settings"]
502
del kwargs["settings"]
503
if "clients" in kwargs:
504
self.clients = kwargs["clients"]
505
del kwargs["clients"]
506
return super(type(self), self).__init__(*args, **kwargs)
507
def server_bind(self):
508
"""This overrides the normal server_bind() function
509
to bind to an interface if one was specified, and also NOT to
510
bind to an address or port if they were not specified."""
511
if self.settings["interface"]:
512
# 25 is from /usr/include/asm-i486/socket.h
513
SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
515
self.socket.setsockopt(socket.SOL_SOCKET,
517
self.settings["interface"])
518
except socket.error, error:
519
if error[0] == errno.EPERM:
520
logger.error(u"No permission to"
521
u" bind to interface %s",
522
self.settings["interface"])
525
# Only bind(2) the socket if we really need to.
526
if self.server_address[0] or self.server_address[1]:
527
if not self.server_address[0]:
529
self.server_address = (in6addr_any,
530
self.server_address[1])
531
elif not self.server_address[1]:
532
self.server_address = (self.server_address[0],
534
# if self.settings["interface"]:
535
# self.server_address = (self.server_address[0],
541
return super(type(self), self).server_bind()
171
__metaclass__ = server_metaclass
172
request_queue_size = 1024
544
177
def string_to_delta(interval):
545
178
"""Parse a string and return a datetime.timedelta
578
def server_state_changed(state):
579
"""Derived from the Avahi example code"""
580
if state == avahi.SERVER_COLLISION:
581
logger.error(u"Server name collision")
583
elif state == avahi.SERVER_RUNNING:
587
def entry_group_state_changed(state, error):
588
"""Derived from the Avahi example code"""
589
logger.debug(u"state change: %i", state)
591
if state == avahi.ENTRY_GROUP_ESTABLISHED:
592
logger.debug(u"Service established.")
593
elif state == avahi.ENTRY_GROUP_COLLISION:
594
logger.warning(u"Service name collision.")
596
elif state == avahi.ENTRY_GROUP_FAILURE:
597
logger.critical(u"Error in group state changed %s",
599
raise AvahiGroupError("State changed: %s", str(error))
601
def if_nametoindex(interface):
602
"""Call the C function if_nametoindex(), or equivalent"""
603
global if_nametoindex
605
if "ctypes.util" not in sys.modules:
607
if_nametoindex = ctypes.cdll.LoadLibrary\
608
(ctypes.util.find_library("c")).if_nametoindex
609
except (OSError, AttributeError):
610
if "struct" not in sys.modules:
612
if "fcntl" not in sys.modules:
614
def if_nametoindex(interface):
615
"Get an interface index the hard way, i.e. using fcntl()"
616
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
618
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
619
struct.pack("16s16x", interface))
621
interface_index = struct.unpack("I", ifreq[16:20])[0]
622
return interface_index
623
return if_nametoindex(interface)
626
def daemon(nochdir = False, noclose = False):
627
"""See daemon(3). Standard BSD Unix function.
628
This should really exist as os.daemon, but it doesn't (yet)."""
637
# Close all standard open file descriptors
638
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
639
if not stat.S_ISCHR(os.fstat(null).st_mode):
640
raise OSError(errno.ENODEV,
641
"/dev/null not a character device")
642
os.dup2(null, sys.stdin.fileno())
643
os.dup2(null, sys.stdout.fileno())
644
os.dup2(null, sys.stderr.fileno())
650
global main_loop_started
651
main_loop_started = False
653
212
parser = OptionParser()
654
213
parser.add_option("-i", "--interface", type="string",
655
metavar="IF", help="Bind to interface IF")
656
parser.add_option("-a", "--address", type="string",
657
help="Address to listen for requests on")
658
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,
659
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")
660
238
parser.add_option("--check", action="store_true", default=False,
661
239
help="Run self-test")
662
parser.add_option("--debug", action="store_true",
663
help="Debug mode; run in foreground and log to"
665
parser.add_option("--priority", type="string", help="GnuTLS"
666
" priority string (see GnuTLS documentation)")
667
parser.add_option("--servicename", type="string", metavar="NAME",
668
help="Zeroconf service name")
669
parser.add_option("--configdir", type="string",
670
default="/etc/mandos", metavar="DIR",
671
help="Directory to search for configuration"
673
240
(options, args) = parser.parse_args()
675
242
if options.check:
677
244
doctest.testmod()
680
# Default values for config file for server-global settings
681
server_defaults = { "interface": "",
686
"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
687
"servicename": "Mandos",
690
# Parse config file for server-global settings
691
server_config = ConfigParser.SafeConfigParser(server_defaults)
693
server_config.read(os.path.join(options.configdir, "mandos.conf"))
694
server_section = "server"
695
# Convert the SafeConfigParser object to a dict
696
server_settings = dict(server_config.items(server_section))
697
# Use getboolean on the boolean config option
698
server_settings["debug"] = server_config.getboolean\
699
(server_section, "debug")
702
# Override the settings from the config file with command line
704
for option in ("interface", "address", "port", "debug",
705
"priority", "servicename", "configdir"):
706
value = getattr(options, option)
707
if value is not None:
708
server_settings[option] = value
710
# Now we have our good server settings in "server_settings"
712
# Parse config file with clients
713
client_defaults = { "timeout": "1h",
715
"checker": "fping -q -- %%(host)s",
717
client_config = ConfigParser.SafeConfigParser(client_defaults)
718
client_config.read(os.path.join(server_settings["configdir"],
722
service = AvahiService(name = server_settings["servicename"],
723
type = "_mandos._tcp", );
724
if server_settings["interface"]:
725
service.interface = if_nametoindex(server_settings["interface"])
730
# From the Avahi example code
731
DBusGMainLoop(set_as_default=True )
732
main_loop = gobject.MainLoop()
733
bus = dbus.SystemBus()
734
server = dbus.Interface(
735
bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
736
avahi.DBUS_INTERFACE_SERVER )
737
# End of Avahi example code
739
debug = server_settings["debug"]
742
console = logging.StreamHandler()
743
# console.setLevel(logging.DEBUG)
744
console.setFormatter(logging.Formatter\
745
('%(levelname)s: %(message)s'))
746
logger.addHandler(console)
750
def remove_from_clients(client):
751
clients.remove(client)
753
logger.critical(u"No clients left, exiting")
756
clients.update(Set(Client(name = section,
757
stop_hook = remove_from_clients,
759
= dict(client_config.items(section)))
760
for section in client_config.sections()))
762
logger.critical(u"No clients defined")
768
pidfilename = "/var/run/mandos/mandos.pid"
771
pidfile = open(pidfilename, "w")
772
pidfile.write(str(pid) + "\n")
776
logger.error(u"Could not write %s file with PID %d",
777
pidfilename, os.getpid())
780
"Cleanup function; run on exit"
782
# From the Avahi example code
783
if not group is None:
786
# End of Avahi example code
789
client = clients.pop()
790
client.stop_hook = None
793
atexit.register(cleanup)
796
signal.signal(signal.SIGINT, signal.SIG_IGN)
797
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
798
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:
800
295
for client in clients:
803
tcp_server = IPv6_TCPServer((server_settings["address"],
804
server_settings["port"]),
806
settings=server_settings,
808
# Find out what port we got
809
service.port = tcp_server.socket.getsockname()[1]
810
logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
811
u" scope_id %d" % tcp_server.socket.getsockname())
813
#service.interface = tcp_server.socket.getsockname()[3]
816
# From the Avahi example code
817
server.connect_to_signal("StateChanged", server_state_changed)
819
server_state_changed(server.GetState())
820
except dbus.exceptions.DBusException, error:
821
logger.critical(u"DBusException: %s", error)
823
# End of Avahi example code
825
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
826
lambda *args, **kwargs:
827
tcp_server.handle_request\
828
(*args[2:], **kwargs) or True)
830
logger.debug(u"Starting main loop")
831
main_loop_started = True
833
except AvahiError, error:
834
logger.critical(u"AvahiError: %s" + unicode(error))
836
except KeyboardInterrupt:
840
if __name__ == '__main__':
296
client.stop_checker()
299
if __name__ == "__main__":