/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
3 by Björn Påhlsson
Python based server
1
#!/usr/bin/python
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
2
# -*- mode: python; coding: utf-8 -*-
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
3
# 
4
# Mandos server - give out binary blobs to connecting clients.
5
# 
6
# This program is partly derived from an example program for an Avahi
7
# service publisher, downloaded from
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
28 by Teddy Hogeborn
* server.conf: New file.
9
# following functions: "AvahiService.add", "AvahiService.remove",
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
10
# "server_state_changed", "entry_group_state_changed", and some lines
11
# in "main".
12
# 
28 by Teddy Hogeborn
* server.conf: New file.
13
# Everything else is
14
# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
15
# 
16
# This program is free software: you can redistribute it and/or modify
17
# it under the terms of the GNU General Public License as published by
18
# the Free Software Foundation, either version 3 of the License, or
19
# (at your option) any later version.
20
#
21
#     This program is distributed in the hope that it will be useful,
22
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
23
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
#     GNU General Public License for more details.
25
# 
26
# You should have received a copy of the GNU General Public License
27
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
# 
28 by Teddy Hogeborn
* server.conf: New file.
29
# Contact the authors at <mandos@fukt.bsnet.se>.
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
30
# 
3 by Björn Påhlsson
Python based server
31
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
32
from __future__ import division
33
3 by Björn Påhlsson
Python based server
34
import SocketServer
35
import socket
36
import select
37
from optparse import OptionParser
38
import datetime
39
import errno
40
import gnutls.crypto
41
import gnutls.connection
42
import gnutls.errors
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
43
import gnutls.library.functions
44
import gnutls.library.constants
45
import gnutls.library.types
3 by Björn Påhlsson
Python based server
46
import ConfigParser
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
47
import sys
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
48
import re
49
import os
50
import signal
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
51
from sets import Set
52
import subprocess
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
53
import atexit
54
import stat
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
55
import logging
56
import logging.handlers
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
57
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
58
import dbus
59
import gobject
60
import avahi
61
from dbus.mainloop.glib import DBusGMainLoop
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
62
import ctypes
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
63
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
64
# Brief description of the operation of this program:
65
# 
66
# This server announces itself as a Zeroconf service.  Connecting
67
# clients use the TLS protocol, with the unusual quirk that this
28 by Teddy Hogeborn
* server.conf: New file.
68
# server program acts as a TLS "client" while a connecting client acts
69
# as a TLS "server".  The client (acting as a TLS "server") must
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
70
# supply an OpenPGP certificate, and the fingerprint of this
71
# certificate is used by this server to look up (in a list read from a
72
# file at start time) which binary blob to give the client.  No other
73
# authentication or authorization is done by this server.
74
13 by Björn Påhlsson
Added following support:
75
76
logger = logging.Logger('mandos')
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
77
syslogger = logging.handlers.SysLogHandler\
78
            (facility = logging.handlers.SysLogHandler.LOG_DAEMON)
79
syslogger.setFormatter(logging.Formatter\
80
                        ('%(levelname)s: %(message)s'))
81
logger.addHandler(syslogger)
82
del syslogger
13 by Björn Påhlsson
Added following support:
83
28 by Teddy Hogeborn
* server.conf: New file.
84
85
class AvahiError(Exception):
86
    def __init__(self, value):
87
        self.value = value
88
    def __str__(self):
89
        return repr(self.value)
90
91
class AvahiServiceError(AvahiError):
92
    pass
93
94
class AvahiGroupError(AvahiError):
95
    pass
96
97
98
class AvahiService(object):
99
    """
100
    interface: integer; avahi.IF_UNSPEC or an interface index.
101
               Used to optionally bind to the specified interface.
102
    name = string; Example: "Mandos"
103
    type = string; Example: "_mandos._tcp".
104
                   See <http://www.dns-sd.org/ServiceTypes.html>
105
    port = integer; what port to announce
106
    TXT = list of strings; TXT record for the service
107
    domain = string; Domain to publish on, default to .local if empty.
108
    host = string; Host to publish records for, default to localhost
109
                   if empty.
110
    max_renames = integer; maximum number of renames
111
    rename_count = integer; counter so we only rename after collisions
112
                   a sensible number of times
113
    """
114
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
115
                 type = None, port = None, TXT = None, domain = "",
116
                 host = "", max_renames = 12):
117
        """An Avahi (Zeroconf) service. """
118
        self.interface = interface
119
        self.name = name
120
        self.type = type
121
        self.port = port
122
        if TXT is None:
