/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)
44 by Teddy Hogeborn
* ca.pem: Removed.
136
        logger.error(u"Changing name to %r ...", name)
28 by Teddy Hogeborn
* server.conf: New file.
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
44 by Teddy Hogeborn
* ca.pem: Removed.
224
    def __init__(self, name = None, stop_hook=None, config={}):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
225
        """Note: the 'checker' argument sets the 'checker_command'
226
        attribute and not the 'checker' attribute.."""
3 by Björn Påhlsson
Python based server
227
        self.name = name
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
228
        logger.debug(u"Creating client %r", self.name)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
229
        # Uppercase and remove spaces from fingerprint
230
        # for later comparison purposes with return value of
231
        # the fingerprint() function
44 by Teddy Hogeborn
* ca.pem: Removed.
232
        self.fingerprint = config["fingerprint"].upper()\
233
                           .replace(u" ", u"")
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
234
        logger.debug(u"  Fingerprint: %s", self.fingerprint)
44 by Teddy Hogeborn
* ca.pem: Removed.
235
        if "secret" in config:
236
            self.secret = config["secret"].decode(u"base64")
237
        elif "secfile" in config:
238
            sf = open(config["secfile"])
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
239
            self.secret = sf.read()
240
            sf.close()
3 by Björn Påhlsson
Python based server
241
        else:
28 by Teddy Hogeborn
* server.conf: New file.
242
            raise TypeError(u"No secret or secfile for client %s"
243
                            % self.name)
44 by Teddy Hogeborn
* ca.pem: Removed.
244
        self.fqdn = config.get("fqdn", "")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
245
        self.created = datetime.datetime.now()
28 by Teddy Hogeborn
* server.conf: New file.
246
        self.last_checked_ok = None
44 by Teddy Hogeborn
* ca.pem: Removed.
247
        self.timeout = string_to_delta(config["timeout"])
