44
44
import gnutls.library.functions
45
45
import gnutls.library.constants
46
46
import gnutls.library.types
47
import ConfigParser as configparser
56
57
import logging.handlers
58
from contextlib import closing
66
62
from dbus.mainloop.glib import DBusGMainLoop
71
SO_BINDTODEVICE = socket.SO_BINDTODEVICE
72
except AttributeError:
74
from IN import SO_BINDTODEVICE
76
# From /usr/include/asm/socket.h
82
logger = logging.Logger(u'mandos')
83
syslogger = (logging.handlers.SysLogHandler
84
(facility = logging.handlers.SysLogHandler.LOG_DAEMON,
85
address = "/dev/log"))
86
syslogger.setFormatter(logging.Formatter
87
(u'Mandos [%(process)d]: %(levelname)s:'
65
# Brief description of the operation of this program:
67
# This server announces itself as a Zeroconf service. Connecting
68
# clients use the TLS protocol, with the unusual quirk that this
69
# server program acts as a TLS "client" while the connecting clients
70
# acts as a TLS "server". The clients (acting as a TLS "server") must
71
# supply an OpenPGP certificate, and the fingerprint of this
72
# certificate is used by this server to look up (in a list read from a
73
# file at start time) which binary blob to give the client. No other
74
# authentication or authorization is done by this server.
77
logger = logging.Logger('mandos')
78
syslogger = logging.handlers.SysLogHandler\
79
(facility = logging.handlers.SysLogHandler.LOG_DAEMON)
80
syslogger.setFormatter(logging.Formatter\
81
('%(levelname)s: %(message)s'))
89
82
logger.addHandler(syslogger)
91
console = logging.StreamHandler()
92
console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:'
95
logger.addHandler(console)
97
class AvahiError(Exception):
98
def __init__(self, value, *args, **kwargs):
100
super(AvahiError, self).__init__(value, *args, **kwargs)
101
def __unicode__(self):
102
return unicode(repr(self.value))
104
class AvahiServiceError(AvahiError):
107
class AvahiGroupError(AvahiError):
111
class AvahiService(object):
112
"""An Avahi (Zeroconf) service.
115
interface: integer; avahi.IF_UNSPEC or an interface index.
116
Used to optionally bind to the specified interface.
117
name: string; Example: u'Mandos'
118
type: string; Example: u'_mandos._tcp'.
119
See <http://www.dns-sd.org/ServiceTypes.html>
120
port: integer; what port to announce
121
TXT: list of strings; TXT record for the service
122
domain: string; Domain to publish on, default to .local if empty.
123
host: string; Host to publish records for, default is localhost
124
max_renames: integer; maximum number of renames
125
rename_count: integer; counter so we only rename after collisions
126
a sensible number of times
128
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
129
servicetype = None, port = None, TXT = None,
130
domain = u"", host = u"", max_renames = 32768,
131
protocol = avahi.PROTO_UNSPEC):
132
self.interface = interface
134
self.type = servicetype
136
self.TXT = TXT if TXT is not None else []
139
self.rename_count = 0
140
self.max_renames = max_renames
141
self.protocol = protocol
143
"""Derived from the Avahi example code"""
144
if self.rename_count >= self.max_renames:
145
logger.critical(u"No suitable Zeroconf service name found"
146
u" after %i retries, exiting.",
148
raise AvahiServiceError(u"Too many renames")
149
self.name = server.GetAlternativeServiceName(self.name)
150
logger.info(u"Changing Zeroconf service name to %r ...",
152
syslogger.setFormatter(logging.Formatter
153
(u'Mandos (%s) [%%(process)d]:'
154
u' %%(levelname)s: %%(message)s'
158
self.rename_count += 1
160
"""Derived from the Avahi example code"""
161
if group is not None:
164
"""Derived from the Avahi example code"""
167
group = dbus.Interface(bus.get_object
169
server.EntryGroupNew()),
170
avahi.DBUS_INTERFACE_ENTRY_GROUP)
171
group.connect_to_signal('StateChanged',
172
entry_group_state_changed)
173
logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
174
service.name, service.type)
176
self.interface, # interface
177
self.protocol, # protocol
178
dbus.UInt32(0), # flags
179
self.name, self.type,
180
self.domain, self.host,
181
dbus.UInt16(self.port),
182
avahi.string_array_to_txt_array(self.TXT))
85
# This variable is used to optionally bind to a specified interface.
86
# It is a global variable to fit in with the other variables from the
88
serviceInterface = avahi.IF_UNSPEC
185
89
# From the Avahi example code:
186
group = None # our entry group
90
serviceName = "Mandos"
91
serviceType = "_mandos._tcp" # http://www.dns-sd.org/ServiceTypes.html
92
servicePort = None # Not known at startup
93
serviceTXT = [] # TXT record for the service
94
domain = "" # Domain to publish on, default to .local
95
host = "" # Host to publish records for, default to localhost
96
group = None #our entry group
97
rename_count = 12 # Counter so we only rename after collisions a
98
# sensible number of times
187
99
# End of Avahi example code
190
def _datetime_to_dbus(dt, variant_level=0):
191
"""Convert a UTC datetime.datetime() to a D-Bus type."""
192
return dbus.String(dt.isoformat(), variant_level=variant_level)
195
102
class Client(object):
196
103
"""A representation of a client host served by this server.
199
name: string; from the config file, used in log messages and
105
name: string; from the config file, used in log messages
201
106
fingerprint: string (40 or 32 hexadecimal digits); used to
202
107
uniquely identify the client
203
secret: bytestring; sent verbatim (over TLS) to client
204
host: string; available for use by the checker command
205
created: datetime.datetime(); (UTC) object creation
206
last_enabled: datetime.datetime(); (UTC)
208
last_checked_ok: datetime.datetime(); (UTC) or None
209
timeout: datetime.timedelta(); How long from last_checked_ok
210
until this client is invalid
211
interval: datetime.timedelta(); How often to start a new checker
212
disable_hook: If set, called by disable() as disable_hook(self)
213
checker: subprocess.Popen(); a running checker process used
214
to see if the client lives.
215
'None' if no process is running.
108
secret: bytestring; sent verbatim (over TLS) to client
109
fqdn: string (FQDN); available for use by the checker command
110
created: datetime.datetime()
111
last_seen: datetime.datetime() or None if not yet seen
112
timeout: datetime.timedelta(); How long from last_seen until
113
this client is invalid
114
interval: datetime.timedelta(); How often to start a new checker
115
stop_hook: If set, called by stop() as stop_hook(self)
116
checker: subprocess.Popen(); a running checker process used
117
to see if the client lives.
118
Is None if no process is running.
216
119
checker_initiator_tag: a gobject event source tag, or None
217
disable_initiator_tag: - '' -
120
stop_initiator_tag: - '' -
218
121
checker_callback_tag: - '' -
219
122
checker_command: string; External command which is run to check if
220
client lives. %() expansions are done at
123
client lives. %()s expansions are done at
221
124
runtime with vars(self) as dict, so that for
222
125
instance %(name)s can be used in the command.
223
current_checker_command: string; current running checker_command
127
_timeout: Real variable for 'timeout'
128
_interval: Real variable for 'interval'
129
_timeout_milliseconds: Used by gobject.timeout_add()
130
_interval_milliseconds: - '' -
227
def _datetime_to_milliseconds(dt):
228
"Convert a datetime.datetime() to milliseconds"
229
return ((dt.days * 24 * 60 * 60 * 1000)
230
+ (dt.seconds * 1000)
231
+ (dt.microseconds // 1000))
233
def timeout_milliseconds(self):
234
"Return the 'timeout' attribute in milliseconds"
235
return self._datetime_to_milliseconds(self.timeout)
237
def interval_milliseconds(self):
238
"Return the 'interval' attribute in milliseconds"
239
return self._datetime_to_milliseconds(self.interval)
241
def __init__(self, name = None, disable_hook=None, config=None):
242
"""Note: the 'checker' key in 'config' sets the
243
'checker_command' attribute and *not* the 'checker'
132
def _set_timeout(self, timeout):
133
"Setter function for 'timeout' attribute"
134
self._timeout = timeout
135
self._timeout_milliseconds = ((self.timeout.days
136
* 24 * 60 * 60 * 1000)
137
+ (self.timeout.seconds * 1000)
138
+ (self.timeout.microseconds
140
timeout = property(lambda self: self._timeout,
143
def _set_interval(self, interval):
144
"Setter function for 'interval' attribute"
145
self._interval = interval
146
self._interval_milliseconds = ((self.interval.days
147
* 24 * 60 * 60 * 1000)
148
+ (self.interval.seconds
150
+ (self.interval.microseconds
152
interval = property(lambda self: self._interval,
155
def __init__(self, name=None, options=None, stop_hook=None,
156
fingerprint=None, secret=None, secfile=None,
157
fqdn=None, timeout=None, interval=-1, checker=None):
158
"""Note: the 'checker' argument sets the 'checker_command'
159
attribute and not the 'checker' attribute.."""
248
logger.debug(u"Creating client %r", self.name)
249
# Uppercase and remove spaces from fingerprint for later
250
# comparison purposes with return value from the fingerprint()
252
self.fingerprint = (config[u"fingerprint"].upper()
254
logger.debug(u" Fingerprint: %s", self.fingerprint)
255
if u"secret" in config:
256
self.secret = config[u"secret"].decode(u"base64")
257
elif u"secfile" in config:
258
with closing(open(os.path.expanduser
260
(config[u"secfile"])))) as secfile:
261
self.secret = secfile.read()
263
raise TypeError(u"No secret or secfile for client %s"
265
self.host = config.get(u"host", u"")
266
self.created = datetime.datetime.utcnow()
268
self.last_enabled = None
269
self.last_checked_ok = None
270
self.timeout = string_to_delta(config[u"timeout"])
271
self.interval = string_to_delta(config[u"interval"])
272
self.disable_hook = disable_hook
161
# Uppercase and remove spaces from fingerprint
162
# for later comparison purposes with return value of
163
# the fingerprint() function
164
self.fingerprint = fingerprint.upper().replace(u" ", u"")
166
self.secret = secret.decode(u"base64")
169
self.secret = sf.read()
172
raise RuntimeError(u"No secret or secfile for client %s"
174
self.fqdn = fqdn # string
175
self.created = datetime.datetime.now()
176
self.last_seen = None
178
self.timeout = options.timeout
180
self.timeout = string_to_delta(timeout)
182
self.interval = options.interval
184
self.interval = string_to_delta(interval)
185
self.stop_hook = stop_hook
273
186
self.checker = None
274
187
self.checker_initiator_tag = None
275
self.disable_initiator_tag = None
188
self.stop_initiator_tag = None
276
189
self.checker_callback_tag = None
277
self.checker_command = config[u"checker"]
278
self.current_checker_command = None
279
self.last_connect = None
190
self.check_command = checker
282
192
"""Start this client's checker and timeout hooks"""
283
self.last_enabled = datetime.datetime.utcnow()
284
193
# Schedule a new checker to be started an 'interval' from now,
285
194
# and every interval from then on.
286
self.checker_initiator_tag = (gobject.timeout_add
287
(self.interval_milliseconds(),
195
self.checker_initiator_tag = gobject.timeout_add\
196
(self._interval_milliseconds,
289
198
# Also start a new checker *right now*.
290
199
self.start_checker()
291
# Schedule a disable() when 'timeout' has passed
292
self.disable_initiator_tag = (gobject.timeout_add
293
(self.timeout_milliseconds(),
298
"""Disable this client."""
299
if not getattr(self, "enabled", False):
200
# Schedule a stop() when 'timeout' has passed
201
self.stop_initiator_tag = gobject.timeout_add\
202
(self._timeout_milliseconds,
206
The possibility that this client might be restarted is left
207
open, but not currently used."""
208
# If this client doesn't have a secret, it is already stopped.
210
logger.debug(u"Stopping client %s", self.name)
301
logger.info(u"Disabling client %s", self.name)
302
if getattr(self, u"disable_initiator_tag", False):
303
gobject.source_remove(self.disable_initiator_tag)
304
self.disable_initiator_tag = None
305
if getattr(self, u"checker_initiator_tag", False):
214
if hasattr(self, "stop_initiator_tag") \
215
and self.stop_initiator_tag:
216
gobject.source_remove(self.stop_initiator_tag)
217
self.stop_initiator_tag = None
218
if hasattr(self, "checker_initiator_tag") \
219
and self.checker_initiator_tag:
306
220
gobject.source_remove(self.checker_initiator_tag)
307
221
self.checker_initiator_tag = None
308
222
self.stop_checker()
309
if self.disable_hook:
310
self.disable_hook(self)
312
225
# Do not run this again if called by a gobject.timeout_add
315
227
def __del__(self):
316
self.disable_hook = None
319
def checker_callback(self, pid, condition, command):
228
self.stop_hook = None
230
def checker_callback(self, pid, condition):
320
231
"""The checker has completed, so take appropriate actions."""
232
now = datetime.datetime.now()
321
233
self.checker_callback_tag = None
322
234
self.checker = None
323
if os.WIFEXITED(condition):
324
exitstatus = os.WEXITSTATUS(condition)
326
logger.info(u"Checker for %(name)s succeeded",
330
logger.info(u"Checker for %(name)s failed",
235
if os.WIFEXITED(condition) \
236
and (os.WEXITSTATUS(condition) == 0):
237
logger.debug(u"Checker for %(name)s succeeded",
240
gobject.source_remove(self.stop_initiator_tag)
241
self.stop_initiator_tag = gobject.timeout_add\
242
(self._timeout_milliseconds,
244
elif not os.WIFEXITED(condition):
333
245
logger.warning(u"Checker for %(name)s crashed?",
336
def checked_ok(self):
337
"""Bump up the timeout for this client.
339
This should only be called when the client has been seen,
342
self.last_checked_ok = datetime.datetime.utcnow()
343
gobject.source_remove(self.disable_initiator_tag)
344
self.disable_initiator_tag = (gobject.timeout_add
345
(self.timeout_milliseconds(),
248
logger.debug(u"Checker for %(name)s failed",
348
250
def start_checker(self):
349
251
"""Start a new checker subprocess if one is not running.
351
252
If a checker already exists, leave it running and do
353
254
# The reason for not killing a running checker is that if we
358
259
# checkers alone, the checker would have to take more time
359
260
# than 'timeout' for the client to be declared invalid, which
360
261
# is as it should be.
362
# If a checker exists, make sure it is not a zombie
363
if self.checker is not None:
364
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
366
logger.warning(u"Checker was a zombie")
367
gobject.source_remove(self.checker_callback_tag)
368
self.checker_callback(pid, status,
369
self.current_checker_command)
370
# Start a new checker if needed
371
262
if self.checker is None:
373
# In case checker_command has exactly one % operator
374
command = self.checker_command % self.host
264
command = self.check_command % self.fqdn
375
265
except TypeError:
376
# Escape attributes for the shell
377
escaped_attrs = dict((key,
378
re.escape(unicode(str(val),
266
escaped_attrs = dict((key, re.escape(str(val)))
382
268
vars(self).iteritems())
384
command = self.checker_command % escaped_attrs
270
command = self.check_command % escaped_attrs
385
271
except TypeError, error:
386
logger.error(u'Could not format string "%s":'
387
u' %s', self.checker_command, error)
272
logger.critical(u'Could not format string "%s":'
273
u' %s', self.check_command, error)
388
274
return True # Try again later
389
self.current_checker_command = command
391
logger.info(u"Starting checker %r for %s",
393
# We don't need to redirect stdout and stderr, since
394
# in normal mode, that is already done by daemon(),
395
# and in debug mode we don't want to. (Stdin is
396
# always replaced by /dev/null.)
397
self.checker = subprocess.Popen(command,
399
shell=True, cwd=u"/")
400
self.checker_callback_tag = (gobject.child_watch_add
402
self.checker_callback,
404
# The checker may have completed before the gobject
405
# watch was added. Check for this.
406
pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
408
gobject.source_remove(self.checker_callback_tag)
409
self.checker_callback(pid, status, command)
410
except OSError, error:
276
logger.debug(u"Starting checker %r for %s",
278
self.checker = subprocess.\
280
close_fds=True, shell=True,
282
self.checker_callback_tag = gobject.child_watch_add\
284
self.checker_callback)
285
except subprocess.OSError, error:
411
286
logger.error(u"Failed to start subprocess: %s",
413
288
# Re-run this periodically if run by gobject.timeout_add
416
290
def stop_checker(self):
417
291
"""Force the checker process, if any, to stop."""
418
292
if self.checker_callback_tag:
419
293
gobject.source_remove(self.checker_callback_tag)
420
294
self.checker_callback_tag = None
421
if getattr(self, u"checker", None) is None:
295
if not hasattr(self, "checker") or self.checker is None:
423
logger.debug(u"Stopping checker for %(name)s", vars(self))
297
logger.debug("Stopping checker for %(name)s", vars(self))
425
299
os.kill(self.checker.pid, signal.SIGTERM)
427
301
#if self.checker.poll() is None:
428
302
# os.kill(self.checker.pid, signal.SIGKILL)
429
303
except OSError, error:
430
if error.errno != errno.ESRCH: # No such process
304
if error.errno != errno.ESRCH:
432
306
self.checker = None
434
def still_valid(self):
307
def still_valid(self, now=None):
435
308
"""Has the timeout not yet passed for this client?"""
436
if not getattr(self, u"enabled", False):
438
now = datetime.datetime.utcnow()
439
if self.last_checked_ok is None:
310
now = datetime.datetime.now()
311
if self.last_seen is None:
440
312
return now < (self.created + self.timeout)
442
return now < (self.last_checked_ok + self.timeout)
445
class ClientDBus(Client, dbus.service.Object):
446
"""A Client class using D-Bus
449
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
451
# dbus.service.Object doesn't use super(), so we can't either.
453
def __init__(self, *args, **kwargs):
454
Client.__init__(self, *args, **kwargs)
455
# Only now, when this client is initialized, can it show up on
457
self.dbus_object_path = (dbus.ObjectPath
459
+ self.name.replace(u".", u"_")))
460
dbus.service.Object.__init__(self, bus,
461
self.dbus_object_path)
463
oldstate = getattr(self, u"enabled", False)
464
r = Client.enable(self)
465
if oldstate != self.enabled:
467
self.PropertyChanged(dbus.String(u"enabled"),
468
dbus.Boolean(True, variant_level=1))
469
self.PropertyChanged(dbus.String(u"last_enabled"),
470
(_datetime_to_dbus(self.last_enabled,
474
def disable(self, signal = True):
475
oldstate = getattr(self, u"enabled", False)
476
r = Client.disable(self)
477
if signal and oldstate != self.enabled:
479
self.PropertyChanged(dbus.String(u"enabled"),
480
dbus.Boolean(False, variant_level=1))
483
def __del__(self, *args, **kwargs):
485
self.remove_from_connection()
488
if hasattr(dbus.service.Object, u"__del__"):
489
dbus.service.Object.__del__(self, *args, **kwargs)
490
Client.__del__(self, *args, **kwargs)
492
def checker_callback(self, pid, condition, command,
494
self.checker_callback_tag = None
497
self.PropertyChanged(dbus.String(u"checker_running"),
498
dbus.Boolean(False, variant_level=1))
499
if os.WIFEXITED(condition):
500
exitstatus = os.WEXITSTATUS(condition)
502
self.CheckerCompleted(dbus.Int16(exitstatus),
503
dbus.Int64(condition),
504
dbus.String(command))
507
self.CheckerCompleted(dbus.Int16(-1),
508
dbus.Int64(condition),
509
dbus.String(command))
511
return Client.checker_callback(self, pid, condition, command,
514
def checked_ok(self, *args, **kwargs):
515
r = Client.checked_ok(self, *args, **kwargs)
517
self.PropertyChanged(
518
dbus.String(u"last_checked_ok"),
519
(_datetime_to_dbus(self.last_checked_ok,
523
def start_checker(self, *args, **kwargs):
524
old_checker = self.checker
525
if self.checker is not None:
526
old_checker_pid = self.checker.pid
528
old_checker_pid = None
529
r = Client.start_checker(self, *args, **kwargs)
530
# Only if new checker process was started
531
if (self.checker is not None
532
and old_checker_pid != self.checker.pid):
534
self.CheckerStarted(self.current_checker_command)
535
self.PropertyChanged(
536
dbus.String(u"checker_running"),
537
dbus.Boolean(True, variant_level=1))
540
def stop_checker(self, *args, **kwargs):
541
old_checker = getattr(self, u"checker", None)
542
r = Client.stop_checker(self, *args, **kwargs)
543
if (old_checker is not None
544
and getattr(self, u"checker", None) is None):
545
self.PropertyChanged(dbus.String(u"checker_running"),
546
dbus.Boolean(False, variant_level=1))
549
## D-Bus methods & signals
550
_interface = u"se.bsnet.fukt.Mandos.Client"
553
@dbus.service.method(_interface)
555
return self.checked_ok()
557
# CheckerCompleted - signal
558
@dbus.service.signal(_interface, signature=u"nxs")
559
def CheckerCompleted(self, exitcode, waitstatus, command):
563
# CheckerStarted - signal
564
@dbus.service.signal(_interface, signature=u"s")
565
def CheckerStarted(self, command):
569
# GetAllProperties - method
570
@dbus.service.method(_interface, out_signature=u"a{sv}")
571
def GetAllProperties(self):
573
return dbus.Dictionary({
574
dbus.String(u"name"):
575
dbus.String(self.name, variant_level=1),
576
dbus.String(u"fingerprint"):
577
dbus.String(self.fingerprint, variant_level=1),
578
dbus.String(u"host"):
579
dbus.String(self.host, variant_level=1),
580
dbus.String(u"created"):
581
_datetime_to_dbus(self.created, variant_level=1),
582
dbus.String(u"last_enabled"):
583
(_datetime_to_dbus(self.last_enabled,
585
if self.last_enabled is not None
586
else dbus.Boolean(False, variant_level=1)),
587
dbus.String(u"enabled"):
588
dbus.Boolean(self.enabled, variant_level=1),
589
dbus.String(u"last_checked_ok"):
590
(_datetime_to_dbus(self.last_checked_ok,
592
if self.last_checked_ok is not None
593
else dbus.Boolean (False, variant_level=1)),
594
dbus.String(u"timeout"):
595
dbus.UInt64(self.timeout_milliseconds(),
597
dbus.String(u"interval"):
598
dbus.UInt64(self.interval_milliseconds(),
600
dbus.String(u"checker"):
601
dbus.String(self.checker_command,
603
dbus.String(u"checker_running"):
604
dbus.Boolean(self.checker is not None,
606
dbus.String(u"object_path"):
607
dbus.ObjectPath(self.dbus_object_path,
611
# IsStillValid - method
612
@dbus.service.method(_interface, out_signature=u"b")
613
def IsStillValid(self):
614
return self.still_valid()
616
# PropertyChanged - signal
617
@dbus.service.signal(_interface, signature=u"sv")
618
def PropertyChanged(self, property, value):
622
# ReceivedSecret - signal
623
@dbus.service.signal(_interface)
624
def ReceivedSecret(self):
629
@dbus.service.signal(_interface)
634
# SetChecker - method
635
@dbus.service.method(_interface, in_signature=u"s")
636
def SetChecker(self, checker):
637
"D-Bus setter method"
638
self.checker_command = checker
640
self.PropertyChanged(dbus.String(u"checker"),
641
dbus.String(self.checker_command,
645
@dbus.service.method(_interface, in_signature=u"s")
646
def SetHost(self, host):
647
"D-Bus setter method"
650
self.PropertyChanged(dbus.String(u"host"),
651
dbus.String(self.host, variant_level=1))
653
# SetInterval - method
654
@dbus.service.method(_interface, in_signature=u"t")
655
def SetInterval(self, milliseconds):
656
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
658
self.PropertyChanged(dbus.String(u"interval"),
659
(dbus.UInt64(self.interval_milliseconds(),
663
@dbus.service.method(_interface, in_signature=u"ay",
665
def SetSecret(self, secret):
666
"D-Bus setter method"
667
self.secret = str(secret)
669
# SetTimeout - method
670
@dbus.service.method(_interface, in_signature=u"t")
671
def SetTimeout(self, milliseconds):
672
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
674
self.PropertyChanged(dbus.String(u"timeout"),
675
(dbus.UInt64(self.timeout_milliseconds(),
679
@dbus.service.method(_interface)
684
# StartChecker - method
685
@dbus.service.method(_interface)
686
def StartChecker(self):
691
@dbus.service.method(_interface)
696
# StopChecker - method
697
@dbus.service.method(_interface)
698
def StopChecker(self):
704
class ClientHandler(socketserver.BaseRequestHandler, object):
705
"""A class to handle client connections.
707
Instantiated once for each connection to handle it.
314
return now < (self.last_seen + self.timeout)
317
def peer_certificate(session):
318
"Return the peer's OpenPGP certificate as a bytestring"
319
# If not an OpenPGP certificate...
320
if gnutls.library.functions.gnutls_certificate_type_get\
321
(session._c_object) \
322
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP:
323
# ...do the normal thing
324
return session.peer_certificate
325
list_size = ctypes.c_uint()
326
cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
327
(session._c_object, ctypes.byref(list_size))
328
if list_size.value == 0:
331
return ctypes.string_at(cert.data, cert.size)
334
def fingerprint(openpgp):
335
"Convert an OpenPGP bytestring to a hexdigit fingerprint string"
336
# New empty GnuTLS certificate
337
crt = gnutls.library.types.gnutls_openpgp_crt_t()
338
gnutls.library.functions.gnutls_openpgp_crt_init\
340
# New GnuTLS "datum" with the OpenPGP public key
341
datum = gnutls.library.types.gnutls_datum_t\
342
(ctypes.cast(ctypes.c_char_p(openpgp),
343
ctypes.POINTER(ctypes.c_ubyte)),
344
ctypes.c_uint(len(openpgp)))
345
# Import the OpenPGP public key into the certificate
346
ret = gnutls.library.functions.gnutls_openpgp_crt_import\
349
gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
350
# New buffer for the fingerprint
351
buffer = ctypes.create_string_buffer(20)
352
buffer_length = ctypes.c_size_t()
353
# Get the fingerprint from the certificate into the buffer
354
gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
355
(crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
356
# Deinit the certificate
357
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
358
# Convert the buffer to a Python bytestring
359
fpr = ctypes.string_at(buffer, buffer_length.value)
360
# Convert the bytestring to hexadecimal notation
361
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
365
class tcp_handler(SocketServer.BaseRequestHandler, object):
366
"""A TCP request handler class.
367
Instantiated by IPv6_TCPServer for each request to handle it.
708
368
Note: This will run in its own forked process."""
710
370
def handle(self):
711
logger.info(u"TCP connection from: %s",
712
unicode(self.client_address))
713
logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
714
# Open IPC pipe to parent process
715
with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
716
session = (gnutls.connection
717
.ClientSession(self.request,
721
line = self.request.makefile().readline()
722
logger.debug(u"Protocol version: %r", line)
724
if int(line.strip().split()[0]) > 1:
726
except (ValueError, IndexError, RuntimeError), error:
727
logger.error(u"Unknown protocol version: %s", error)
730
# Note: gnutls.connection.X509Credentials is really a
731
# generic GnuTLS certificate credentials object so long as
732
# no X.509 keys are added to it. Therefore, we can use it
733
# here despite using OpenPGP certificates.
735
#priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
736
# u"+AES-256-CBC", u"+SHA1",
737
# u"+COMP-NULL", u"+CTYPE-OPENPGP",
739
# Use a fallback default, since this MUST be set.
740
priority = self.server.gnutls_priority
743
(gnutls.library.functions
744
.gnutls_priority_set_direct(session._c_object,
749
except gnutls.errors.GNUTLSError, error:
750
logger.warning(u"Handshake failed: %s", error)
751
# Do not run session.bye() here: the session is not
752
# established. Just abandon the request.
754
logger.debug(u"Handshake succeeded")
756
fpr = self.fingerprint(self.peer_certificate(session))
757
except (TypeError, gnutls.errors.GNUTLSError), error:
758
logger.warning(u"Bad certificate: %s", error)
761
logger.debug(u"Fingerprint: %s", fpr)
763
for c in self.server.clients:
764
if c.fingerprint == fpr:
371
logger.debug(u"TCP connection from: %s",
372
unicode(self.client_address))
373
session = gnutls.connection.ClientSession(self.request,
377
#priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
378
# "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
380
priority = "SECURE256"
382
gnutls.library.functions.gnutls_priority_set_direct\
383
(session._c_object, priority, None);
387
except gnutls.errors.GNUTLSError, error:
388
logger.debug(u"Handshake failed: %s", error)
389
# Do not run session.bye() here: the session is not
390
# established. Just abandon the request.
393
fpr = fingerprint(peer_certificate(session))
394
except (TypeError, gnutls.errors.GNUTLSError), error:
395
logger.debug(u"Bad certificate: %s", error)
398
logger.debug(u"Fingerprint: %s", fpr)
400
for c in self.server.clients:
401
if c.fingerprint == fpr:
404
# Have to check if client.still_valid(), since it is possible
405
# that the client timed out while establishing the GnuTLS
407
if (not client) or (not client.still_valid()):
409
logger.debug(u"Client %(name)s is invalid",
768
ipc.write(u"NOTFOUND %s\n" % fpr)
771
# Have to check if client.still_valid(), since it is
772
# possible that the client timed out while establishing
773
# the GnuTLS session.
774
if not client.still_valid():
775
ipc.write(u"INVALID %s\n" % client.name)
778
ipc.write(u"SENDING %s\n" % client.name)
780
while sent_size < len(client.secret):
781
sent = session.send(client.secret[sent_size:])
782
logger.debug(u"Sent: %d, remaining: %d",
783
sent, len(client.secret)
784
- (sent_size + sent))
412
logger.debug(u"Client not found for fingerprint: %s",
789
def peer_certificate(session):
790
"Return the peer's OpenPGP certificate as a bytestring"
791
# If not an OpenPGP certificate...
792
if (gnutls.library.functions
793
.gnutls_certificate_type_get(session._c_object)
794
!= gnutls.library.constants.GNUTLS_CRT_OPENPGP):
795
# ...do the normal thing
796
return session.peer_certificate
797
list_size = ctypes.c_uint(1)
798
cert_list = (gnutls.library.functions
799
.gnutls_certificate_get_peers
800
(session._c_object, ctypes.byref(list_size)))
801
if not bool(cert_list) and list_size.value != 0:
802
raise gnutls.errors.GNUTLSError(u"error getting peer"
804
if list_size.value == 0:
807
return ctypes.string_at(cert.data, cert.size)
810
def fingerprint(openpgp):
811
"Convert an OpenPGP bytestring to a hexdigit fingerprint"
812
# New GnuTLS "datum" with the OpenPGP public key
813
datum = (gnutls.library.types
814
.gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
817
ctypes.c_uint(len(openpgp))))
818
# New empty GnuTLS certificate
819
crt = gnutls.library.types.gnutls_openpgp_crt_t()
820
(gnutls.library.functions
821
.gnutls_openpgp_crt_init(ctypes.byref(crt)))
822
# Import the OpenPGP public key into the certificate
823
(gnutls.library.functions
824
.gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
825
gnutls.library.constants
826
.GNUTLS_OPENPGP_FMT_RAW))
827
# Verify the self signature in the key
828
crtverify = ctypes.c_uint()
829
(gnutls.library.functions
830
.gnutls_openpgp_crt_verify_self(crt, 0,
831
ctypes.byref(crtverify)))
832
if crtverify.value != 0:
833
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
834
raise (gnutls.errors.CertificateSecurityError
836
# New buffer for the fingerprint
837
buf = ctypes.create_string_buffer(20)
838
buf_len = ctypes.c_size_t()
839
# Get the fingerprint from the certificate into the buffer
840
(gnutls.library.functions
841
.gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
842
ctypes.byref(buf_len)))
843
# Deinit the certificate
844
gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
845
# Convert the buffer to a Python bytestring
846
fpr = ctypes.string_at(buf, buf_len.value)
847
# Convert the bytestring to hexadecimal notation
848
hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
852
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
853
"""Like socketserver.ForkingMixIn, but also pass a pipe.
855
Assumes a gobject.MainLoop event loop.
857
def process_request(self, request, client_address):
858
"""Overrides and wraps the original process_request().
860
This function creates a new pipe in self.pipe
862
self.pipe = os.pipe()
863
super(ForkingMixInWithPipe,
864
self).process_request(request, client_address)
865
os.close(self.pipe[1]) # close write end
866
# Call "handle_ipc" for both data and EOF events
867
gobject.io_add_watch(self.pipe[0],
868
gobject.IO_IN | gobject.IO_HUP,
870
def handle_ipc(source, condition):
871
"""Dummy function; override as necessary"""
876
class IPv6_TCPServer(ForkingMixInWithPipe,
877
socketserver.TCPServer, object):
878
"""IPv6-capable TCP server. Accepts 'None' as address and/or port
417
while sent_size < len(client.secret):
418
sent = session.send(client.secret[sent_size:])
419
logger.debug(u"Sent: %d, remaining: %d",
420
sent, len(client.secret)
421
- (sent_size + sent))
426
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
427
"""IPv6 TCP server. Accepts 'None' as address and/or port.
881
enabled: Boolean; whether this server is activated yet
882
interface: None or a network interface name (string)
883
use_ipv6: Boolean; to use IPv6 or not
885
clients: set of Client objects
886
gnutls_priority GnuTLS priority string
887
use_dbus: Boolean; to emit D-Bus signals or not
429
options: Command line options
430
clients: Set() of Client objects
889
def __init__(self, server_address, RequestHandlerClass,
890
interface=None, use_ipv6=True, clients=None,
891
gnutls_priority=None, use_dbus=True):
893
self.interface = interface
895
self.address_family = socket.AF_INET6
896
self.clients = clients
897
self.use_dbus = use_dbus
898
self.gnutls_priority = gnutls_priority
899
socketserver.TCPServer.__init__(self, server_address,
432
address_family = socket.AF_INET6
433
def __init__(self, *args, **kwargs):
434
if "options" in kwargs:
435
self.options = kwargs["options"]
436
del kwargs["options"]
437
if "clients" in kwargs:
438
self.clients = kwargs["clients"]
439
del kwargs["clients"]
440
return super(type(self), self).__init__(*args, **kwargs)
901
441
def server_bind(self):
902
442
"""This overrides the normal server_bind() function
903
443
to bind to an interface if one was specified, and also NOT to
904
444
bind to an address or port if they were not specified."""
905
if self.interface is not None:
445
if self.options.interface:
446
if not hasattr(socket, "SO_BINDTODEVICE"):
447
# From /usr/include/asm-i486/socket.h
448
socket.SO_BINDTODEVICE = 25
907
450
self.socket.setsockopt(socket.SOL_SOCKET,
909
str(self.interface + u'\0'))
451
socket.SO_BINDTODEVICE,
452
self.options.interface)
910
453
except socket.error, error:
911
454
if error[0] == errno.EPERM:
912
logger.error(u"No permission to"
913
u" bind to interface %s",
455
logger.warning(u"No permission to"
456
u" bind to interface %s",
457
self.options.interface)
917
460
# Only bind(2) the socket if we really need to.
918
461
if self.server_address[0] or self.server_address[1]:
919
462
if not self.server_address[0]:
920
if self.address_family == socket.AF_INET6:
921
any_address = u"::" # in6addr_any
923
any_address = socket.INADDR_ANY
924
self.server_address = (any_address,
464
self.server_address = (in6addr_any,
925
465
self.server_address[1])
926
elif not self.server_address[1]:
466
elif self.server_address[1] is None:
927
467
self.server_address = (self.server_address[0],
930
# self.server_address = (self.server_address[0],
935
return socketserver.TCPServer.server_bind(self)
936
def server_activate(self):
938
return socketserver.TCPServer.server_activate(self)
941
def handle_ipc(self, source, condition, file_objects={}):
943
gobject.IO_IN: u"IN", # There is data to read.
944
gobject.IO_OUT: u"OUT", # Data can be written (without
946
gobject.IO_PRI: u"PRI", # There is urgent data to read.
947
gobject.IO_ERR: u"ERR", # Error condition.
948
gobject.IO_HUP: u"HUP" # Hung up (the connection has been
949
# broken, usually for pipes and
952
conditions_string = ' | '.join(name
954
condition_names.iteritems()
956
logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
959
# Turn the pipe file descriptor into a Python file object
960
if source not in file_objects:
961
file_objects[source] = os.fdopen(source, u"r", 1)
963
# Read a line from the file object
964
cmdline = file_objects[source].readline()
965
if not cmdline: # Empty line means end of file
967
file_objects[source].close()
968
del file_objects[source]
970
# Stop calling this function
973
logger.debug(u"IPC command: %r", cmdline)
975
# Parse and act on command
976
cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
978
if cmd == u"NOTFOUND":
979
logger.warning(u"Client not found for fingerprint: %s",
983
mandos_dbus_service.ClientNotFound(args)
984
elif cmd == u"INVALID":
985
for client in self.clients:
986
if client.name == args:
987
logger.warning(u"Client %s is invalid", args)
993
logger.error(u"Unknown client %s is invalid", args)
994
elif cmd == u"SENDING":
995
for client in self.clients:
996
if client.name == args:
997
logger.info(u"Sending secret to %s", client.name)
1001
client.ReceivedSecret()
1004
logger.error(u"Sending secret to unknown client %s",
1007
logger.error(u"Unknown IPC command: %r", cmdline)
1009
# Keep calling this function
469
return super(type(self), self).server_bind()
1013
472
def string_to_delta(interval):
1014
473
"""Parse a string and return a datetime.timedelta
1016
>>> string_to_delta(u'7d')
475
>>> string_to_delta('7d')
1017
476
datetime.timedelta(7)
1018
>>> string_to_delta(u'60s')
477
>>> string_to_delta('60s')
1019
478
datetime.timedelta(0, 60)
1020
>>> string_to_delta(u'60m')
479
>>> string_to_delta('60m')
1021
480
datetime.timedelta(0, 3600)
1022
>>> string_to_delta(u'24h')
481
>>> string_to_delta('24h')
1023
482
datetime.timedelta(1)
1024
483
>>> string_to_delta(u'1w')
1025
484
datetime.timedelta(7)
1026
>>> string_to_delta(u'5m 30s')
1027
datetime.timedelta(0, 330)
1029
timevalue = datetime.timedelta(0)
1030
for s in interval.split():
1032
suffix = unicode(s[-1])
1035
delta = datetime.timedelta(value)
1036
elif suffix == u"s":
1037
delta = datetime.timedelta(0, value)
1038
elif suffix == u"m":
1039
delta = datetime.timedelta(0, 0, 0, 0, value)
1040
elif suffix == u"h":
1041
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
1042
elif suffix == u"w":
1043
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1046
except (ValueError, IndexError):
487
suffix=unicode(interval[-1])
488
value=int(interval[:-1])
490
delta = datetime.timedelta(value)
492
delta = datetime.timedelta(0, value)
494
delta = datetime.timedelta(0, 0, 0, 0, value)
496
delta = datetime.timedelta(0, 0, 0, 0, 0, value)
498
delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1047
500
raise ValueError
501
except (ValueError, IndexError):
507
"""Derived from the Avahi example code"""
508
global group, serviceName, serviceType, servicePort, serviceTXT, \
511
group = dbus.Interface(
512
bus.get_object( avahi.DBUS_NAME,
513
server.EntryGroupNew()),
514
avahi.DBUS_INTERFACE_ENTRY_GROUP)
515
group.connect_to_signal('StateChanged',
516
entry_group_state_changed)
517
logger.debug(u"Adding service '%s' of type '%s' ...",
518
serviceName, serviceType)
521
serviceInterface, # interface
522
avahi.PROTO_INET6, # protocol
523
dbus.UInt32(0), # flags
524
serviceName, serviceType,
526
dbus.UInt16(servicePort),
527
avahi.string_array_to_txt_array(serviceTXT))
531
def remove_service():
532
"""From the Avahi example code"""
535
if not group is None:
1052
539
def server_state_changed(state):
1053
540
"""Derived from the Avahi example code"""
1054
541
if state == avahi.SERVER_COLLISION:
1055
logger.error(u"Zeroconf server name collision")
542
logger.warning(u"Server name collision")
1057
544
elif state == avahi.SERVER_RUNNING:
1061
548
def entry_group_state_changed(state, error):
1062
549
"""Derived from the Avahi example code"""
1063
logger.debug(u"Avahi state change: %i", state)
550
global serviceName, server, rename_count
552
logger.debug(u"state change: %i", state)
1065
554
if state == avahi.ENTRY_GROUP_ESTABLISHED:
1066
logger.debug(u"Zeroconf service established.")
555
logger.debug(u"Service established.")
1067
556
elif state == avahi.ENTRY_GROUP_COLLISION:
1068
logger.warning(u"Zeroconf service name collision.")
558
rename_count = rename_count - 1
560
name = server.GetAlternativeServiceName(name)
561
logger.warning(u"Service name collision, "
562
u"changing name to '%s' ...", name)
567
logger.error(u"No suitable service name found after %i"
568
u" retries, exiting.", n_rename)
1070
570
elif state == avahi.ENTRY_GROUP_FAILURE:
1071
logger.critical(u"Avahi: Error in group state changed %s",
1073
raise AvahiGroupError(u"State changed: %s" % unicode(error))
571
logger.error(u"Error in group state changed %s",
1075
576
def if_nametoindex(interface):
1076
"""Call the C function if_nametoindex(), or equivalent
1078
Note: This function cannot accept a unicode string."""
1079
global if_nametoindex
577
"""Call the C function if_nametoindex()"""
1081
if_nametoindex = (ctypes.cdll.LoadLibrary
1082
(ctypes.util.find_library(u"c"))
579
libc = ctypes.cdll.LoadLibrary("libc.so.6")
580
return libc.if_nametoindex(interface)
1084
581
except (OSError, AttributeError):
1085
logger.warning(u"Doing if_nametoindex the hard way")
1086
def if_nametoindex(interface):
1087
"Get an interface index the hard way, i.e. using fcntl()"
1088
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
1089
with closing(socket.socket()) as s:
1090
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1091
struct.pack(str(u"16s16x"),
1093
interface_index = struct.unpack(str(u"I"),
1095
return interface_index
1096
return if_nametoindex(interface)
1099
def daemon(nochdir = False, noclose = False):
582
if "struct" not in sys.modules:
584
if "fcntl" not in sys.modules:
586
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
588
ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
589
struct.pack("16s16x", interface))
591
interface_index = struct.unpack("I", ifreq[16:20])[0]
592
return interface_index
595
def daemon(nochdir, noclose):
1100
596
"""See daemon(3). Standard BSD Unix function.
1102
597
This should really exist as os.daemon, but it doesn't (yet)."""
1111
604
# Close all standard open file descriptors
1112
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
605
null = os.open("/dev/null", os.O_NOCTTY | os.O_RDWR)
1113
606
if not stat.S_ISCHR(os.fstat(null).st_mode):
1114
607
raise OSError(errno.ENODEV,
1115
u"/dev/null not a character device")
608
"/dev/null not a character device")
1116
609
os.dup2(null, sys.stdin.fileno())
1117
610
os.dup2(null, sys.stdout.fileno())
1118
611
os.dup2(null, sys.stderr.fileno())
616
def killme(status = 0):
617
logger.debug("Stopping server with exit status %d", status)
619
if main_loop_started:
1125
######################################################################
1126
# Parsing of options, both command line and config file
1128
parser = optparse.OptionParser(version = "%%prog %s" % version)
1129
parser.add_option("-i", u"--interface", type=u"string",
1130
metavar="IF", help=u"Bind to interface IF")
1131
parser.add_option("-a", u"--address", type=u"string",
1132
help=u"Address to listen for requests on")
1133
parser.add_option("-p", u"--port", type=u"int",
1134
help=u"Port number to receive requests on")
1135
parser.add_option("--check", action=u"store_true",
1136
help=u"Run self-test")
1137
parser.add_option("--debug", action=u"store_true",
1138
help=u"Debug mode; run in foreground and log to"
1140
parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1141
u" priority string (see GnuTLS documentation)")
1142
parser.add_option("--servicename", type=u"string",
1143
metavar=u"NAME", help=u"Zeroconf service name")
1144
parser.add_option("--configdir", type=u"string",
1145
default=u"/etc/mandos", metavar=u"DIR",
1146
help=u"Directory to search for configuration"
1148
parser.add_option("--no-dbus", action=u"store_false",
1149
dest=u"use_dbus", help=u"Do not provide D-Bus"
1150
u" system bus interface")
1151
parser.add_option("--no-ipv6", action=u"store_false",
1152
dest=u"use_ipv6", help=u"Do not use IPv6")
1153
options = parser.parse_args()[0]
628
global main_loop_started
629
main_loop_started = False
631
parser = OptionParser()
632
parser.add_option("-i", "--interface", type="string",
633
default=None, metavar="IF",
634
help="Bind to interface IF")
635
parser.add_option("-a", "--address", type="string", default=None,
636
help="Address to listen for requests on")
637
parser.add_option("-p", "--port", type="int", default=None,
638
help="Port number to receive requests on")
639
parser.add_option("--timeout", type="string", # Parsed later
641
help="Amount of downtime allowed for clients")
642
parser.add_option("--interval", type="string", # Parsed later
644
help="How often to check that a client is up")
645
parser.add_option("--check", action="store_true", default=False,
646
help="Run self-test")
647
parser.add_option("--debug", action="store_true", default=False,
649
(options, args) = parser.parse_args()
1155
651
if options.check:
1157
653
doctest.testmod()
1160
# Default values for config file for server-global settings
1161
server_defaults = { u"interface": u"",
1166
u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1167
u"servicename": u"Mandos",
1168
u"use_dbus": u"True",
1169
u"use_ipv6": u"True",
1172
# Parse config file for server-global settings
1173
server_config = configparser.SafeConfigParser(server_defaults)
1175
server_config.read(os.path.join(options.configdir,
1177
# Convert the SafeConfigParser object to a dict
1178
server_settings = server_config.defaults()
1179
# Use the appropriate methods on the non-string config options
1180
for option in (u"debug", u"use_dbus", u"use_ipv6"):
1181
server_settings[option] = server_config.getboolean(u"DEFAULT",
1183
if server_settings["port"]:
1184
server_settings["port"] = server_config.getint(u"DEFAULT",
1188
# Override the settings from the config file with command line
1190
for option in (u"interface", u"address", u"port", u"debug",
1191
u"priority", u"servicename", u"configdir",
1192
u"use_dbus", u"use_ipv6"):
1193
value = getattr(options, option)
1194
if value is not None:
1195
server_settings[option] = value
1197
# Force all strings to be unicode
1198
for option in server_settings.keys():
1199
if type(server_settings[option]) is str:
1200
server_settings[option] = unicode(server_settings[option])
1201
# Now we have our good server settings in "server_settings"
1203
##################################################################
1206
debug = server_settings[u"debug"]
1207
use_dbus = server_settings[u"use_dbus"]
1208
use_ipv6 = server_settings[u"use_ipv6"]
1211
syslogger.setLevel(logging.WARNING)
1212
console.setLevel(logging.WARNING)
1214
if server_settings[u"servicename"] != u"Mandos":
1215
syslogger.setFormatter(logging.Formatter
1216
(u'Mandos (%s) [%%(process)d]:'
1217
u' %%(levelname)s: %%(message)s'
1218
% server_settings[u"servicename"]))
1220
# Parse config file with clients
1221
client_defaults = { u"timeout": u"1h",
1223
u"checker": u"fping -q -- %%(host)s",
1226
client_config = configparser.SafeConfigParser(client_defaults)
1227
client_config.read(os.path.join(server_settings[u"configdir"],
1230
global mandos_dbus_service
1231
mandos_dbus_service = None
1234
tcp_server = IPv6_TCPServer((server_settings[u"address"],
1235
server_settings[u"port"]),
1238
server_settings[u"interface"],
1242
server_settings[u"priority"],
1244
pidfilename = u"/var/run/mandos.pid"
1246
pidfile = open(pidfilename, u"w")
1248
logger.error(u"Could not open file %r", pidfilename)
1251
uid = pwd.getpwnam(u"_mandos").pw_uid
1252
gid = pwd.getpwnam(u"_mandos").pw_gid
1255
uid = pwd.getpwnam(u"mandos").pw_uid
1256
gid = pwd.getpwnam(u"mandos").pw_gid
1259
uid = pwd.getpwnam(u"nobody").pw_uid
1260
gid = pwd.getpwnam(u"nobody").pw_gid
1267
except OSError, error:
1268
if error[0] != errno.EPERM:
1271
# Enable all possible GnuTLS debugging
1273
# "Use a log level over 10 to enable all debugging options."
1275
gnutls.library.functions.gnutls_global_set_log_level(11)
1277
@gnutls.library.types.gnutls_log_func
1278
def debug_gnutls(level, string):
1279
logger.debug(u"GnuTLS: %s", string[:-1])
1281
(gnutls.library.functions
1282
.gnutls_global_set_log_function(debug_gnutls))
1285
protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1286
service = AvahiService(name = server_settings[u"servicename"],
1287
servicetype = u"_mandos._tcp",
1288
protocol = protocol)
1289
if server_settings["interface"]:
1290
service.interface = (if_nametoindex
1291
(str(server_settings[u"interface"])))
656
# Parse the time arguments
658
options.timeout = string_to_delta(options.timeout)
660
parser.error("option --timeout: Unparseable time")
662
options.interval = string_to_delta(options.interval)
664
parser.error("option --interval: Unparseable time")
667
defaults = { "checker": "fping -q -- %%(fqdn)s" }
668
client_config = ConfigParser.SafeConfigParser(defaults)
669
#client_config.readfp(open("global.conf"), "global.conf")
670
client_config.read("mandos-clients.conf")
1293
672
global main_loop