123
            self.TXT = []
124
        else:
125
            self.TXT = TXT
126
        self.domain = domain
127
        self.host = host
128
        self.rename_count = 0
129
    def rename(self):
130
        """Derived from the Avahi example code"""
131
        if self.rename_count >= self.max_renames:
132
            logger.critical(u"No suitable service name found after %i"
133
                            u" retries, exiting.", rename_count)
134
            raise AvahiServiceError("Too many renames")
135
        name = server.GetAlternativeServiceName(name)
136
        logger.notice(u"Changing name to %r ...", name)
137
        self.remove()
138
        self.add()
139
        self.rename_count += 1
140
    def remove(self):
141
        """Derived from the Avahi example code"""
142
        if group is not None:
143
            group.Reset()
144
    def add(self):
145
        """Derived from the Avahi example code"""
146
        global group
147
        if group is None:
148
            group = dbus.Interface\
149
                    (bus.get_object(avahi.DBUS_NAME,
150
                                    server.EntryGroupNew()),
151
                     avahi.DBUS_INTERFACE_ENTRY_GROUP)
152
            group.connect_to_signal('StateChanged',
153
                                    entry_group_state_changed)
154
        logger.debug(u"Adding service '%s' of type '%s' ...",
155
                     service.name, service.type)
156
        group.AddService(
157
                self.interface,         # interface
158
                avahi.PROTO_INET6,      # protocol
159
                dbus.UInt32(0),         # flags
160
                self.name, self.type,
161
                self.domain, self.host,
162
                dbus.UInt16(self.port),
163
                avahi.string_array_to_txt_array(self.TXT))
164
        group.Commit()