248
        self.interval = string_to_delta(config["interval"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
249
        self.stop_hook = stop_hook
250
        self.checker = None
251
        self.checker_initiator_tag = None
252
        self.stop_initiator_tag = None
253
        self.checker_callback_tag = None
44 by Teddy Hogeborn
* ca.pem: Removed.
254
        self.check_command = config["checker"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
255
    def start(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
256
        """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
257
        # Schedule a new checker to be started an 'interval' from now,
258
        # and every interval from then on.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
259
        self.checker_initiator_tag = gobject.timeout_add\
260
                                     (self._interval_milliseconds,
261
                                      self.start_checker)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
262
        # Also start a new checker *right now*.
263
        self.start_checker()
264
        # Schedule a stop() when 'timeout' has passed
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
265
        self.stop_initiator_tag = gobject.timeout_add\
266
                                  (self._timeout_milliseconds,
267
                                   self.stop)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
268
    def stop(self):
269
        """Stop this client.
28 by Teddy Hogeborn
* server.conf: New file.
270
        The possibility that a client might be restarted is left open,
271
        but not currently used."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
272
        # If this client doesn't have a secret, it is already stopped.
273
        if self.secret:
44 by Teddy Hogeborn
* ca.pem: Removed.
274
            logger.info(u"Stopping client %s", self.name)
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
275
            self.secret = None
276
        else:
277
            return False
28 by Teddy Hogeborn
* server.conf: New file.
278
        if getattr(self, "stop_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
279
            gobject.source_remove(self.stop_initiator_tag)
280
            self.stop_initiator_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
281
        if getattr(self, "checker_initiator_tag", False):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
282
            gobject.source_remove(self.checker_initiator_tag)
283
            self.checker_initiator_tag = None
284
        self.stop_checker()
285
        if self.stop_hook:
286
            self.stop_hook(self)
287
        # Do not run this again if called by a gobject.timeout_add
288
        return False
289
    def __del__(self):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
290
        self.stop_hook = None
291
        self.stop()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
292
    def checker_callback(self, pid, condition):
293
        """The checker has completed, so take appropriate actions."""
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
294
        now = datetime.datetime.now()
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
295
        self.checker_callback_tag = None
296
        self.checker = None
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
297
        if os.WIFEXITED(condition) \
298
               and (os.WEXITSTATUS(condition) == 0):
44 by Teddy Hogeborn
* ca.pem: Removed.
299
            logger.info(u"Checker for %(name)s succeeded",
300
                        vars(self))
28 by Teddy Hogeborn
* server.conf: New file.
301
            self.last_checked_ok = now
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
302
            gobject.source_remove(self.stop_initiator_tag)
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
303
            self.stop_initiator_tag = gobject.timeout_add\
304
                                      (self._timeout_milliseconds,
305
                                       self.stop)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
306
        elif not os.WIFEXITED(condition):
13 by Björn Påhlsson
Added following support:
307
            logger.warning(u"Checker for %(name)s crashed?",
308
                           vars(self))
309
        else:
44 by Teddy Hogeborn
* ca.pem: Removed.
310
            logger.info(u"Checker for %(name)s failed",
311
                        vars(self))
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
312
    def start_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
313
        """Start a new checker subprocess if one is not running.
314
        If a checker already exists, leave it running and do
315
        nothing."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
316
        # The reason for not killing a running checker is that if we
317
        # did that, then if a checker (for some reason) started
318
        # running slowly and taking more than 'interval' time, the
319
        # client would inevitably timeout, since no checker would get
320
        # a chance to run to completion.  If we instead leave running
321
        # checkers alone, the checker would have to take more time
322
        # than 'timeout' for the client to be declared invalid, which
323
        # is as it should be.
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
324
        if self.checker is None:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
325
            try:
28 by Teddy Hogeborn
* server.conf: New file.
326
                # In case check_command has exactly one % operator
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
327
                command = self.check_command % self.fqdn
328
            except TypeError:
28 by Teddy Hogeborn
* server.conf: New file.
329
                # Escape attributes for the shell
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
330
                escaped_attrs = dict((key, re.escape(str(val)))
331
                                     for key, val in
332
                                     vars(self).iteritems())
13 by Björn Påhlsson
Added following support:
333
                try:
334
                    command = self.check_command % escaped_attrs
335
                except TypeError, error:
28 by Teddy Hogeborn
* server.conf: New file.
336
                    logger.error(u'Could not format string "%s":'
337
                                 u' %s', self.check_command, error)
13 by Björn Påhlsson
Added following support:
338
                    return True # Try again later
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
339
            try:
44 by Teddy Hogeborn
* ca.pem: Removed.
340
                logger.info(u"Starting checker %r for %s",
341
                            command, self.name)
28 by Teddy Hogeborn
* server.conf: New file.
342
                self.checker = subprocess.Popen(command,
343
                                                close_fds=True,
344
                                                shell=True, cwd="/")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
345
                self.checker_callback_tag = gobject.child_watch_add\
346
                                            (self.checker.pid,
347
                                             self.checker_callback)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
348
            except subprocess.OSError, error:
13 by Björn Påhlsson
Added following support:
349
                logger.error(u"Failed to start subprocess: %s",
350
                             error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
351
        # Re-run this periodically if run by gobject.timeout_add
352
        return True
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
353
    def stop_checker(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
354
        """Force the checker process, if any, to stop."""
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
355
        if self.checker_callback_tag:
356
            gobject.source_remove(self.checker_callback_tag)
357
            self.checker_callback_tag = None
28 by Teddy Hogeborn
* server.conf: New file.
358
        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.
359
            return
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
360
        logger.debug("Stopping checker for %(name)s", vars(self))
361
        try:
362
            os.kill(self.checker.pid, signal.SIGTERM)
363
            #os.sleep(0.5)
364
            #if self.checker.poll() is None:
365
            #    os.kill(self.checker.pid, signal.SIGKILL)
366
        except OSError, error:
28 by Teddy Hogeborn
* server.conf: New file.
367
            if error.errno != errno.ESRCH: # No such process
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
368
                raise
9 by Teddy Hogeborn
* client.cpp (main): Get t_old early since it is used on error exits.
369
        self.checker = None
28 by Teddy Hogeborn
* server.conf: New file.
370
    def still_valid(self):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
371
        """Has the timeout not yet passed for this client?"""
28 by Teddy Hogeborn
* server.conf: New file.
372
        now = datetime.datetime.now()
373
        if self.last_checked_ok is None:
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
374
            return now < (self.created + self.timeout)
375
        else:
28 by Teddy Hogeborn
* server.conf: New file.
376
            return now < (self.last_checked_ok + self.timeout)
3 by Björn Påhlsson
Python based server
377
378
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
379
def peer_certificate(session):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
380
    "Return the peer's OpenPGP certificate as a bytestring"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
381
    # If not an OpenPGP certificate...
382
    if gnutls.library.functions.gnutls_certificate_type_get\
383
            (session._c_object) \
384
           != gnutls.library.constants.GNUTLS_CRT_OPENPGP:
385
        # ...do the normal thing
386
        return session.peer_certificate
387
    list_size = ctypes.c_uint()
388
    cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
389
        (session._c_object, ctypes.byref(list_size))
390
    if list_size.value == 0:
391
        return None
392
    cert = cert_list[0]
393
    return ctypes.string_at(cert.data, cert.size)
394
395
396
def fingerprint(openpgp):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
397
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
398
    # New empty GnuTLS certificate
399
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
400
    gnutls.library.functions.gnutls_openpgp_crt_init\
401
        (ctypes.byref(crt))
402
    # New GnuTLS "datum" with the OpenPGP public key
403
    datum = gnutls.library.types.gnutls_datum_t\
404
        (ctypes.cast(ctypes.c_char_p(openpgp),
405
                     ctypes.POINTER(ctypes.c_ubyte)),
406
         ctypes.c_uint(len(openpgp)))
407
    # Import the OpenPGP public key into the certificate
408
    ret = gnutls.library.functions.gnutls_openpgp_crt_import\
409
        (crt,
410
         ctypes.byref(datum),
411
         gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
412
    # New buffer for the fingerprint
413
    buffer = ctypes.create_string_buffer(20)
414
    buffer_length = ctypes.c_size_t()
415
    # Get the fingerprint from the certificate into the buffer
416
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
417
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
418
    # Deinit the certificate
419
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
420
    # Convert the buffer to a Python bytestring
421
    fpr = ctypes.string_at(buffer, buffer_length.value)
422
    # Convert the bytestring to hexadecimal notation
423
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
424
    return hex_fpr
425
426
3 by Björn Påhlsson
Python based server
427
class tcp_handler(SocketServer.BaseRequestHandler, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
428
    """A TCP request handler class.
429
    Instantiated by IPv6_TCPServer for each request to handle it.
430
    Note: This will run in its own forked process."""
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
431
    
3 by Björn Påhlsson
Python based server
432
    def handle(self):
44 by Teddy Hogeborn
* ca.pem: Removed.
433
        logger.info(u"TCP connection from: %s",
13 by Björn Påhlsson
Added following support:
434
                     unicode(self.client_address))
41 by Teddy Hogeborn
Merge.
435
        session = gnutls.connection.ClientSession\
436
                  (self.request, gnutls.connection.X509Credentials())
437
        
24.1.12 by Björn Påhlsson
merge +
438
        line = self.request.makefile().readline()
439
        logger.debug(u"Protocol version: %r", line)
24.1.11 by Björn Påhlsson
Added support for protocol version handling
440
        try:
441
            if int(line.strip().split()[0]) > 1:
442
                raise RuntimeError
443
        except (ValueError, IndexError, RuntimeError), error:
444
            logger.error(u"Unknown protocol version: %s", error)
445
            return
446
        
28 by Teddy Hogeborn
* server.conf: New file.
447
        # Note: gnutls.connection.X509Credentials is really a generic
448
        # GnuTLS certificate credentials object so long as no X.509
449
        # keys are added to it.  Therefore, we can use it here despite
450
        # using OpenPGP certificates.
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
451
        
452
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
453
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
454
        #                "+DHE-DSS"))
28 by Teddy Hogeborn
* server.conf: New file.
455
        priority = "NORMAL"             # Fallback default, since this
456
                                        # MUST be set.
457
        if self.server.settings["priority"]:
458
            priority = self.server.settings["priority"]
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
459
        gnutls.library.functions.gnutls_priority_set_direct\
460
            (session._c_object, priority, None);
461
        
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
462
        try:
463
            session.handshake()
464
        except gnutls.errors.GNUTLSError, error:
44 by Teddy Hogeborn
* ca.pem: Removed.
465
            logger.warning(u"Handshake failed: %s", error)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
466
            # Do not run session.bye() here: the session is not
467
            # established.  Just abandon the request.
468
            return
3 by Björn Påhlsson
Python based server
469
        try:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
470
            fpr = fingerprint(peer_certificate(session))
471
        except (TypeError, gnutls.errors.GNUTLSError), error:
44 by Teddy Hogeborn
* ca.pem: Removed.
472
            logger.warning(u"Bad certificate: %s", error)
3 by Björn Påhlsson
Python based server
473
            session.bye()
474
            return
13 by Björn Påhlsson
Added following support:
475
        logger.debug(u"Fingerprint: %s", fpr)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
476
        client = None
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
477
        for c in self.server.clients:
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
478
            if c.fingerprint == fpr:
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
479
                client = c
480
                break
28 by Teddy Hogeborn
* server.conf: New file.
481
        if not client:
44 by Teddy Hogeborn
* ca.pem: Removed.
482
            logger.warning(u"Client not found for fingerprint: %s",
483
                           fpr)
28 by Teddy Hogeborn
* server.conf: New file.
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():
44 by Teddy Hogeborn
* ca.pem: Removed.
490
            logger.warning(u"Client %(name)s is invalid",
491
                           vars(client))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
492
            session.bye()
493
            return
494
        sent_size = 0
495
        while sent_size < len(client.secret):
496
            sent = session.send(client.secret[sent_size:])
13 by Björn Påhlsson
Added following support:
497
            logger.debug(u"Sent: %d, remaining: %d",
498
                         sent, len(client.secret)
499
                         - (sent_size + sent))
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
500
            sent_size += sent
3 by Björn Påhlsson
Python based server
501
        session.bye()
502
5 by Teddy Hogeborn
* server.py (server_metaclass): New.
503
3 by Björn Påhlsson
Python based server
504
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
505
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
506
    Attributes:
28 by Teddy Hogeborn
* server.conf: New file.
507
        settings:       Server settings
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
508
        clients:        Set() of Client objects
509
    """
510
    address_family = socket.AF_INET6
511
    def __init__(self, *args, **kwargs):
28 by Teddy Hogeborn
* server.conf: New file.
512
        if "settings" in kwargs:
513
            self.settings = kwargs["settings"]
514
            del kwargs["settings"]
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
515
        if "clients" in kwargs:
516
            self.clients = kwargs["clients"]
517
            del kwargs["clients"]
518
        return super(type(self), self).__init__(*args, **kwargs)
519
    def server_bind(self):
520
        """This overrides the normal server_bind() function
521
        to bind to an interface if one was specified, and also NOT to
522
        bind to an address or port if they were not specified."""
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
523
        if self.settings["interface"]:
28 by Teddy Hogeborn
* server.conf: New file.
524
            # 25 is from /usr/include/asm-i486/socket.h
525
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
526
            try:
527
                self.socket.setsockopt(socket.SOL_SOCKET,
28 by Teddy Hogeborn
* server.conf: New file.
528
                                       SO_BINDTODEVICE,
529
                                       self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
530
            except socket.error, error:
531
                if error[0] == errno.EPERM:
44 by Teddy Hogeborn
* ca.pem: Removed.
532
                    logger.error(u"No permission to"
533
                                 u" bind to interface %s",
534
                                 self.settings["interface"])
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
535
                else:
536
                    raise error
537
        # Only bind(2) the socket if we really need to.
538
        if self.server_address[0] or self.server_address[1]:
539
            if not self.server_address[0]:
540
                in6addr_any = "::"
541
                self.server_address = (in6addr_any,
542
                                       self.server_address[1])
543
            elif self.server_address[1] is None:
544
                self.server_address = (self.server_address[0],
545
                                       0)
546
            return super(type(self), self).server_bind()
10 by Teddy Hogeborn
* server.py: Bug fix: Do "from __future__ import division".
547
3 by Björn Påhlsson
Python based server
548
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
549
def string_to_delta(interval):
550
    """Parse a string and return a datetime.timedelta
551
552
    >>> string_to_delta('7d')
553
    datetime.timedelta(7)
554
    >>> string_to_delta('60s')
555
    datetime.timedelta(0, 60)
556
    >>> string_to_delta('60m')
557
    datetime.timedelta(0, 3600)
558
    >>> string_to_delta('24h')
559
    datetime.timedelta(1)
560
    >>> string_to_delta(u'1w')
561
    datetime.timedelta(7)
562
    """
563
    try:
564
        suffix=unicode(interval[-1])
565
        value=int(interval[:-1])
566
        if suffix == u"d":
567
            delta = datetime.timedelta(value)
568
        elif suffix == u"s":
569
            delta = datetime.timedelta(0, value)
570
        elif suffix == u"m":
571
            delta = datetime.timedelta(0, 0, 0, 0, value)
572
        elif suffix == u"h":
573
            delta = datetime.timedelta(0, 0, 0, 0, 0, value)
574
        elif suffix == u"w":
575
            delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
576
        else:
577
            raise ValueError
578
    except (ValueError, IndexError):
579
        raise ValueError
580
    return delta
581
8 by Teddy Hogeborn
* Makefile (client_debug): Bug fix; add quotes and / to CERT_ROOT.
582
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
583
def server_state_changed(state):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
584
    """Derived from the Avahi example code"""
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
585
    if state == avahi.SERVER_COLLISION:
44 by Teddy Hogeborn
* ca.pem: Removed.
586
        logger.error(u"Server name collision")
28 by Teddy Hogeborn
* server.conf: New file.
587
        service.remove()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
588
    elif state == avahi.SERVER_RUNNING:
28 by Teddy Hogeborn
* server.conf: New file.
589
        service.add()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
590
591
592
def entry_group_state_changed(state, error):
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
593
    """Derived from the Avahi example code"""
13 by Björn Påhlsson
Added following support:
594
    logger.debug(u"state change: %i", state)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
595
    
596
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
13 by Björn Påhlsson
Added following support:
597
        logger.debug(u"Service established.")
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
598
    elif state == avahi.ENTRY_GROUP_COLLISION:
28 by Teddy Hogeborn
* server.conf: New file.
599
        logger.warning(u"Service name collision.")
600
        service.rename()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
601
    elif state == avahi.ENTRY_GROUP_FAILURE:
28 by Teddy Hogeborn
* server.conf: New file.
602
        logger.critical(u"Error in group state changed %s",
603
                        unicode(error))
604
        raise AvahiGroupError("State changed: %s", str(error))
605
24.1.13 by Björn Påhlsson
mandosclient
606
def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
607
    """Call the C function if_nametoindex(), or equivalent"""
24.1.13 by Björn Påhlsson
mandosclient
608
    global if_nametoindex
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
24.1.13 by Björn Påhlsson
mandosclient
612
        if_nametoindex = ctypes.cdll.LoadLibrary\
613
            (ctypes.util.find_library("c")).if_nametoindex
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
614
    except (OSError, AttributeError):
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
615
        if "struct" not in sys.modules:
616
            import struct
617
        if "fcntl" not in sys.modules:
618
            import fcntl
24.1.13 by Björn Påhlsson
mandosclient
619
        def if_nametoindex(interface):
28 by Teddy Hogeborn
* server.conf: New file.
620
            "Get an interface index the hard way, i.e. using fcntl()"
621
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
622
            s = socket.socket()
623
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
624
                                struct.pack("16s16x", interface))
625
            s.close()
626
            interface_index = struct.unpack("I", ifreq[16:20])[0]
627
            return interface_index
24.1.13 by Björn Påhlsson
mandosclient
628
    return if_nametoindex(interface)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
629
630
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
631
def daemon(nochdir, noclose):
632
    """See daemon(3).  Standard BSD Unix function.
633
    This should really exist as os.daemon, but it doesn't (yet)."""
634
    if os.fork():
635
        sys.exit()
636
    os.setsid()
637
    if not nochdir:
638
        os.chdir("/")
639
    if not noclose:
640
        # Close all standard open file descriptors
28 by Teddy Hogeborn
* server.conf: New file.
641
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
642
        if not stat.S_ISCHR(os.fstat(null).st_mode):
643
            raise OSError(errno.ENODEV,
644
                          "/dev/null not a character device")
645
        os.dup2(null, sys.stdin.fileno())
646
        os.dup2(null, sys.stdout.fileno())
647
        os.dup2(null, sys.stderr.fileno())
648
        if null > 2:
649
            os.close(null)
650
651
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
652
def main():
653
    global main_loop_started
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
654
    main_loop_started = False
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
655
    
3 by Björn Påhlsson
Python based server
656
    parser = OptionParser()
657
    parser.add_option("-i", "--interface", type="string",
28 by Teddy Hogeborn
* server.conf: New file.
658
                      metavar="IF", help="Bind to interface IF")
659
    parser.add_option("-a", "--address", type="string",
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
660
                      help="Address to listen for requests on")
28 by Teddy Hogeborn
* server.conf: New file.
661
    parser.add_option("-p", "--port", type="int",
3 by Björn Påhlsson
Python based server
662
                      help="Port number to receive requests on")
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
663
    parser.add_option("--check", action="store_true", default=False,
664
                      help="Run self-test")
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
665
    parser.add_option("--debug", action="store_true", default=False,
28 by Teddy Hogeborn
* server.conf: New file.
666
                      help="Debug mode; run in foreground and log to"
667
                      " terminal")
668
    parser.add_option("--priority", type="string", help="GnuTLS"
669
                      " priority string (see GnuTLS documentation)")
670
    parser.add_option("--servicename", type="string", metavar="NAME",
671
                      help="Zeroconf service name")
672
    parser.add_option("--configdir", type="string",
673
                      default="/etc/mandos", metavar="DIR",
674
                      help="Directory to search for configuration"
675
                      " files")
3 by Björn Påhlsson
Python based server
676
    (options, args) = parser.parse_args()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
677
    
4 by Teddy Hogeborn
* server.py (Client.created, Client.next_check): New.
678
    if options.check:
679
        import doctest
680
        doctest.testmod()
681
        sys.exit()
3 by Björn Påhlsson
Python based server
682
    
28 by Teddy Hogeborn
* server.conf: New file.
683
    # Default values for config file for server-global settings
684
    server_defaults = { "interface": "",
685
                        "address": "",
686
                        "port": "",
687
                        "debug": "False",
688
                        "priority":
689
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
690
                        "servicename": "Mandos",
691
                        }
692
    
693
    # Parse config file for server-global settings
694
    server_config = ConfigParser.SafeConfigParser(server_defaults)
695
    del server_defaults
696
    server_config.read(os.path.join(options.configdir, "server.conf"))
697
    server_section = "server"
698
    # Convert the SafeConfigParser object to a dict
699
    server_settings = dict(server_config.items(server_section))
700
    # Use getboolean on the boolean config option
701
    server_settings["debug"] = server_config.getboolean\
702
                               (server_section, "debug")
703
    del server_config
704
    
705
    # Override the settings from the config file with command line
706
    # options, if set.
707
    for option in ("interface", "address", "port", "debug",
708
                   "priority", "servicename", "configdir"):
709
        value = getattr(options, option)
710
        if value is not None:
711
            server_settings[option] = value
712
    del options
713
    # Now we have our good server settings in "server_settings"
714
    
715
    # Parse config file with clients
716
    client_defaults = { "timeout": "1h",
717
                        "interval": "5m",
718
                        "checker": "fping -q -- %%(fqdn)s",
719
                        }
720
    client_config = ConfigParser.SafeConfigParser(client_defaults)
721
    client_config.read(os.path.join(server_settings["configdir"],
722
                                    "clients.conf"))
723
    
724
    global service
725
    service = AvahiService(name = server_settings["servicename"],
726
                           type = "_mandos._tcp", );
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
727
    if server_settings["interface"]:
728
        service.interface = if_nametoindex(server_settings["interface"])
25 by Teddy Hogeborn
* mandos-clients.conf ([DEFAULT]): New section.
729
    
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
730
    global main_loop
731
    global bus
732
    global server
733
    # From the Avahi example code
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
734
    DBusGMainLoop(set_as_default=True )
735
    main_loop = gobject.MainLoop()
736
    bus = dbus.SystemBus()
737
    server = dbus.Interface(
738
            bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
739
            avahi.DBUS_INTERFACE_SERVER )
740
    # End of Avahi example code
741
    
28 by Teddy Hogeborn
* server.conf: New file.
742
    debug = server_settings["debug"]
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
743
    
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
744
    if debug:
745
        console = logging.StreamHandler()
746
        # console.setLevel(logging.DEBUG)
747
        console.setFormatter(logging.Formatter\
748
                             ('%(levelname)s: %(message)s'))
749
        logger.addHandler(console)
750
        del console
751
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
752
    clients = Set()
753
    def remove_from_clients(client):
754
        clients.remove(client)
755
        if not clients:
44 by Teddy Hogeborn
* ca.pem: Removed.
756
            logger.critical(u"No clients left, exiting")
28 by Teddy Hogeborn
* server.conf: New file.
757
            sys.exit()
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
758
    
44 by Teddy Hogeborn
* ca.pem: Removed.
759
    clients.update(Set(Client(name = section,
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
760
                              stop_hook = remove_from_clients,
44 by Teddy Hogeborn
* ca.pem: Removed.
761
                              config
762
                              = dict(client_config.items(section)))
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
763
                       for section in client_config.sections()))
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
764
    
765
    if not debug:
766
        daemon(False, False)
767
    
768
    def cleanup():
769
        "Cleanup function; run on exit"
770
        global group
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
771
        # From the Avahi example code
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
772
        if not group is None:
773
            group.Free()
774
            group = None
775
        # End of Avahi example code
776
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
777
        while clients:
778
            client = clients.pop()
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
779
            client.stop_hook = None
780
            client.stop()
781
    
782
    atexit.register(cleanup)
783
    
784
    if not debug:
785
        signal.signal(signal.SIGINT, signal.SIG_IGN)
28 by Teddy Hogeborn
* server.conf: New file.
786
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
787
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
788
    
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
789
    for client in clients:
790
        client.start()
791
    
28 by Teddy Hogeborn
* server.conf: New file.
792
    tcp_server = IPv6_TCPServer((server_settings["address"],
793
                                 server_settings["port"]),
3 by Björn Påhlsson
Python based server
794
                                tcp_handler,
28 by Teddy Hogeborn
* server.conf: New file.
795
                                settings=server_settings,
12 by Teddy Hogeborn
* mandos-clients.conf ([foo]/dn, [foo]/password, [braxen_client]/dn,
796
                                clients=clients)
28 by Teddy Hogeborn
* server.conf: New file.
797
    # Find out what port we got
798
    service.port = tcp_server.socket.getsockname()[1]
44 by Teddy Hogeborn
* ca.pem: Removed.
799
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
800
                u" scope_id %d" % tcp_server.socket.getsockname())
28 by Teddy Hogeborn
* server.conf: New file.
801
    
29 by Teddy Hogeborn
* plugins.d/mandosclient.c (start_mandos_communication): Changed
802
    #service.interface = tcp_server.socket.getsockname()[3]
28 by Teddy Hogeborn
* server.conf: New file.
803
    
804
    try:
805
        # From the Avahi example code
806
        server.connect_to_signal("StateChanged", server_state_changed)
807
        try:
808
            server_state_changed(server.GetState())
809
        except dbus.exceptions.DBusException, error:
810
            logger.critical(u"DBusException: %s", error)
811
            sys.exit(1)
812
        # End of Avahi example code
813
        
814
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
815
                             lambda *args, **kwargs:
816
                             tcp_server.handle_request\
817
                             (*args[2:], **kwargs) or True)
818
        
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
819
        logger.debug("Starting main loop")
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
820
        main_loop_started = True
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
821
        main_loop.run()
28 by Teddy Hogeborn
* server.conf: New file.
822
    except AvahiError, error:
823
        logger.critical(u"AvahiError: %s" + unicode(error))
824
        sys.exit(1)
11 by Teddy Hogeborn
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
825
    except KeyboardInterrupt:
15 by Teddy Hogeborn
* mandos-clients.conf ([foo]): Uncommented.
826
        if debug:
827
            print
16 by Teddy Hogeborn
* Makefile: Include targets for all binaries.
828
829
if __name__ == '__main__':
830
    main()