165
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
166
# From the Avahi example code:
28 by Teddy Hogeborn
* server.conf: New file.
167
group = None                            # our entry group
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
168
# End of Avahi example code
169
170
3 by Björn Påhlsson
Python based server
171
class Client(object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
172
    """A representation of a client host served by this server.
173
    Attributes:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
174
    name:      string; from the config file, used in log messages
175
    fingerprint: string (40 or 32 hexadecimal digits); used to
176
                 uniquely identify the client
177
    secret:    bytestring; sent verbatim (over TLS) to client
178
    fqdn:      string (FQDN); available for use by the checker command
28 by Teddy Hogeborn
* server.conf: New file.
179
    created:   datetime.datetime(); object creation, not client host
180
    last_checked_ok: datetime.datetime() or None if not yet checked OK
181
    timeout:   datetime.timedelta(); How long from last_checked_ok
182
                                     until this client is invalid
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
183
    interval:  datetime.timedelta(); How often to start a new checker
184
    stop_hook: If set, called by stop() as stop_hook(self)
185
    checker:   subprocess.Popen(); a running checker process used
186
                                   to see if the client lives.
28 by Teddy Hogeborn
* server.conf: New file.
187
                                   'None' if no process is running.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
188
    checker_initiator_tag: a gobject event source tag, or None
189
    stop_initiator_tag:    - '' -
190
    checker_callback_tag:  - '' -
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
191
    checker_command: string; External command which is run to check if
28 by Teddy Hogeborn
* server.conf: New file.
192
                     client lives.  %() expansions are done at
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
193
                     runtime with vars(self) as dict, so that for
194
                     instance %(name)s can be used in the command.
195
    Private attibutes:
196
    _timeout: Real variable for 'timeout'
197
    _interval: Real variable for 'interval'
28 by Teddy Hogeborn
* server.conf: New file.
198
    _timeout_milliseconds: Used when calling gobject.timeout_add()
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
199
    _interval_milliseconds: - '' -
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
200
    """
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
201
    def _set_timeout(self, timeout):
202
        "Setter function for 'timeout' attribute"
203
        self._timeout = timeout
204
        self._timeout_milliseconds = ((self.timeout.days
205
                                       * 24 * 60 * 60 * 1000)
206
                                      + (self.timeout.seconds * 1000)
207
                                      + (self.timeout.microseconds
208
                                         // 1000))
209
    timeout = property(lambda self: self._timeout,
210
                       _set_timeout)
211
    del _set_timeout
212
    def _set_interval(self, interval):
213
        "Setter function for 'interval' attribute"
214
        self._interval = interval
215
        self._interval_milliseconds = ((self.interval.days
216
                                        * 24 * 60 * 60 * 1000)
217
                                       + (self.interval.seconds
218
                                          * 1000)
219
                                       + (self.interval.microseconds
220
                                          // 1000))
221
    interval = property(lambda self: self._interval,
222
                        _set_interval)
223
    del _set_interval
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
224
    def __init__(self, name=None, stop_hook=None, fingerprint=None,
225
                 secret=None, secfile=None, fqdn=None, timeout=None,
226
                 interval=-1, checker=None):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
227
        """Note: the 'checker' argument sets the 'checker_command'
228
        attribute and not the 'checker' attribute.."""
3 by Björn Påhlsson
Python based server
229
        self.name = name
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
230
        logger.debug(u"Creating client %r", self.name)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
231
        # Uppercase and remove spaces from fingerprint
232
        # for later comparison purposes with return value of
233
        # the fingerprint() function
234
        self.fingerprint = fingerprint.upper().replace(u" ", u"")
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
235
        logger.debug(u"  Fingerprint: %s", self.fingerprint)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
236
        if secret:
237
            self.secret = secret.decode(u"base64")
238
        elif secfile:
239
            sf = open(secfile)
240
            self.secret = sf.read()
241
            sf.close()
3 by Björn Påhlsson
Python based server
242
        else:
28 by Teddy Hogeborn
* server.conf: New file.
243
            raise TypeError(u"No secret or secfile for client %s"
244
                            % self.name)
245
        self.fqdn = fqdn
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
246
        self.created = datetime.datetime.now()
28 by Teddy Hogeborn
* server.conf: New file.
247
        self.last_checked_ok = None
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
248
        self.timeout = string_to_delta(timeout)
249
        self.interval = string_to_delta(interval)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
250
        self.stop_hook = stop_hook
251
        self.checker = None
252
        self.checker_initiator_tag = None
253
        self.stop_initiator_tag = None
254
        self.checker_callback_tag = None
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
255
        self.check_command = checker
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
256
    def start(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
257
        """Start this client's checker and timeout hooks"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
258
        # Schedule a new checker to be started an 'interval' from now,
259
        # and every interval from then on.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
260
        self.checker_initiator_tag = gobject.timeout_add\
261
                                     (self._interval_milliseconds,
262
                                      self.start_checker)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
263
        # Also start a new checker *right now*.
264
        self.start_checker()
265
        # Schedule a stop() when 'timeout' has passed
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
266
        self.stop_initiator_tag = gobject.timeout_add\
267
                                  (self._timeout_milliseconds,
268
                                   self.stop)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
269
    def stop(self):
270
        """Stop this client.
28 by Teddy Hogeborn
* server.conf: New file.
271
        The possibility that a client might be restarted is left open,
272
        but not currently used."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
273
        # If this client doesn't have a secret, it is already stopped.
274
        if self.secret:
275
            logger.debug(u"Stopping client %s", self.name)
276
            self.secret = None
277
        else:
278
            return False
28 by Teddy Hogeborn
* server.conf: New file.
279
        if getattr(self, "stop_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
280
            gobject.source_remove(self.stop_initiator_tag)
281
            self.stop_initiator_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
282
        if getattr(self, "checker_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
283
            gobject.source_remove(self.checker_initiator_tag)
284
            self.checker_initiator_tag = None
285
        self.stop_checker()
286
        if self.stop_hook:
287
            self.stop_hook(self)
288
        # Do not run this again if called by a gobject.timeout_add
289
        return False
290
    def __del__(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
291
        self.stop_hook = None
292
        self.stop()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
293
    def checker_callback(self, pid, condition):
294
        """The checker has completed, so take appropriate actions."""
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
295
        now = datetime.datetime.now()
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
296
        self.checker_callback_tag = None
297
        self.checker = None
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
298
        if os.WIFEXITED(condition) \
299
               and (os.WEXITSTATUS(condition) == 0):
13 by Björn Påhlsson
Added following support:
300
            logger.debug(u"Checker for %(name)s succeeded",
301
                         vars(self))
28 by Teddy Hogeborn
* server.conf: New file.
302
            self.last_checked_ok = now
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
303
            gobject.source_remove(self.stop_initiator_tag)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
304
            self.stop_initiator_tag = gobject.timeout_add\
305
                                      (self._timeout_milliseconds,
306
                                       self.stop)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
307
        elif not os.WIFEXITED(condition):
13 by Björn Påhlsson
Added following support:
308
            logger.warning(u"Checker for %(name)s crashed?",
309
                           vars(self))
310
        else:
311
            logger.debug(u"Checker for %(name)s failed",
312
                         vars(self))
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
313
    def start_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
314
        """Start a new checker subprocess if one is not running.
315
        If a checker already exists, leave it running and do
316
        nothing."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
317
        # The reason for not killing a running checker is that if we
318
        # did that, then if a checker (for some reason) started
319
        # running slowly and taking more than 'interval' time, the
320
        # client would inevitably timeout, since no checker would get
321
        # a chance to run to completion.  If we instead leave running
322
        # checkers alone, the checker would have to take more time
323
        # than 'timeout' for the client to be declared invalid, which
324
        # is as it should be.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
325
        if self.checker is None:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
326
            try:
28 by Teddy Hogeborn
* server.conf: New file.
327
                # In case check_command has exactly one % operator
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
328
                command = self.check_command % self.fqdn
329
            except TypeError:
28 by Teddy Hogeborn
* server.conf: New file.
330
                # Escape attributes for the shell
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
331
                escaped_attrs = dict((key, re.escape(str(val)))
332
                                     for key, val in
333
                                     vars(self).iteritems())
13 by Björn Påhlsson
Added following support:
334
                try:
335
                    command = self.check_command % escaped_attrs
336
                except TypeError, error:
28 by Teddy Hogeborn
* server.conf: New file.
337
                    logger.error(u'Could not format string "%s":'
338
                                 u' %s', self.check_command, error)
13 by Björn Påhlsson
Added following support:
339
                    return True # Try again later
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
340
            try:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
341
                logger.debug(u"Starting checker %r for %s",
342
                             command, self.name)
28 by Teddy Hogeborn
* server.conf: New file.
343
                self.checker = subprocess.Popen(command,
344
                                                close_fds=True,
345
                                                shell=True, cwd="/")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
346
                self.checker_callback_tag = gobject.child_watch_add\
347
                                            (self.checker.pid,
348
                                             self.checker_callback)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
349
            except subprocess.OSError, error:
13 by Björn Påhlsson
Added following support:
350
                logger.error(u"Failed to start subprocess: %s",
351
                             error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
352
        # Re-run this periodically if run by gobject.timeout_add
353
        return True
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
354
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
355
        """Force the checker process, if any, to stop."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
356
        if self.checker_callback_tag:
357
            gobject.source_remove(self.checker_callback_tag)
358
            self.checker_callback_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
359
        if getattr(self, "checker", None) is None:
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
360
            return
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
361
        logger.debug("Stopping checker for %(name)s", vars(self))
362
        try:
363
            os.kill(self.checker.pid, signal.SIGTERM)
364
            #os.sleep(0.5)
365
            #if self.checker.poll() is None:
366
            #    os.kill(self.checker.pid, signal.SIGKILL)
367
        except OSError, error:
28 by Teddy Hogeborn
* server.conf: New file.
368
            if error.errno != errno.ESRCH: # No such process
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
369
                raise
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
370
        self.checker = None
28 by Teddy Hogeborn
* server.conf: New file.
371
    def still_valid(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
372
        """Has the timeout not yet passed for this client?"""
28 by Teddy Hogeborn
* server.conf: New file.
373
        now = datetime.datetime.now()
374
        if self.last_checked_ok is None:
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
375
            return now < (self.created + self.timeout)
376
        else:
28 by Teddy Hogeborn
* server.conf: New file.
377
            return now < (self.last_checked_ok + self.timeout)
3 by Björn Påhlsson
Python based server
378
379
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
380
def peer_certificate(session):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
381
    "Return the peer's OpenPGP certificate as a bytestring"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
382
    # If not an OpenPGP certificate...
383
    if gnutls.library.functions.gnutls_certificate_type_get\
384
            (session._c_object) \
385
           != gnutls.library.constants.GNUTLS_CRT_OPENPGP:
386
        # ...do the normal thing
387
        return session.peer_certificate
388
    list_size = ctypes.c_uint()
389
    cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
390
        (session._c_object, ctypes.byref(list_size))
391
    if list_size.value == 0:
392
        return None
393
    cert = cert_list[0]
394
    return ctypes.string_at(cert.data, cert.size)
395
396
397
def fingerprint(openpgp):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
398
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
399
    # New empty GnuTLS certificate
400
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
401
    gnutls.library.functions.gnutls_openpgp_crt_init\
402
        (ctypes.byref(crt))
403
    # New GnuTLS "datum" with the OpenPGP public key
404
    datum = gnutls.library.types.gnutls_datum_t\
405
        (ctypes.cast(ctypes.c_char_p(openpgp),
406
                     ctypes.POINTER(ctypes.c_ubyte)),
407
         ctypes.c_uint(len(openpgp)))
408
    # Import the OpenPGP public key into the certificate
409
    ret = gnutls.library.functions.gnutls_openpgp_crt_import\
410
        (crt,
411
         ctypes.byref(datum),
412
         gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
413
    # New buffer for the fingerprint
414
    buffer = ctypes.create_string_buffer(20)
415
    buffer_length = ctypes.c_size_t()
416
    # Get the fingerprint from the certificate into the buffer
417
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
418
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
419
    # Deinit the certificate
420
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
421
    # Convert the buffer to a Python bytestring
422
    fpr = ctypes.string_at(buffer, buffer_length.value)
423
    # Convert the bytestring to hexadecimal notation
424
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
425
    return hex_fpr
426
427
3 by Björn Påhlsson
Python based server
428
class tcp_handler(SocketServer.BaseRequestHandler, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
429
    """A TCP request handler class.
430
    Instantiated by IPv6_TCPServer for each request to handle it.
431
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
432
    
3 by Björn Påhlsson
Python based server
433
    def handle(self):
13 by Björn Påhlsson
Added following support:
434
        logger.debug(u"TCP connection from: %s",
435
                     unicode(self.client_address))
24.1.11 by Björn Påhlsson
Added support for protocol version handling
436
24.1.12 by Björn Påhlsson
merge +
437
        line = self.request.makefile().readline()
438
        logger.debug(u"Protocol version: %r", line)
24.1.11 by Björn Påhlsson
Added support for protocol version handling
439
        try:
440
            if int(line.strip().split()[0]) > 1:
441
                raise RuntimeError
442
        except (ValueError, IndexError, RuntimeError), error:
443
            logger.error(u"Unknown protocol version: %s", error)
444
            return
445
        
28 by Teddy Hogeborn
* server.conf: New file.
446
        session = gnutls.connection.ClientSession\
447
                  (self.request, gnutls.connection.X509Credentials())
448
        # Note: gnutls.connection.X509Credentials is really a generic
449
        # GnuTLS certificate credentials object so long as no X.509
450
        # keys are added to it.  Therefore, we can use it here despite
451
        # using OpenPGP certificates.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
452
        
453
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
454
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
455
        #                "+DHE-DSS"))
28 by Teddy Hogeborn
* server.conf: New file.
456
        priority = "NORMAL"             # Fallback default, since this
457
                                        # MUST be set.
458
        if self.server.settings["priority"]:
459
            priority = self.server.settings["priority"]
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
460
        gnutls.library.functions.gnutls_priority_set_direct\
461
            (session._c_object, priority, None);
462
        
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
463
        try:
464
            session.handshake()
465
        except gnutls.errors.GNUTLSError, error:
13 by Björn Påhlsson
Added following support:
466
            logger.debug(u"Handshake failed: %s", error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
467
            # Do not run session.bye() here: the session is not
468
            # established.  Just abandon the request.
469
            return
3 by Björn Påhlsson
Python based server
470
        try:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
471
            fpr = fingerprint(peer_certificate(session))
472
        except (TypeError, gnutls.errors.GNUTLSError), error:
13 by Björn Påhlsson
Added following support:
473
            logger.debug(u"Bad certificate: %s", error)
3 by Björn Påhlsson
Python based server
474
            session.bye()
475
            return
13 by Björn Påhlsson
Added following support:
476
        logger.debug(u"Fingerprint: %s", fpr)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
477
        client = None
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
478
        for c in self.server.clients:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
479
            if c.fingerprint == fpr:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
480
                client = c
481
                break
28 by Teddy Hogeborn
* server.conf: New file.
482
        if not client:
483
            logger.debug(u"Client not found for fingerprint: %s", fpr)
484
            session.bye()
485
            return
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
486
        # Have to check if client.still_valid(), since it is possible
487
        # that the client timed out while establishing the GnuTLS
488
        # session.
28 by Teddy Hogeborn
* server.conf: New file.
489
        if not client.still_valid():
490
            logger.debug(u"Client %(name)s is invalid", vars(client))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
491
            session.bye()
492
            return
493
        sent_size = 0
494
        while sent_size < len(client.secret):
495
            sent = session.send(client.secret[sent_size:])
13 by Björn Påhlsson
Added following support:
496
            logger.debug(u"Sent: %d, remaining: %d",
497
                         sent, len(client.secret)
498
                         - (sent_size + sent))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
499
            sent_size += sent
3 by Björn Påhlsson
Python based server
500
        session.bye()
501
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
502
3 by Björn Påhlsson
Python based server
503
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
504
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
505
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
506
        settings:       Server settings
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
507
        clients:        Set() of Client objects
508
    """
509
    address_family = socket.AF_INET6
510
    def __init__(self, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
511
        if "settings" in kwargs:
512
            self.settings = kwargs["settings"]
513
            del kwargs["settings"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
514
        if "clients" in kwargs:
515
            self.clients = kwargs["clients"]
516
            del kwargs["clients"]
517
        return super(type(self), self).__init__(*args, **kwargs)
518
    def server_bind(self):
519
        """This overrides the normal server_bind() function
520
        to bind to an interface if one was specified, and also NOT to
521
        bind to an address or port if they were not specified."""
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
522
        if self.settings["interface"]:
28 by Teddy Hogeborn
* server.conf: New file.
523
            # 25 is from /usr/include/asm-i486/socket.h
524
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
525
            try:
526
                self.socket.setsockopt(socket.SOL_SOCKET,
28 by Teddy Hogeborn
* server.conf: New file.
527
                                       SO_BINDTODEVICE,
528
                                       self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
529
            except socket.error, error:
530
                if error[0] == errno.EPERM:
13 by Björn Påhlsson
Added following support:
531
                    logger.warning(u"No permission to"
532
                                   u" bind to interface %s",
28 by Teddy Hogeborn
* server.conf: New file.
533
                                   self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
534
                else:
535
                    raise error
536
        # Only bind(2) the socket if we really need to.
537
        if self.server_address[0] or self.server_address[1]:
538
            if not self.server_address[0]:
539
                in6addr_any = "::"
540
                self.server_address = (in6addr_any,
541
                                       self.server_address[1])
542
            elif self.server_address[1] is None:
543
                self.server_address = (self.server_address[0],
544
                                       0)
545
            return super(type(self), self).server_bind()
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
546
3 by Björn Påhlsson
Python based server
547
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
548
def string_to_delta(interval):
549
    """Parse a string and return a datetime.timedelta
550
551
    >>> string_to_delta('7d')
552
    datetime.timedelta(7)
553
    >>> string_to_delta('60s')
554
    datetime.timedelta(0, 60)
555
    >>> string_to_delta('60m')
556
    datetime.timedelta(0, 3600)
557
    >>> string_to_delta('24h')
558
    datetime.timedelta(1)
559
    >>> string_to_delta(u'1w')
560
    datetime.timedelta(7)
561
    """
562
    try:
563
        suffix=unicode(interval[-1])
564
        value=int(interval[:-1])
565
        if suffix == u"d":
566
            delta = datetime.timedelta(value)
567
        elif suffix == u"s":
568
            delta = datetime.timedelta(0, value)
569
        elif suffix == u"m":
570
            delta = datetime.timedelta(0, 0, 0, 0, value)
571
        elif suffix == u"h":
572
            delta = datetime.timedelta(0, 0, 0, 0, 0, value)
573
        elif suffix == u"w":
574
            delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
575
        else:
576
            raise ValueError
577
    except (ValueError, IndexError):
578
        raise ValueError
579
    return delta
580
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
581
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
582
def server_state_changed(state):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
583
    """Derived from the Avahi example code"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
584
    if state == avahi.SERVER_COLLISION:
13 by Björn Påhlsson
Added following support:
585
        logger.warning(u"Server name collision")
28 by Teddy Hogeborn
* server.conf: New file.
586
        service.remove()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
587
    elif state == avahi.SERVER_RUNNING:
28 by Teddy Hogeborn
* server.conf: New file.
588
        service.add()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
589
590
591
def entry_group_state_changed(state, error):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
592
    """Derived from the Avahi example code"""
13 by Björn Påhlsson
Added following support:
593
    logger.debug(u"state change: %i", state)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
594
    
595
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
13 by Björn Påhlsson
Added following support:
596
        logger.debug(u"Service established.")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
597
    elif state == avahi.ENTRY_GROUP_COLLISION:
28 by Teddy Hogeborn
* server.conf: New file.
598
        logger.warning(u"Service name collision.")
599
        service.rename()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
600
    elif state == avahi.ENTRY_GROUP_FAILURE:
28 by Teddy Hogeborn
* server.conf: New file.
601
        logger.critical(u"Error in group state changed %s",
602
                        unicode(error))
603
        raise AvahiGroupError("State changed: %s", str(error))
604
605
def if_nametoindex(interface, _func=[None]):
606
    """Call the C function if_nametoindex(), or equivalent"""
607
    if _func[0] is not None:
608
        return _func[0](interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
609
    try:
28 by Teddy Hogeborn
* server.conf: New file.
610
        if "ctypes.util" not in sys.modules:
611
            import ctypes.util
612
        while True:
613
            try:
614
                libc = ctypes.cdll.LoadLibrary\
615
                       (ctypes.util.find_library("c"))
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
616
                _func[0] = libc.if_nametoindex
28 by Teddy Hogeborn
* server.conf: New file.
617
                return _func[0](interface)
618
            except IOError, e:
619
                if e != errno.EINTR:
620
                    raise
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
621
    except (OSError, AttributeError):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
622
        if "struct" not in sys.modules:
623
            import struct
624
        if "fcntl" not in sys.modules:
625
            import fcntl
28 by Teddy Hogeborn
* server.conf: New file.
626
        def the_hard_way(interface):
627
            "Get an interface index the hard way, i.e. using fcntl()"
628
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
629
            s = socket.socket()
630
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
631
                                struct.pack("16s16x", interface))
632
            s.close()
633
            interface_index = struct.unpack("I", ifreq[16:20])[0]
634
            return interface_index
635
        _func[0] = the_hard_way
636
        return _func[0](interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
637
638
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
639
def daemon(nochdir, noclose):
640
    """See daemon(3).  Standard BSD Unix function.
641
    This should really exist as os.daemon, but it doesn't (yet)."""
642
    if os.fork():
643
        sys.exit()
644
    os.setsid()
645
    if not nochdir:
646
        os.chdir("/")
647
    if not noclose:
648
        # Close all standard open file descriptors
28 by Teddy Hogeborn
* server.conf: New file.
649
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
650
        if not stat.S_ISCHR(os.fstat(null).st_mode):
651
            raise OSError(errno.ENODEV,
652
                          "/dev/null not a character device")
653
        os.dup2(null, sys.stdin.fileno())
654
        os.dup2(null, sys.stdout.fileno())
655
        os.dup2(null, sys.stderr.fileno())
656
        if null > 2:
657
            os.close(null)
658
659
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
660
def main():
661
    global main_loop_started
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
662
    main_loop_started = False
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
663
    
3 by Björn Påhlsson
Python based server
664
    parser = OptionParser()
665
    parser.add_option("-i", "--interface", type="string",
28 by Teddy Hogeborn
* server.conf: New file.
666
                      metavar="IF", help="Bind to interface IF")
667
    parser.add_option("-a", "--address", type="string",
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
668
                      help="Address to listen for requests on")
28 by Teddy Hogeborn
* server.conf: New file.
669
    parser.add_option("-p", "--port", type="int",
3 by Björn Påhlsson
Python based server
670
                      help="Port number to receive requests on")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
671
    parser.add_option("--check", action="store_true", default=False,
672
                      help="Run self-test")
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
673
    parser.add_option("--debug", action="store_true", default=False,
28 by Teddy Hogeborn
* server.conf: New file.
674
                      help="Debug mode; run in foreground and log to"
675
                      " terminal")
676
    parser.add_option("--priority", type="string", help="GnuTLS"
677
                      " priority string (see GnuTLS documentation)")
678
    parser.add_option("--servicename", type="string", metavar="NAME",
679
                      help="Zeroconf service name")
680
    parser.add_option("--configdir", type="string",
681
                      default="/etc/mandos", metavar="DIR",
682
                      help="Directory to search for configuration"
683
                      " files")
3 by Björn Påhlsson
Python based server
684
    (options, args) = parser.parse_args()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
685
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
686
    if options.check:
687
        import doctest
688
        doctest.testmod()
689
        sys.exit()
3 by Björn Påhlsson
Python based server
690
    
28 by Teddy Hogeborn
* server.conf: New file.
691
    # Default values for config file for server-global settings
692
    server_defaults = { "interface": "",
693
                        "address": "",
694
                        "port": "",
695
                        "debug": "False",
696
                        "priority":
697
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
698
                        "servicename": "Mandos",
699
                        }
700
    
701
    # Parse config file for server-global settings
702
    server_config = ConfigParser.SafeConfigParser(server_defaults)
703
    del server_defaults
704
    server_config.read(os.path.join(options.configdir, "server.conf"))
705
    server_section = "server"
706
    # Convert the SafeConfigParser object to a dict
707
    server_settings = dict(server_config.items(server_section))
708
    # Use getboolean on the boolean config option
709
    server_settings["debug"] = server_config.getboolean\
710
                               (server_section, "debug")
711
    del server_config
712
    
713
    # Override the settings from the config file with command line
714
    # options, if set.
715
    for option in ("interface", "address", "port", "debug",
716
                   "priority", "servicename", "configdir"):
717
        value = getattr(options, option)
718
        if value is not None:
719
            server_settings[option] = value
720
    del options
721
    # Now we have our good server settings in "server_settings"
722
    
723
    # Parse config file with clients
724
    client_defaults = { "timeout": "1h",
725
                        "interval": "5m",
726
                        "checker": "fping -q -- %%(fqdn)s",
727
                        }
728
    client_config = ConfigParser.SafeConfigParser(client_defaults)
729
    client_config.read(os.path.join(server_settings["configdir"],
730
                                    "clients.conf"))
731
    
732
    global service
733
    service = AvahiService(name = server_settings["servicename"],
734
                           type = "_mandos._tcp", );
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
735
    if server_settings["interface"]:
736
        service.interface = if_nametoindex(server_settings["interface"])
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
737
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
738
    global main_loop
739
    global bus
740
    global server
741
    # From the Avahi example code
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
742
    DBusGMainLoop(set_as_default=True )
743
    main_loop = gobject.MainLoop()
744
    bus = dbus.SystemBus()
745
    server = dbus.Interface(
746
            bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
747
            avahi.DBUS_INTERFACE_SERVER )
748
    # End of Avahi example code
749
    
28 by Teddy Hogeborn
* server.conf: New file.
750
    debug = server_settings["debug"]
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
751
    
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
752
    if debug:
753
        console = logging.StreamHandler()
754
        # console.setLevel(logging.DEBUG)
755
        console.setFormatter(logging.Formatter\
756
                             ('%(levelname)s: %(message)s'))
757
        logger.addHandler(console)
758
        del console
759
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
760
    clients = Set()
761
    def remove_from_clients(client):
762
        clients.remove(client)
763
        if not clients:
13 by Björn Påhlsson
Added following support:
764
            logger.debug(u"No clients left, exiting")
28 by Teddy Hogeborn
* server.conf: New file.
765
            sys.exit()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
766
    
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
767
    clients.update(Set(Client(name=section,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
768
                              stop_hook = remove_from_clients,
769
                              **(dict(client_config\
770
                                      .items(section))))
771
                       for section in client_config.sections()))
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
772
    
773
    if not debug:
774
        daemon(False, False)
775
    
776
    def cleanup():
777
        "Cleanup function; run on exit"
778
        global group
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
779
        # From the Avahi example code
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
780
        if not group is None:
781
            group.Free()
782
            group = None
783
        # End of Avahi example code
784
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
785
        while clients:
786
            client = clients.pop()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
787
            client.stop_hook = None
788
            client.stop()
789
    
790
    atexit.register(cleanup)
791
    
792
    if not debug:
793
        signal.signal(signal.SIGINT, signal.SIG_IGN)
28 by Teddy Hogeborn
* server.conf: New file.
794
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
795
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
796
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
797
    for client in clients:
798
        client.start()
799
    
28 by Teddy Hogeborn
* server.conf: New file.
800
    tcp_server = IPv6_TCPServer((server_settings["address"],
801
                                 server_settings["port"]),
3 by Björn Påhlsson
Python based server
802
                                tcp_handler,
28 by Teddy Hogeborn
* server.conf: New file.
803
                                settings=server_settings,
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
804
                                clients=clients)
28 by Teddy Hogeborn
* server.conf: New file.
805
    # Find out what port we got
806
    service.port = tcp_server.socket.getsockname()[1]
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
807
    logger.debug(u"Now listening on address %r, port %d, flowinfo %d,"
808
                 u" scope_id %d" % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
809
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
810
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
811
    
812
    try:
813
        # From the Avahi example code
814
        server.connect_to_signal("StateChanged", server_state_changed)
815
        try:
816
            server_state_changed(server.GetState())
817
        except dbus.exceptions.DBusException, error:
818
            logger.critical(u"DBusException: %s", error)
819
            sys.exit(1)
820
        # End of Avahi example code
821
        
822
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
823
                             lambda *args, **kwargs:
824
                             tcp_server.handle_request\
825
                             (*args[2:], **kwargs) or True)
826
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
827
        logger.debug("Starting main loop")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
828
        main_loop_started = True
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
829
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
830
    except AvahiError, error:
831
        logger.critical(u"AvahiError: %s" + unicode(error))
832
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
833
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
834
        if debug:
835
            print
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
836
837
if __name__ == '__main__':
838
    main()